h44628
s 00001/00001/01266
d D 1.2 83/03/17 20:12:24 bog 2 1
c Comment "not interruptable" comment.
e
s 01267/00000/00000
d D 1.1 83/03/17 14:37:36 tes 1 0
c date and time created 83/03/17 14:37:36 by tes
e
u
4
U
t
T
I 1
GSX_Code	CSeg	Byte	Public

		Public	GSX_Entry_0,GSX_Entry_1,GSX_Entry

		Public	Old_Vector_0,Old_Vector_1

		Public	GSX_Bdos,Default_Drive

		Public	BI_Open_Assign,BI_Parse_Line

		Extrn	FOpen		:Near
		Extrn	FSkipBl		:Near
		Extrn	FGetCh		:Near
		Extrn	FClose		:Far

		Extrn	NParse		:Near
		Extrn	FParse		:Near

		Extrn	PSize		:Far
		Extrn	PLoad		:Near

; GSX-86 Data Area Access

		Extrn	HowBound	:Byte
		Extrn	ProcPtr		:Dword
		Extrn	BasePageSegment	:Word
		Extrn	ModuleSize	:Word
		Extrn	ProcId		:Word
		Extrn	XPixels		:Word
		Extrn	YPixels		:Word

; Values for HowBound

UnBound		Equ	-1		;  Nothing bound to this layer
BoundIndirect	Equ	0		;  Proc Bound Indirect
BoundFcb	Equ	1		;  Proc Bound by Fcb
BoundDirect	Equ	2		;  Proc Bound Direct

		Public	UnBound,BoundIndirect,BoundFcb,BoundDirect

	Include	BStruct.A86

Bdos	Equ	224			; Int code for Bdos
Gdos	Equ	Bdos			; Int code for Gdos

	Extrn	MAXPROC		:Abs	; Maximum layer number
	Extrn	MAXPROC_Paragraphs	:Abs	; Maximum layer * 16

MAXFUNC	Equ	7			; Maximum valid Gdos function

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; GSX-86 Graphics loader and dispatcher

;	Int	Gdos		{224; Cl = 115}

; Ch[7..4]	"Layer" number	0..MAXPROC
; Ch[3..0]	Function Code	0..MAXFUNC

; Each layer may contain a driver or Kernel or Plot.  Only one procedure
; is ever present within a layer at one time, but the layers are independent
; so multiple drivers may be present in different layers.

; Each function is separately described below.

; Note that in general no registers (including the flags) are changed except
; those specifically stated which return values.

; If an illegal layer or an illegal function code is specified the following
; message appears and the calling program is terminated:
;   "GSX-86 ssss:oooo Illegal function: ffff"
; where ssss:oooo are the Cs base and Ip offset of the instruction following
; the Int Gdos and ffff are the Cx layer and function of the offending call.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function 0    Kill Layer

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	0

; Mark as UnBound any procedure currently bound in the layer specified by
; Ch[7..4].  Memory associated with the layer is not deallocated.  If no
; procedure is bound to the layer, nothing happens.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function 1    Bind Layer Indirect

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	1
; Dx		Procedure Id

;	Al      0       Procedure found and bound
;		255     New procedure not bound.  Ah holds the reason:
;			Ah  0   ASSIGN.SYS not found
;			    1   Syntax error in ASSIGN.SYS
;			    2   Procedure Id not found in ASSIGN.SYS
;			    3   Close error on ASSIGN.SYS
;			    4   File specified in ASSIGN.SYS not found
;			    5	Non-lethal unexpected Eof on procedure
;			    6	Syntax error (absolute bucket) in procedure
;			    7   Not enough room for procedure specified
;			    8   Load error on new procedure
;			Unless there was a load error, any previously bound
;			procedure in the layer is not disturbed if a new
;			procedure is not bound.  If Ah is 8, the layer has
;			been killed.
;	Flags   As if "Or Al,Al" had been done immediately prior to return.

; The file ASSIGN.SYS on the default drive contains the mapping from
; Procedure Id to filespec for each layer.

; Any currently bound procedure in the layer specified by Ch[7..4] is killed
; and the filespec mapped by the Id in Dx in ASSIGN.SYS is bound instead.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function 2    Bind Layer by Fcb

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	2
; Ds:Dx		Address of unopened Fcb of .SYS file of procedure to bind

;	Al      0       New procedure loaded and bound.
;		255     Ah  4	File specified in Fcb not found
;			    5	Non-lethal unexpected Eof on procedure
;			    6	Syntax error (absolute bucket) in procedure
;			    7   Not enough room for procedure specified
;			    8   Load error on new procedure
;			Unless there was a load error, any previously bound
;			procedure in the layer is not disturbed if a new
;			procedure is not bound.  If Ah is 8, the layer has
;			been killed.
;	Flags   As if "Or Al,Al" had been done immediately prior to return.

; Any currently bound procedure in the layer specified by Ch[7..4] is killed
; and the file specified by the unopened Fcb pointed to by Ds:Dx is bound.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function 3    Bind Layer Direct

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	3
; Ds:Dx		Address of procedure to bind

; Any currently bound procedure in the layer specified by Ch is killed
; and the procedure pointed to by Ds:Dx is bound.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function 4    Invoke Driver

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	4
; Ds:Dx   Dword address of parameter block:
;   @ Ds:Dx     Dword address of Contrl array
;   @ Ds:Dx+4   Dword address of IntIn array
;   @ Ds:Dx+8   Dword address of PtsIn array
;   @ Ds:Dx+12  Dword address of IntOut array
;   @ Ds:Dx+16  Dword address of PtsOut array

;     Only in the case of Open Workstation is an output specified:
;	Al      0       Procedure found and bound
;		255     New procedure not bound.  Ah holds the reason:
;			Ah  0   ASSIGN.SYS not found
;			    1   Syntax error in ASSIGN.SYS
;			    2   Procedure Id not found in ASSIGN.SYS
;			    3   Close error on ASSIGN.SYS
;			    4   File specified in ASSIGN.SYS not found
;			    5	Non-lethal unexpected Eof on procedure
;			    6	Syntax error (absolute bucket) in procedure
;			    7   Not enough room for procedure specified
;			When the above errors are returned to the Open
;			Workstation caller, the previous driver is not
;			disturbed and the Open Workstation was passed to it.
;	Flags   As if "Or Al,Al" had been done immediately prior to return.

;	If a load error occurs on a new driver such that the old driver cannot
;	be salvaged, the following message appears and the calling program is
;	terminated:
;	  "GSX-86 ssss:oooo GIOS Load error on Id xxxx"
;	where ssss:oooo are the Cs base and Ip offset of the instruction
;	following the Int Gdos and xxxx is the Device Id of the driver for
;	which a load was attempted.

;	If, through a combination of error conditions, an Invoke Driver call
;	which is not a valid Open Workstation is issued when no valid driver
;	is present, the following message appears and the calling program is
;	terminated:
;	  "GSX-86 ssss:oooo GIOS Invalid"
;	where ssss:oooo is as above.  Note that a valid Open Workstation call
;	(the normal circumstance) will load a valid driver and prevent this
;	message from ever appearing.

; 32K-space to device-space transformation is performed on any PtsIn and an
; inverse transform is performed (in place) on any points returned in PtsOut,
; as in GSX-80.

; The invoker is expected to have enough stack space for GSX-86 but the driver
; has its own stack.

; GSX-86 stack requirements:
;  Open Workstation     400 bytes (decimal)
;  All others           PtsIn size + 64 bytes (decimal)

; Note that the caller must have enough stack space to replicate PtsIn
; for the 32K to raster transform.

; The driver is invoked with a Callf from GSX, and should return with a Retf.
; It is expected to switch to its own stack for internal use, beyond an
; allowed overhead of a few pushes.  Suggested entry:

;Drivr: Mov     Bp,Sp                   ;Need Ss, Sp in registers
;	Mov     Bx,Ss
;	Mov     Ax,Segment DrivStk      ;Mov Ss,xx
;	Mov     Ss,Ax                   ;Mov Sp,yy  pair not
D 2
;	Mov     Sp,Offset DrivStk       ; interruptable on 8086/88
E 2
I 2
;	Mov     Sp,Offset DrivStk       ; interruptable on NEW 8086/88
E 2
; Now using driver's stack
;	Push    Bx                      ;Old Ss
;	Push    Bp                      ;Old Sp
;	...
; Driver functions.  Ds:Dx points to parameters.
;	...
;	Pop     Bp                      ;Old Sp
;	Pop     Bx                      ;Old Ss
;	Mov     Ss,Bx                   ;Restore old Ss, Sp with
;	Mov     Sp,Bp                   ; non-interruptable pair
;	Retf                            ;Return to GSX-86

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function 5    Pass Through

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	5

; The procedure in the layer specified in Ch[7..4] is invoked.  No registers
; are changed.  When the layer gains control it will appear as if it had
; been invoked with a Callf instead of an Int Gdos through GSX.

; Note that this function allows use of a driver directly in raster space
; rather than 32K space, with attendant device dependencies.

; If no procedure is bound to the specified layer, a message is shown and
; a Bdos 0 is performed, i.e. program killed.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; Function 6    Return Layer Pointer

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	6

;	Al	255	UnBound		No procedure bound
;		0	BoundIndirect	Procedure bound Indirect
;		1	BoundFcb	Procedure bound by Fcb
;		2	BoundDirect	Procedure bound Direct
;	Es:Bx	Address of Layer Table entry, as follows:
;	  HowBound	Rb	1	; As above; always valid
;	  Procedure_Ptr	Rd	1	; DWord Ptr to procedure in layer;
;					;  valid unless UnBound
;	  Base_Page	Rw	1	; Segment of Base Page for procedure;
;					;  valid unless UnBound or BoundDirect
;	  Module_Size	Rw	1	; Number of paragraphs in layer;
;					;  always valid
;	  Procedure_Id	Rw	1	; Device Id of procedure in layer;
;					;  valid only if BoundIndirect
;	Flags   As if "Or Al,Al" had been done immediately prior to return.

; Return the doubleword address of the Layer Table entry for the layer
; specified in Ch[7..4].

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function 7    Return Maximum Layer Number

; Ch[7..4]	Layer number	0..MAXPROC
; Ch[3..0]	Function Code	7

;	Ax      Maximum layer number allowed in this GSX.

; Return the maximum layer number allowed in this particular version of GSX.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Random Comments

; Format of ASSIGN.SYS
;   01  ddVRet  ; Procedure Id (Device Id) and associated file
;   2 filespec  ; Comments allowed after semicolon
;   298 KernE87.Cmd     ; Layer 1: GKS Kernel, 8087 emulation version
;    299 GSSPlot        ;	space shared with Plot
;	5 dd7470        ; Both hp7470 driver and Vt100 RetroGraphics can be
;			;  in at the same time, on different layers.

; Graphics.Cmd allocates space in layer 0 for the first driver specified in
; Assign.Sys.  Thus the largest driver shoud be mentioned first.  Graphics.Cmd
; allocates space for the communications driver on layer 1, too.  No other
; layers are automatically allocated memory.  Other layers should only be
; Bound Direct; if a Bind Indirect or by Fcb any layer other than 0 or 1, the
; caller must be responsible for fooling GSX into thinking it has enough
; memory for the procedure.

; Character I/O Interface (Layer 1)

; Ch[7..4]	Layer number	1
; Ch[3..0]	Function code	5 (GSX-86 Pass Through)
; Dx	Dx	Datum to Send or Receive if appropriate subfunction and status
; Bl		Channel number	0..MAXCHAN
; Bh		Subfunction:
;		  0:	Reset channel	(unimplemented)
;		  1:	Immediately return receive READY status (Dx unchanged)
;		  2:	Wait until receive channel READY, then return
;			  receive status and datum
;		  3:	Immediately return receive status; if receive channel
;			  READY, then return datum also else Dx unchanged
;		  4:	Reserved
;		  5:	Immediately return send READY status
;		  6:	Wait until send channel READY, then send datum
;			  and return send status
;		  7:	Immediately return send status; if send channel READY,
;			  then send datum also
;	Ax	Status of channel (bits):
;		 On both send and receive subfunctions:
;		  0000 0000 0000 0000	Channel not ready.  Dx unchanged.
;		  1xxx xxxx xxxx xxxx	Ready.  Datum sent or received if
;					 not status only subfunction.
;		  1X.. .... .... ....	1:  No connection to channel
;		 On receive subfunctions:
;		  1.X. .... .... ....	1:  EOF received (if supported)
;		  1... .... .... .X..	1:  Framing error on received datum
;		  1... .... .... ..X.	1:  Overrun error on received datum
;		  1... .... .... ...X	1:  Parity error on received datum
;		 On send subfunctions:
;		  1.X. .... .... ....	1:  On channels with send queuing in
;					     either software or hardware, send
;					     queue neither full nor empty;
;					     i.e. not all data enqueued has
;					     been sent
;		  			0:  Send queue (if any) empty
; Bh		Subfunction:
;		  8:	Read buffer	(currently only works on Con:)
;					Does Bdos 10 into Ds:Dx
;	Flags	As if "Or Ax,Ax" had been done just before returning.

; Send or receive the character (up to 16 bits) in Dx according to the
; subfunction in Bx.

; Note that no other registers than the ones specified above are changed.  In
; particular, the direction flag is not changed.

; By convention for 8-bit serial devices a BREAK is sent as 0fffeh (-2) in
; Dx.  A received BREAK can be either 0fffeh (-2) in Dx or 0 in Dx with the
; Framing Error bit set in Ax.

; Note that if no device is assigned to the channel the status always returns
; with READY and NO_ CONNECTION.  On Send, data sent goes away.  On receive,
; data received is always 0001ah (^Z) and the EOF bit is set.

	Eject

; These spots entered with Int Gdos, as if it were a Pushf, Callf GSX.
; Interrupts are disabled and they should not be for very long.

GSX_Entry_0:			; Int 224 entry
	Cmp	Cl,115		; Gdos function code?
	Je	GSX_Entry	; Jump if yes

;	Jmpf	Far Ptr Old_Vector_0
	Db	0eah		; Jmpf opcode

Old_Vector_0	Rd	1

GSX_Entry_1:			; Int 225 entry
	Cmp	Cl,115		; Gdos function code?
	Je	GSX_Entry	; Jump if yes

;	Jmpf	Far Ptr Old_Vector_1
	Db	0eah		; Jmpf opcode

Old_Vector_1	Rd	1

	Eject

GSX_Entry:
	Sub	Sp,4			; Room for Far Ptr to layer
	Push	Bp			; Save caller's Bp
	Mov	Bp,Sp			; Need access into stack

; Right now:	[Bp]		Caller's Bp
;		6[Bp]		Caller's Ip
;		8[Bp]		Caller's Cs
;		10[Bp]		Caller's Flags

	Push	Word Ptr 10[Bp]		; Push caller's flags
	Popf				; Restore interrupt state

; After futzing:
OldCs		Equ	Ss:(Word Ptr 10)[Bp]; Caller's Cs
OldIp		Equ	Ss:(Word Ptr 8)[Bp] ; Caller's Ip
ProcCs		Equ	Ss:(Word Ptr 6)[Bp] ; Layer Cs if PassThru or RetPtr
ProcIp		Equ	Ss:(Word Ptr 4)[Bp] ; Layer Ip if PassThru or RetPtr
OldPtsInSegment Equ	Ss:(Word Ptr 6)[Bp] ; Caller's original Dword Ptr to
OldPtsInOffset	Equ	Ss:(Word Ptr 4)[Bp] ;	PtsIn if InvokeDriver
OldFlags	Equ	Ss:(Word Ptr 2)[Bp] ; Caller's flags
OldBp		Equ	Ss:(Word Ptr 0)[Bp] ; Caller's Bp
OldAx		Equ	Ss:(Word Ptr (-2))[Bp]; Caller's Ax
OldBx		Equ	Ss:(Word Ptr (-4))[Bp]; Caller's Bx
OldCx		Equ	Ss:(Word Ptr (-6))[Bp]; Caller's Cx
OldDx		Equ	Ss:(Word Ptr (-8))[Bp]; Caller's Dx
OldSi		Equ	Ss:(Word Ptr (-10))[Bp]; Caller's Si
OldDi		Equ	Ss:(Word Ptr (-12))[Bp]; Caller's Di
OldDs		Equ	Ss:(Word Ptr (-14))[Bp]; Caller's Ds
OldEs		Equ	Ss:(Word Ptr (-16))[Bp];Caller's Es

	Push	Ax			; Preserve caller's registers
	Push	Bx
	Push	Cx
	Push	Dx
	Push	Si
	Push	Di
	Push	Ds
	Push	Es

	Eject

	Mov	Bx,Ss:(Word Ptr 10)[Bp]	; Get caller's flags
	Mov	OldFlags,Bx		;  to where they belong
	Mov	Bx,Ss:(Word Ptr 8)[Bp]	; Get caller's Cs
	Mov	OldCs,Bx		;  to where it belongs
	Mov	Bx,Ss:(Word Ptr 6)[Bp]	; Get caller's Ip
	Mov	OldIp,Bx		;  to where it belongs


	Mov	Bl,Ch			; Layer number *16 to Bl
	Mov	Bh,0			; High byte 0
	Mov	Si,Bx			; Layer & function to Si
	And	Bl,0f0h			; Mask to just layer number
	Cmp	Bx,MAXPROC_Paragraphs	; Illegal layer?
	Ja	IllLayer		; Jump if yes
	Push	Bx			; To Tos

LayerIndex	Equ	Ss:(Word Ptr (-18))[Bp]; Layer table index

	And	Si,00fh			; Mask to just function code
	Cmp	Si,MAXFUNC		; Illegal function?
	Ja	IllFunc			; Jump if yes
	Shl	Si,1			; Times 2 for function code table index
	Mov	Di,Dx			; Put param block offset into Di
	Jmp	Cs:Function[Si]		; Dispatch to specialists

; Ds:Di points to parameter block, if any
; Bx contains layer index

Function	Dw	Offset KillLayer	; Function 0
KillLayerOp	Equ	0
		Dw	Offset BindIndirect	;	   1
BindIndirectOp	Equ	1
		Dw	Offset BindFcb		;	   2
BindFcbOp	Equ	2
		Dw	Offset BindDirect	;	   3
BindDirectOp	Equ	3
		Dw	Offset InvokeDriver	;	   4
InvokeDriverOp	Equ	4
		Dw	Offset PassThrough	;	   5
PassThroughOp	Equ	5
		Dw	Offset ReturnPtr	;	   6
ReturnPtrOp	Equ	6
		Dw	Offset ReturnMaxProc	;	   7
ReturnMaxProcOp Equ	7

	Eject

IllLayer:
IllFunc:
	Jmp	ShowIllegalFunction	; Show Cs:Ip, string, Cx and die

Default_Drive	Rb	0

AssignName	Db	0,'ASSIGN  SYS'

; Di and Dx contain the Procedure Id
; Bx contains the layer index
BindIndirect:
	Call	PushDmaAddress		; Save Dma Segment & Offset
	Mov	Al,0			; Assume proc gets loaded
	Cmp	Cs:HowBound[Bx],BoundIndirect; Current resident Bound Indirect?
	Jne	BISearch		; If not Bound Indirect must search

	Cmp	Di,Cs:ProcId[Bx]	; Asking for same proc again?
	Je	BIQuit			; Jump if already there and alive

BISearch:
	Sub	Sp,72+BuffStructSize	; Make room for 2 Fcbs and buff struct

ProcFcb		Equ	Ss:(Byte Ptr (LayerIndex-36))[Bp]
AssignFcb	Equ	Ss:(Byte Ptr (ProcFcb-36))[Bp]
AssignBuff	Equ	Ss:(Byte Ptr (AssignFcb-BuffStructSize))[Bp]

	Push	Cs			; Make a Callf
	Call	BI_Open_Assign		; Open ASSIGN.SYS
	Jnc	BI_Parse_Loop		; If opened ok, go parse lines

	Mov	Ah,0			; ASSIGN.SYS not found

BIError:
	Mov	Al,0ffh			; Error in ASSIGN.SYS scan

BIQuit:
	Jmp	PopDmaAddress		; Restore caller's Dma & return w err

BI_Parse_Loop:
	Push	Cs			; Make a Callf
	Call	BI_Parse_Line		; Scan one line of ASSIGN.SYS
	Jnz	BIError			; Quit if error in scan; Ah is code

	Cmp	Dx,OldDx		; Match requested Device Id?
	Jne	BI_Parse_Loop		; Try next line

	Push	Ss			; FcbPtr
	Push	Bx
	Push	Cs			; Make a Callf
	Call	FClose			; Close ASSIGN.SYS

	Mov	Ah,3			; 3 is close error on ASSIGN.SYS
	Jc	BIError			; Jump if file not found in close

; Done with ASSIGN.SYS buffer and Fcb

	Mov	Sp,Di			; Maintain proc's Fcb

	Push	Ss			; Need ptr to Fcb
	Pop	Ds
	Mov	Dx,Di			; Ds:Dx points to Fcb
	Mov	Cx,OldCx		; Get caller's function code
	And	Ch,0f0h			; Preserve layer number
	Add	Ch,BindFcbOp		;  & change function to Bind by Fcb
	Int	Gdos			; Bind the program by Fcb
	Jnz	BIQuit			; Quit if error in load

	Mov	Bx,LayerIndex		; Point to layer table entry
	Mov	Cs:(Byte Ptr HowBound)[Bx],BoundIndirect; Flag how bound

	Mov	Dx,OldDx		; Get ProcId
	Mov	Cs:(Word Ptr ProcId)[Bx],Dx;  into layer table

	Jmps	BIQuit			;  & return with Al from BindFcbOp

	Eject

; Ds:Dx point to opened Fcb
; Bx has LayerIndex
BindFcb:
	Call	PushDmaAddress		; Save caller's Dma Segment & Offset
	Sub	Sp,BuffStructSize	; Make room for buffer structure

HeaderBuff	Equ	Ss:(Byte Ptr (LayerIndex-BuffStructSize))[Bp]

	Mov	HeaderBuff+BSUpZ,01ah	; ^Z for Eof

	Push	Ds			; PSize needs FcbPtr
	Push	Dx
	Push	Ss			;  & HeaderPtr
	Lea	Si,HeaderBuff
	Push	Si
	Push	Cs			; Make a Callf
	Call	PSize			; Open and find size of program
	Jnz	BFError			; Quit if error on sizing

; Bx holds total number of paragraphs required to load module

	Mov	Si,LayerIndex		; Get index into layer table

	Mov	Al,7			; Assume size error

	Cmp	Bx,Cs:ModuleSize[Si]	; Will it fit in space available?
	Ja	BFError			; Jump if module is too large for slot

	Mov	Cs:(Byte Ptr HowBound)[Si],UnBound ; Any further will trash old

	Push	Ds			; PLoad needs FcbPtr
	Push	Dx
	Push	Ss			;  & HeaderPtr
	Lea	Bx,HeaderBuff
	Push	Bx
	Push	Cs			;  & LayerPtr
	Push	Si
	Call	PLoad			; Load the module
	Jnz	BFError			; Jump if load error

	Mov	Cs:HowBound[Si],BoundFcb; Flag layer as bound by Fcb

BFQuit:
	Jmp	PopDmaAddress		; Return, restoring Dma	

BFError:
	Mov	Ah,Al			; Ah <- error type
	Mov	Al,0ffh			; Al <- 255
	Jmps	BFQuit			; Return error and flags

	Eject

BindDirect:
	Mov	Cs:(Word Ptr ProcPtr+2)[Bx],Ds	; Point to procedure
	Mov	Cs:(Word Ptr ProcPtr)[Bx],Dx	;  as Far pointer

	Mov	Cs:(Byte Ptr HowBound)[Bx],BoundDirect	; Flag as Bound Direct

	Jmp	GSXQuit			; Return to Gdos caller

	Eject

InvokeDriver:

; Ds:Di points to parameter block.  Bx holds Layer Index.

	Les	Si,Ds:(Dword Ptr 8)[Di]	; Es:Si <- Ptr to PtsIn
	Mov	OldPtsInSegment,Es	; Save for after driver invocation
	Mov	OldPtsInOffset,Si

	Call	TransformToRaster	; Transform any PtsIn to raster space

	Les	Si,Ds:(Dword Ptr 0)[Di]	; Get pointer to Contrl array

	Cmp	Es:(Word Ptr 0)[Si],1	; Open WorkStation?
	Jne	IDNotOpenWS		; Jump if not Open WorkStation

; Open Workstation driver invocation

; Ds:Di point to parameter block
; Es:Si point to Contrl array

	Les	Si,Ds:(Dword Ptr 4)[Di]	; Get pointer to IntIn
	Mov	Si,Es:(Word Ptr 0)[Si]	; Get IntIn(1), Device Id

	Cmp	Cs:HowBound[Bx],UnBound	; Layer holds proc?
	Je	IDLoad			; Jump if no proc in layer

	Cmp	Si,Cs:ProcId[Bx]	; Device already loaded?
	Je	IDLoaded		; Jump if device driver already in

; Load the driver -- Device Id in Si
IDLoad:
	Mov	Dx,Si			; Device Id to Dx
	And	Ch,0f0h			; Mask to just layer number
	Add	Ch,BindIndirectOp	; Bind Indirect opcode
	Int	Gdos			; Try to load in the new driver

	Mov	Byte Ptr OldAx,Al	; Flag to caller if ok
	Jz	IDLoaded		; Jump if ok

	Mov	OldAx,Ax		; Flag bad load if bad load

	Cmp	Al,8			; Load error?
	Jne	IDLoaded		; Jump if previous driver ok

	Jmp	ShowLoadError		; Die if previous driver trashed

IDLoaded:
	Cmp	Cs:HowBound[Bx],UnBound	; Driver present?
	Je	ShowInvalidGios_0	; Jump if driver not present

	Push	OldFlags		; Get caller's original flags
	Popf				; Restore them
	Or	Al,Al			; Set status on Al value
	Pushf				; Save flags
	Pop	OldFlags		;  back for caller

	Call	Driver			; Invoke the driver

	Les	Si,Ds:(Dword Ptr 12)[Di]; Es:Si <- ptr to IntOut

	Mov	Ax,Es:(Word Ptr 0)[Si]	; Get highest X pixel address
	Inc	Ax			; Now number of pixels in X
	Mov	Cs:XPixels[Bx],Ax	; Save number of pixels in X

	Mov	Ax,Es:(Word Ptr 2)[Si]	; Get highest Y pixel address
	Inc	Ax			; Now number of pixels in Y
	Mov	Cs:YPixels[Bx],Ax	; Save number of pixels in Y

	Jmps	IDTransformOutPts	;  & go transform any output points

IDNotOpenWS:
	Cmp	Cs:HowBound[Bx],UnBound	; Driver present?

ShowInvalidGios_0:
	Je	ShowInvalidGios		; Jump if driver not present

	Call	Driver			; Invoke the driver

IDTransformOutPts:
	Lea	Sp,LayerIndex		; Trash transformed PtsIn

	Les	Si,Dword Ptr OldPtsInOffset	; Restore caller's PtsIn ptr
	Mov	Ds:(Word Ptr 8)[Di],Si
	Mov	Ds:(Word Ptr 10)[Di],Es

	Call	TransformTo32K		; Transform any PtsOut to 32K space

GSXQuit:
	Lea	Sp,OldEs		; Trash LayerIndex

	Pop	Es			; Restore registers
	Pop	Ds
	Pop	Di
	Pop	Si
	Pop	Dx
	Pop	Cx
	Pop	Bx
	Pop	Ax

	Pop	OldPtsInSegment		; Orig Bp to just below return address
	Popf				; Restore driver's flags
	Pop	Bp			; Trash old PtsIn offset
	Pop	Bp			; Restore driver's Bp
	Retf				; Return to caller

PopDmaAddress:
	Push	Ax			; Save return code
	Mov	Dx,ProcCs		; Get caller's Dma Segment Base
	Mov	Cl,033h			; Bdos "Set Dma Segment Base" function
	Push	Cs			; Far call
	Call	GSX_Bdos		; Let Bdos set the Dma base

	Mov	Dx,ProcIp		; Get caller's Dma Offset
	Mov	Cl,01ah			; Bdos "Set Dma Offset" function
	Push	Cs			; Far call
	Call	GSX_Bdos		; Let Bdos set the Dma offset
	Pop	Ax			; Restore return code

GSXQuitStuffAx:
	Mov	OldAx,Ax		; Return Al and maybe Ah to caller

	Push	OldFlags		; Get caller's flags back
	Popf

	Or	Al,Al			; Set caller's flags

	Pushf
	Pop	OldFlags

	Jmps	GSXQuit			;  & return to caller

KillLayer:
	Mov	Cs:(Byte Ptr HowBound)[Bx],UnBound	; Make layer unbound
	Jmps	GSXQuit			; & return to caller

	Eject

ShowInvalidGios:
	Mov	Dx,Offset InvalidGiosString; Point to "GIOS invalid"
	Call	ShowCsIpString	;  & show "GSX-86 cs:ip GIOS invalid"
	Jmps	Die			;  then System Reset

; Show message and Bdos 0 if PassThrough and layer is empty

ShowIllegalFunction:
	Mov	Dx,Offset IllegalFunctionString; Point to "Illegal function:  "
	Call	ShowCsIpString	;  & show "GSX-86 cs:ip Illegal function:  "
	Mov	Dx,OldCx		; Get caller's function & layer
	Call	PoopDx			;  & show it

Die:	Mov	Cl,0			; Bdos "System Reset" opcode
	Sub	Dx,Dx			; Do not preserve memory
	Int	Bdos			; Return to CCP

ShowLoadError:
	Mov	Dx,Offset LoadErrorString; Point to "GIOS load error on Id  "
	Call	ShowCsIpString ;  & show "GSX-86 cs:ip GIOS load error on Id "
	Mov	Dx,Si			; Id in Si to Dx
	Call	PoopDx			; Show Device Id
	Mov	Dx,Offset HexString	; Point to "h (hex)"
	Call	ShowCsIpString		;  & show it
	Jmps	Die			;  then System Reset

	Eject

PassThrough:
	Cmp	Cs:(Byte Ptr HowBound)[Bx],UnBound	; Layer empty?
	Je	ShowIllegalFunction	; Die if layer is empty

	Mov	Si,Cs:(Word Ptr ProcPtr+2)[Bx]	; Get layer Cs
	Mov	ProcCs,Si			;  to place for Retf
	Mov	Si,Cs:(Word Ptr ProcPtr)[Bx]	; Get layer Ip
	Mov	ProcIp,Si			;  to place for Retf

	Pop	Bx			; Trash LayerIndex

	Pop	Es			; Restore caller's registers
	Pop	Ds
	Pop	Di
	Pop	Si
	Pop	Dx
	Pop	Cx
	Pop	Bx
	Pop	Ax
	Pop	Bp
	Popf				; Restore caller's flags

; Invoke the layer's procedure with a Retf so that it will appear as if
; the caller had done a Callf of the layer.
	Retf				; Invoke proc via Retf

	Eject

ReturnPtr:
	Mov	Al,Cs:(Byte Ptr HowBound)[Bx]	; Return how proc was bound

	Push	Cs			; Base of Layer Table
	Pop	OldEs			;  to caller's Es

	Lea	Bx,(Byte Ptr HowBound)[Bx]; Bx <- offset of entry
	Mov	OldBx,Bx		;  to caller's Bx

GSXQuitStuffAx_0:
	Jmp	GSXQuitStuffAx		; Quit, returning proc's Cs:Ip in Es:Bx

ReturnMaxProc:
	Mov	Ax,MAXPROC		; Return maximum layer number

GSXQuit1:
	Jmp	GSXQuit			;  & return to caller

	Eject

BI_Open_Assign:
	Mov	Si,Offset AssignName	; Point to ASSIGN.SYS drive, name, etc.
	Mov	Cx,6			; Move 12 bytes of drive, name, ext
	Lea	Di,AssignFcb		; Point to ASSIGN.SYS Fcb
	Push	Ss			; Dest base
	Pop	Es			;  is Stack
	Cld				; Work up in address

; Cannot use Rep Movs because there is a Cs override and an interrupt won't Rep
BIMoveAssignName:
	Movs	Es:(Word Ptr 0)[Di],Cs:(Word Ptr 0)[Si]	; Move a word
	Loop	BIMoveAssignName	; Loop until 12 bytes moved

	Mov	AssignBuff+BSUpZ,01ah	; ^Z for Eof

	Lea	Bx,AssignFcb		; Point to ASSIGN.SYS Fcb with Bx
	Push	Ss
	Push	Bx
	Lea	Si,AssignBuff		;  & buffer structure with Si
	Push	Ss
	Push	Si
	Call	FOpen			; Open ASSIGN.SYS

	Lea	Di,ProcFcb		; Point Di to Proc Fcb

; Ss:Bx is ASSIGN.SYS Fcb
; Ss:Si is ASSIGN.SYS buffer structure
; Ss:Di is potential driver Fcb

	Mov	Al,' '			; Force FSkipBl to FGetCh first time

	Retf				;  & return to caller

	Eject

BI_Parse_Line:
	Push	Ax			; Save stopper of last line

	Mov	Al,Default_Drive	; Get default drive 0..16
	Mov	ProcFcb,Al		;  into potential Fcb

	Mov	Al,'S'			; Get 1st char default extension
	Mov	ProcFcb+9,Al		;  into extension
	Mov	Ax,05359h		; Get 2nd & 3rd chars def ext
	Mov	Word Ptr ProcFcb+10,Ax	;  into extension

	Pop	Ax			; Restore stopper from prev line

	Mov	Ah,2			; Assume ProcIdNotFound error

BI_Look_For_Number:
	Push	Ss			; FcbPtr
	Push	Bx
	Push	Ss			;  & BuffPtr
	Push	Si
	Call	NParse			; Dx <- valid number
	Jnc	BI_Look_for_File	; Look for filename if ok

	Cmp	Al,01ah			; EOL?
	Je	BIProcIdNotFound	; Quit with Driver Not Found error

	Cmp	Al,';'			; Comment line?
	Jne	BISyntaxError		; Syntax error if not

	Call	BI_Skip_Past_EOL	; Skip past end of this line
	Jmps	BI_Look_for_Number	;  & try for number again

BI_Look_for_File:
	Push	Ss			; FcbPtr
	Push	Bx
	Push	Ss			;  & BuffPtr
	Push	Si
	Push	Ss			;  & ProcFcbPtr
	Push	Di
	Call	FParse			; Parse filespec to ProcFcb
	Jc	BISyntaxError		; Jump if error in filespec

	Call	BI_Skip_Past_EOL	; Skip past EOL for next line

	Dec	Ah			; 0 here is no error

BISyntaxError:
	Dec	Ah			; 1 is syntax error in ASSIGN.SYS

BIProcIdNotFound:
	Or	Ah,Ah			; Set Z flag on Ah for errors
	Retf				;  & return

BI_Skip_Past_EOL:

; While ((Al != EOF) & (Al != Cr) & (Al != Lf)) {
;   Al <- FGetCh
;  }
; While ((Al == Cr) | (Al == Lf)) {
;   Al <- FGetCh
;  }

	Cmp	Al,01ah			; EOF?
	Je	BISPE_Ret		; Jump if end

	Cmp	Al,00ah			; Lf?
	Je	BI_Skip_EOL		; Skip EOL if so
	Cmp	Al,00dh			; Cr?
	Je	BI_Skip_EOL		; Skip EOL if so

	Push	Ss			; FcbPtr
	Push	Bx
	Push	Ss			;  & BuffPtr
	Push	Si
	Call	FGetCh			; Skip a character

	Jmps	BI_Skip_Past_EOL	;  & go try again for EOL

BI_Skip_EOL:
	Push	Ss			; FcbPtr
	Push	Bx
	Push	Ss			;  & BuffPtr
	Push	Si
	Call	FGetCh			; Skip an EOL character

	Cmp	Al,13			; Skip again if Cr
	Je	BI_Skip_EOL
	Cmp	Al,10			; Skip again if Lf
	Je	BI_Skip_EOL

BISPE_Ret:
	Ret				; Return; Al has first char past EOL

	Eject

ShowCsIpString:
	Push	Cs			; Ds must point to code base
	Pop	Ds			;  for Bdos
	Call	PoopCsIp		; Show OldCs:OldIp
	Mov	Cl,9			;  "Print String" call
	Push	Cs			; Far call
	Call	GSX_Bdos		; Print the string
	Ret				;  & return

IllegalFunctionString	Db	'Illegal function:  $'
LoadErrorString		Db	'GIOS load error on Id $'
HexString		Db	'h (hex)$'
InvalidGiosString	Db	'GIOS invalid$'
GSX_String		Db	'GSX-86 $'

	Eject

; Show Gdos caller's Cs:Ip
PoopCsIp:
	Push	Ax			; Save caller's registers
	Push	Bx
	Push	Cx
	Push	Dx
	Push	Ds
	Push	Es

	Mov	Dx,Offset GSX_String	; Point to "GSX-86 "
	Mov	Cl,9			; Bdos "Print String" function
	Push	Cs			; Far call
	Call	GSX_Bdos		; Show "GSX-86 "

	Mov	Dx,OldCs		; Get Gdos caller's Cs
	Call	PoopDx			; Show it

	Mov	Dl,':'			; Show ':' between Cs & Ip
	Call	ShowChar		;  like "xxxx:yyyy"

	Mov	Dx,OldIp		; Get Gdos caller's Ip
	Call	PoopDx			;  & show it

	Mov	Dl,' '			; Show a space
	Call	ShowChar		;  after Ip

	Pop	Es			; Restore caller's registers
	Pop	Ds
	Pop	Dx
	Pop	Cx
	Pop	Bx
	Pop	Ax

	Ret				;  & return

ShowChar:
	Mov	Cl,2			; Bdos "Console Output" function
	Push	Cs			; Far call
	Call	GSX_Bdos		; Show the character
	Ret				;  & return

	Eject

PoopDx:
	Call	Poop2Hex		; Show high-order digits

Poop2Hex:
	Call	PoopHex			; Show high-order digit

PoopHex:
	Mov	Cl,4			; Rotate left by 4
	Rol	Dx,Cl			;  so digit desired is on right
	Push	Dx			; Save unaffected rotated Dx

	And	Dl,00fh			; Mask to single digit
	Cmp	Dl,9			; a..f?
	Jbe	NotAF			; Jump if not a..f
	Add	Dl,7			; Bump a..f to 11..15

NotAF:	Add	Dl,'0'			; 0..9,a..f -> '0'..'9','A'..'F'

	Call	ShowChar		; Show the hex digit

	Pop	Dx			; Restore rotated value to show

	Ret				; Return with rotated value

	Eject

; Save caller's Dma Segment Base and Dma Offset in ProcCs and ProcIp

PushDmaAddress:
	Push	Ax			; Save caller's registers
	Push	Bx
	Push	Cx
	Push	Es

	Mov	Cl,034h			; Bdos "Get Dma Base & Offset" function
	Push	Cs			; Far call
	Call	GSX_Bdos		; Get Dma Base and Offset in Es:Bx

	Mov	ProcCs,Es		; Save caller's Dma base
	Mov	ProcIp,Bx		;  & offset

	Pop	Es			; Restore registers
	Pop	Cx
	Pop	Bx
	Pop	Ax
	Ret				;  & return

; Input Cx, Ds, Dx (preserved), output Es, Bx, Ax from Bdos function

GSX_Bdos:
	Push	Cx			; Preserve registers from Bdos
	Push	Dx
	Push	Bp
	Push	Si
	Push	Di
	Push	Ds

	Int	Bdos

	Pop	Ds
	Pop	Di
	Pop	Si
	Pop	Bp
	Pop	Dx
	Pop	Cx
	Retf


	Eject

; Register context should be such that both the driver and the person who
; emitted the Int Gdos think GSX is not there.  In other words, the driver
; should see the Gdos caller's register context and the Gdos caller should,
; upon return, see the driver's context.

Driver:	Push	Bx			; Preserve caller's Ds:Di & Bx
	Push	Di
	Push	Ds

	Push	Bp			; Preserve Bp just prior to driver call

	Push	Cs			; Fake a Callf
	Call	CallDriver		; Invoke the driver

	Push	Bp			; Driver's Bp at [Bp]
	Mov	Bp,Sp			; Point into stack
	Pushf				; Driver's flags below driver's Bp

	Mov	Bp,Ss:(Word Ptr 2)[Bp]	; Get GSX's Bp

	Mov	OldAx,Ax		; Driver's Ax for Gdos caller

	Pop	OldFlags		; Get driver's flags for Gdos caller

	Pop	OldBp			; Get driver's Bp for Gdos caller

	Mov	OldBx,Bx		; Arrange such that caller sees
	Mov	OldCx,Cx		;  driver's registers
	Mov	OldDx,Dx
	Mov	OldSi,Si
	Mov	OldDi,Di
	Mov	OldDs,Ds
	Mov	OldEs,Es

	Pop	Bp			; Get GSX's Bp again

	Pop	Ds			; Get caller's registers
	Pop	Di
	Pop	Bx
	Ret				; Return to InvokeDriver level

	Eject

CallDriver:
	Push	Cs:(Word Ptr ProcPtr+2)[Bx]; Segment of driver
	Push	Cs:(Word Ptr ProcPtr)[Bx]; Offset of driver
	Mov	Ax,OldAx		; Set up registers for driver
	Mov	Bx,OldBx
	Mov	Cx,OldCx
	Mov	Dx,OldDx
	Mov	Si,OldSi
	Mov	Di,OldDi
;	Mov	Ds,OldDs
	Mov	Es,OldEs
	Mov	Bp,OldBp
	Retf				; Call driver via Retf

	Eject

; Ds:Di points to parameter block
; OldPtsInSegment:OldPtsInOffset points to PtsIn array
TransformToRaster:
	Les	Si,Ds:(Dword Ptr 0)[Di]	; Point Es:Si to Contrl array

	Mov	Si,Es:(Word Ptr 2)[Si]	; Contrl(2) vertices in PtsIn
	Shl	Si,1			; Times 4 for x,y pair each
	Jz	TtRQuit			; Quit if no vertices

	Mov	Cx,Si			; Twice # of vertices is # of points

	Shl	Si,1			; 2 bytes each point is 4 each vertex
	Sub	Sp,Si			; Enough room in stack for all vertices

	Mov	Ds:(Word Ptr 8)[Di],Sp	; Replace ptr to PtsIn with
	Mov	Ds:(Word Ptr 10)[Di],Ss	;  ptr to transformed PtsIn

	Push	Word Ptr LayerIndex-2	; Push return address

	Push	Bx			; Save caller's registers
	Push	Di
	Push	Ds

	Push	Bp			; Bp used as temp in loop

	Mov	Di,Ds:(Word Ptr 8)[Di]	; Offset from Ss to transformed PtsIn

	Push	Ss			; Transformed PtsIn is in stack
	Pop	Es

	Lds	Si,Dword Ptr OldPtsInOffset	; Point to caller's PtsIn

	Mov	Bp,Cs:(Word Ptr YPixels)[Bx]; Bp holds YPixels at loop begin
	Mov	Bx,Cs:(Word Ptr XPixels)[Bx]; Bx holds XPixels at loop begin

	Cld				; Work up in address

	Eject

; In transform loop:
;  Bp and Bx swap number of pixels in X and Y, 0..65534 maximum
;  Ds:Si points to caller's PtsIn
;  Es:Di points into stack at transformed PtsIn
;  Cx contains point count; Ax and Dx used in multiply

TtRLoop:
	Lods	Ds:(Word Ptr 0)[Si]	; Get an X or Y
	Shl	Ax,1			; Transform 32K -> 64K space
	Mul	Bx			; Transform 64K -> raster space
	Mov	Ax,Dx			; High-order result is raster space
	Stos	Es:(Word Ptr 0)[Di]	; Stuff in transformed PtsIn

	Xchg	Bx,Bp			; Swap number of pixels in Y or X to Bx

	Loop	TtRLoop			; Loop until all points transformed

	Pop	Bp			; Restore caller's registers
	Pop	Ds
	Pop	Di
	Pop	Bx

TtRQuit:
	Ret				; Return to caller

	Eject

; Ds:Di point to parameter block

TransformTo32K:
	Les	Si,Ds:(Dword Ptr 0)[Di]	; Get pointer to Contrl array
	Mov	Cx,Es:(Word Ptr 4)[Si]	; Get output vertex count
	Jcxz	Tt32KQuit		; Quit if nothing to transform

	Push	Bx			; Save caller's registers
	Push	Di
	Push	Ds

	Shl	Cx,1			; Twice # vertices is # points

	Lds	Si,Ds:(Dword Ptr 16)[Di]; Ds:Si <- ptr to PtsOut array

	Mov	Di,Cs:(Word Ptr XPixels)[Bx]; Di <- number of pixels in X
	Mov	Bx,Cs:(Word Ptr YPixels)[Bx]; Bx <- number of pixels in Y

	Cld				; Work up in address

; In transform loop:
;  Ds:Si points to PtsOut array
;  Cx holds point count
;  Di and Bx swap number of pixels in X and Y

Tt32KLoop:
	Lods	Ds:(Word Ptr 0)[Si]	; Get a raster-space X or Y
	Mov	Dx,Ax			; Dividend into Dx
	Lea	Ax,-1[Di]		;  with almost divisor on right
	Add	Ax,Di			; Must be almost twice divisor
	Adc	Dx,0			;  on right
	Cmp	Dx,Di			; Would divide overflow?
	Jae	Tt32KOflow		; Jump if yes -- pin to max
	Div	Di			; Transform raster point to 64K space
	Shr	Ax,1			;  to 32K space

Tt32KStuff:
	Mov	Ds:(Word Ptr (-2))[Si],Ax; Back into PtsOut

	Xchg	Bx,Di			; Di <- number of pixels in Y or X

	Loop	Tt32KLoop		; Loop until all vertices transformed

	Pop	Ds			; Restore caller's registers
	Pop	Di
	Pop	Bx

Tt32KQuit:
	Ret				;  & return

Tt32KOFlow:
	Mov	Ax,32767		; Pin returned 32k point at max
	Js	Tt32KStuff		; Return maximum value

	End
E 1
