COMMENT *

			     CLUBware  (tm)

	 ASMBASIC interfaces assembly language subroutines to the BASIC
		 interpreter.

		   Copyright 1984 Rayhawk Automation N.W. Inc
				  P.O. Box 1427
				  Beaverton, Oregon   97075


	 Method:
	      1) Our Basic code calls the service routines directly
		   CALL QPRINT ( FLAG% , STRING )
	      2) The flag indicates whether we are interpreted or compiled
	      3) Compiled code has the object code for QPRINT linked into it
	      4) Interpreted code has a separate module resident below
		   the BASIC interpreter (this module contains QPRINT)
	      5) Both the interpreted Basic code and the compiled Basic code
		   use an initialization routine to determine whether
		   currently running compiled or interpreted and sets global
		   variable, FLAG, accordingly
	      6) If compiled, basic initialization routine does nothing
	      7) If interpreted, initialization routine performs following
		   steps
		   A) Insert two instruction assembly language subroutine
			into variable SUBINIT
			Two instructions are   INT 67h
					       RET 2
		   B) Make the following assignments
			SEGVALUE% = 0
			QPRINT%   = 1
			ZPRINT%   = 2
			CLREOL%   = 3
			SHELSORT% = 4
			etc
			(Everyone concerned with this project will agree on an
			  ordering and use it consistently.)
		   C) Make repeated calls to SUBINIT
			CALL SUBINIT ( SEGVALUE% )
			CALL SUBINIT ( QPRINT% )
			CALL SUBINIT ( ZPRINT% )
			CALL SUBINIT ( CLREOL% )
			CALL SUBINIT ( SHELSORT% )
		   D) Use the Basic DEFSEG to make our subroutines addressable
			DEFSEG = SEGVALUE%
		   E) Return from initialization
		   F) Implied but not stated is that after return from SUBINIT
			the variable QPRINT% no longer contains 1 but the
			offset within SEGVALUE% where the QPRINT% subroutine
			can be found. Same for other subroutines
									      *


CODE	  SEGMENT PARA PUBLIC 'CODE'
	  ASSUME  CS:CODE,DS:NOTHING,ES:NOTHING,SS:STACK

;_______________________________________________________________________________


;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	   INT 67h	interrupt routine	 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  EXTRN   QPRINT:FAR
	  EXTRN   SCRLDN:FAR
	  EXTRN   SCRLUP:FAR
	  EXTRN   XREP:FAR
	  EXTRN   YREP:FAR
	  EXTRN   CLREOL:FAR
	  EXTRN   CLREOS:FAR
	  EXTRN   ZPRINT:FAR
;	  EXTRN   STDIN:FAR
;	  EXTRN   STDOUT:FAR

TABLE_LEN	  LABEL  WORD
	  DW	  8		       ; eight subroutines so far

	  DW	  5252h 	       ; tag to allow basic to make sure
				       ;  ASMBASIC is resident
; entry point for assembly SUBINT routine

SUBINIT   PROC	  FAR
	  JMP	  SHORT SUB_CODE

SUBROUTINE_TABLE  LABEL  WORD	       ; table of subroutine offsets
	  DW	  OFFSET BAD_CALL      ; 0
	  DW	  OFFSET QPRINT        ; 1
	  DW	  OFFSET SCRLDN        ; 2
	  DW	  OFFSET SCRLUP        ; 3
	  DW	  OFFSET XREP	       ; 4
	  DW	  OFFSET YREP	       ; 5
	  DW	  OFFSET CLREOL        ; 6
	  DW	  OFFSET CLREOS        ; 7
	  DW	  OFFSET ZPRINT        ; 8
;	  DW	  OFFSET STDIN	       ; 9
;	  DW	  OFFSET STDOUT        ; 10

ARG	  EQU	  WORD PTR [BP+12]     ; argument to SUBINIT

SUB_CODE:

	  PUSH	  BP		       ; address the argument
	  MOV	  BP,SP

	  MOV	  SI,ARG	       ; load the address of the argument
	  MOV	  AX,WORD PTR [SI]     ; load the argument itself


;    argument of 0 means return code segment for use in DEFSEG statement

	  CMP	  AX,0		       ; return segment?
	  JA	  PREP_SUB

	  MOV	  AX,SEG CODE	       ; get our segment
	  MOV	  WORD PTR DS:[SI],AX  ; return it to caller
	  JMP	  FAR PTR SUBINIT_DONE

PREP_SUB:


;    non-zero argument means return offset of that subroutine

	  CMP	  AX,TABLE_LEN
	  JBE	  GOOD_ARGUMENT

	  CALL	  FAR PTR BAD_CALL     ; let user know a bad call
	  MOV	  AX,0		       ;  has been made

GOOD_ARGUMENT:

	  SHL	  AX,1		       ; each table entry is 2 bytes long
	  MOV	  BX,AX
	  MOV	  AX,SUBROUTINE_TABLE[BX]     ; load subroutine offset
	  MOV	  WORD PTR DS:[SI],AX	      ; return offset to caller



;    initialization complete, return to caller
;	  stack is cleared by code within basic segment

SUBINIT_DONE:
	  POP	  BP
	  IRET

SUBINIT   ENDP

;_______________________________________________________________________________

BAD_MESSAGE	  LABEL  BYTE
	  DB	  10,13,'An invalid subroutine call was made to subinit.',10,13
	  DB	  'There are only 8 subroutines defined within subinit.',10,13
	  DB	  'A request was made for a subroutine outside of this range.'
	  DB	  10,13,'$'

BAD_CALL  PROC	  FAR

	  PUSH	  DS

	  MOV	  AX,SEG CODE
	  MOV	  DS,AX

	  MOV	  DX,OFFSET BAD_MESSAGE
	  MOV	  AH,09
	  INT	  21h

	  POP	  DS
	  RET

BAD_CALL  ENDP

;_______________________________________________________________________________


ASMBASIC  PROC	  FAR

;					 Establish standard DOS linkage
	  PUSH	  DS		       ; Push addr of Program Segment Prefix
	  XOR	  AX,AX 	       ; Zero AX
	  PUSH	  AX		       ; Push zero onto stack
;					 (offset of INT 20 within PSF)


;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	   take over the INT 67h		 |
;		      | 	   interrupt if not already done	 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  DS,AX 		   ; address low memory
	  LDS	  BX,DWORD PTR DS:[019Ch]  ; load interrupt vector for int 67

	  MOV	  AX,WORD PTR SUBINIT
	  CMP	  AX,WORD PTR DS:[BX]
	  JNE	  NOT_HERE_YET
	  MOV	  AX,WORD PTR SUBINIT+2
	  CMP	  AX,WORD PTR DS:[BX+2]
	  JNE	  NOT_HERE_YET
	  MOV	  AX,WORD PTR SUBINIT+4
	  CMP	  AX,WORD PTR DS:[BX+4]
	  JNE	  NOT_HERE_YET
	  MOV	  AX,WORD PTR SUBINIT+6
	  CMP	  AX,WORD PTR DS:[BX+6]
	  JNE	  NOT_HERE_YET

	  JMP	  SHORT ALREADY_RESIDENT

NOT_HERE_YET:

	  MOV	  AX,SEG ASMBASIC      ; Move our code segment
	  MOV	  DS,AX 	       ; to the data segment register

;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	   take over the INT 67h		 |
;		      | 	   interrupt				 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  DX,OFFSET SUBINIT    ; Load offset of interrupt service mod
	  MOV	  AX,2567h	       ; Prepare for DOS service call type 25
;					 to establish service for INT 05
	  INT	  21h		       ; Ask DOS to establish service

;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	   modify INT 20 into INT 27 in the	 |
;		      | 	   program segment prefix		 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  BYTE PTR ES:[01],27h ; Change INT 20h to INT 27h

;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	6) load address of ending tag into DX	 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  AX,SEG BOTSTACK
	  SUB	  AX,SEG ASMBASIC
	  MOV	  CL,4		       ; prepare for 4 bit shift
	  SHL	  AX,CL 	       ; shift up (convert from seg to abs)
	  ADD	  AX,OFFSET BOTSTACK   ; add address of bottom location
	  ADD	  AX,0103h	       ; Pad offset because DOS measures
;					  offset relative to Program
;					  Segment Prefix
	  MOV	  DX,AX 	       ; leave where DOS will find it

;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	7) use RET FAR to return to DOS and	 |
;		      | 	   leave service routine resident	 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|


ALREADY_RESIDENT:

	  RET

ASMBASIC  ENDP

CODE	  ENDS

;_______________________________________________________________________________

STACK	  SEGMENT PARA STACK 'STACK'
BOTSTACK  LABEL   BYTE
	  DB	  24 DUP('STACK***')
TOPSTACK  DB	  0
STACK	  ENDS

	  END	  ASMBASIC
