	TITLE	SYSINT

	LOCALS	@@

; Version equates

    IFDEF _DPMI_
DPMIVersion	EQU	1
    ELSE
DPMIVersion	EQU	0
    ENDIF

; Double byte record

b0		EQU	(BYTE PTR 0)
b1		EQU	(BYTE PTR 1)

; Double word record

w0		EQU	(WORD PTR 0)
w2		EQU	(WORD PTR 2)

; Keyboard scan	codes

scBackKey	EQU	0EH
scSpaceKey	EQU	39H
scEnterKey	EQU	1CH
scRightKey	EQU	4DH
scLeftKey	EQU	4BH
scUpKey		EQU	48H
scDownKey	EQU	50H
scHomeKey	EQU	47H
scEndKey	EQU	4FH
scInsKey	EQU	52H
scDelKey	EQU	53H
scPgUpKey	EQU	49H
scPgDnKey	EQU	51H

; Keyboard shift flags

kbShiftKey	EQU	03H
kbCtrlKey	EQU	04H
kbAltKey	EQU	08H

; ROM BIOS workspace

KeyFlags	EQU	(BYTE PTR 17H)
KeyBufHead	EQU	(WORD PTR 1AH)
KeyBufTail	EQU	(WORD PTR 1CH)
KeyBufOrg	EQU	(WORD PTR 1EH)
KeyBufEnd	EQU	(WORD PTR 3EH)

; DOS function call classes

cNothing	EQU	0	;No check needed
cName		EQU	2	;Check name at DS:DX
cHandle		EQU	4	;Check handle in BX
cDrive		EQU	6	;Check drive in	DL

    IF DPMIVersion

; DPMI interrupt vector	number

DPMI		EQU	31H

; DPMI function	codes

dpmiAllocDesc	EQU	0000H		;Allocate descriptor
dpmiFreeDesc	EQU	0001H		;Free descriptor
dpmiSetSegBase	EQU	0007H		;Set segment base address
dpmiGetRealInt	EQU	0200H		;Get real mode interrupt vector
dpmiSetRealInt	EQU	0201H		;Set real mode interrupt vector
dpmiGetProtInt	EQU	0204H		;Get protected mode interrupt vector
dpmiSetProtInt	EQU	0205H		;Set protected mode interrupt vector
dpmiAllocRMCB	EQU	0303H		;Allocate real mode call-back
dpmiFreeRMCB	EQU	0304H		;Free real mode	call-back

; DPMI real mode call-back registers structure

realDI		EQU	(WORD PTR 00H)
realSI		EQU	(WORD PTR 04H)
realBP		EQU	(WORD PTR 08H)
realBX		EQU	(WORD PTR 10H)
realDX		EQU	(WORD PTR 14H)
realCX		EQU	(WORD PTR 18H)
realAX		EQU	(WORD PTR 1CH)
realFlags	EQU	(WORD PTR 20H)
realES		EQU	(WORD PTR 22H)
realDS		EQU	(WORD PTR 24H)
realFS		EQU	(WORD PTR 26H)
realGS		EQU	(WORD PTR 28H)
realIP		EQU	(WORD PTR 2AH)
realCS		EQU	(WORD PTR 2CH)
realSP		EQU	(WORD PTR 2EH)
realSS		EQU	(WORD PTR 30H)

    ENDIF

; Data segment

_DATA		SEGMENT	WORD PUBLIC 'DATA'

; Externals

	EXTRN	SysErrorFunc:DWORD
	EXTRN	CtrlBreakHit:BYTE
	EXTRN	SaveCtrlBreak:BYTE
	EXTRN	SysErrActive:BYTE
	EXTRN	SaveInt09:DWORD

    IF DPMIVersion

	EXTRN	SelectorInc:WORD
	EXTRN	Seg0040:WORD
	EXTRN	RealModeRegs:BYTE

; Temporary selector for Int24Handler

TempSelector	DW	?

    ENDIF

_DATA		ENDS

; Data group

DGROUP		GROUP	_DATA

; Code segment

SYSINT_TEXT	SEGMENT	BYTE PUBLIC 'CODE'

	ASSUME	CS:SYSINT_TEXT,DS:DGROUP

	PUBLIC	InitSysError
	PUBLIC	DoneSysError

; CS-based variables

OldInt09	DD	0	;Saved INT 09H vector
OldInt1B	DD	0	;Saved INT 1BH vector
OldInt21	DD	0	;Saved INT 21H vector
OldInt23	DD	0	;Saved INT 23H vector
OldInt24	DD	0	;Saved INT 24H vector

; Keyboard conversion table

KeyConvertTab	LABEL	BYTE

	DB	scSpaceKey,kbAltKey
	DW	0200H
	DB	scInsKey,kbCtrlKey
	DW	0400H
	DB	scInsKey,kbShiftKey
	DW	0500H
	DB	scDelKey,kbCtrlKey
	DW	0600H
	DB	scDelKey,kbShiftKey
	DW	0700H
	DB	scEnterKey,kbAltKey
	DW	1C0DH
	DB	scUpKey,kbCtrlKey
	DW	0800H
	DB	scDownKey,kbCtrlKey
	DW	0900H
	DB	scRightKey,kbAltKey
	DW	0A00H
	DB	scLeftKey,kbAltKey
	DW	0B00H
	DB	scUpKey,kbAltKey
	DW	0C00H
	DB	scDownKey,kbAltKey
	DW	0D00H
	DB	scInsKey,kbAltKey
	DW	0E00H
KeyConvertCnt	EQU	($-KeyConvertTab)/4

; DOS function call class table

FuncClassTab	LABEL	BYTE

	DB	cDrive		;36H - Get disk	free space
	DB	cNothing
	DB	cNothing
	DB	cName		;39H - Make directory
	DB	cName		;3AH - Remove directory
	DB	cName		;3BH - Change directory
	DB	cName		;3CH - Create file
	DB	cName		;3DH - Open file
	DB	cHandle		;3EH - Close file
	DB	cHandle		;3FH - Read file
	DB	cHandle		;40H - Write file
	DB	cName		;41H - Delete file
	DB	cHandle		;42H - Seek file
	DB	cName		;43H - Change file attributes
	DB	cNothing
	DB	cNothing
	DB	cNothing
	DB	cDrive		;47H - Get current directory
	DB	cNothing
	DB	cNothing
	DB	cNothing
	DB	cName		;4BH - Load or execute program
	DB	cNothing
	DB	cNothing
	DB	cName		;4EH - Find first
	DB	cNothing
	DB	cNothing
	DB	cNothing
	DB	cNothing
	DB	cNothing
	DB	cNothing
	DB	cNothing
	DB	cName		;56H - Rename file
	DB	cHandle		;57H - Get/Set file date and time

; Function check routines table

FuncCheckTab	LABEL	WORD

	DW	CheckNothing
	DW	CheckName
	DW	CheckHandle
	DW	CheckDrive

; Install system error handlers

InitSysError:

	MOV	AX,3300H
	INT	21H
	MOV	SaveCtrlBreak,DL
	MOV	AX,3301H
	MOV	DL,0
	INT	21H
    IF DPMIVersion
	MOV	AX,dpmiAllocDesc
	MOV	CX,1
	INT	DPMI
	MOV	TempSelector,AX
	MOV	AX,CS
	ADD	AX,SelectorInc
	MOV	ES,AX
	MOV	DI,OFFSET OldInt09
	CLD
	MOV	BL,09H
	CALL	GetProtInt
	MOV	BL,1BH
	CALL	GetRealInt
	MOV	BL,21H
	CALL	GetProtInt
	MOV	BL,23H
	CALL	GetProtInt
	MOV	BL,24H
	CALL	GetRealInt
	MOV	BL,09H
	MOV	DX,OFFSET Int09Handler
	MOV	CX,CS
	CALL	SetProtInt
	MOV	BL,1BH
	MOV	DX,OFFSET Int1BHandler
	MOV	CX,CS
	CALL	SetRealInt
	MOV	ES,Seg0040
	MOV	AX,ES:[10H]
	AND	AX,0C1H
	DEC	AX
	JNE	@@1
	MOV	BL,21H
	MOV	DX,OFFSET Int21Handler
	MOV	CX,CS
	CALL	SetProtInt
@@1:	MOV	BL,23H
	MOV	DX,OFFSET Int23Handler
	MOV	CX,CS
	CALL	SetProtInt
	MOV	BL,24H
	MOV	DX,OFFSET Int24Handler
	MOV	CX,CS
	CALL	SetRealInt
	MOV	AX,dpmiGetProtInt
	MOV	BL,10H
	INT	DPMI
	PUSH	CX
	PUSH	DX
	MOV	AX,dpmiSetProtInt
	MOV	BL,10H
	MOV	DX,OFFSET Int10Handler
	MOV	CX,CS
	INT	DPMI
	MOV	AH,0BH
	INT	21H
	POP	DX
	POP	CX
	MOV	AX,dpmiSetProtInt
	MOV	BL,10H
	INT	DPMI
    ELSE
	PUSH	DS
	XOR	AX,AX
	MOV	DS,AX
	MOV	DI,OFFSET OldInt09
	PUSH	CS
	POP	ES
	CLD
	CLI
	MOV	SI,09H*4
	MOVSW
	MOVSW
	MOV	SI,1BH*4
	MOVSW
	MOVSW
	MOV	SI,21H*4
	MOVSW
	MOVSW
	MOV	SI,23H*4
	MOVSW
	MOVSW
	MOVSW
	MOVSW
	MOV	WORD PTR DS:[09H*4+0],OFFSET Int09Handler
	MOV	WORD PTR DS:[09H*4+2],CS
	MOV	WORD PTR DS:[1BH*4+0],OFFSET Int1BHandler
	MOV	WORD PTR DS:[1BH*4+2],CS
	MOV	AX,DS:[410H]
	AND	AX,0C1H
	DEC	AX
	JNE	@@1
	MOV	WORD PTR DS:[21H*4+0],OFFSET Int21Handler
	MOV	WORD PTR DS:[21H*4+2],CS
@@1:	MOV	WORD PTR DS:[23H*4+0],OFFSET Int23Handler
	MOV	WORD PTR DS:[23H*4+2],CS
	MOV	WORD PTR DS:[24H*4+0],OFFSET Int24Handler
	MOV	WORD PTR DS:[24H*4+2],CS
	STI
	MOV	AX,CS
	XCHG	AX,WORD	PTR DS:[10H*4+2]
	PUSH	AX
	MOV	AX,OFFSET CS:Int10Handler
	XCHG	AX,WORD	PTR DS:[10H*4+0]
	PUSH	AX
	MOV	AH,0BH
	INT	21H
	POP	DS:WORD	PTR [10H*4+0]
	POP	DS:WORD	PTR [10H*4+2]
	POP	DS
    ENDIF
	MOV	AX,OldInt09.w0
	MOV	SaveInt09.w0,AX
	MOV	AX,OldInt09.w2
	MOV	SaveInt09.w2,AX
	MOV	SysErrActive,1
	RETF

; Remove system	error handlers

DoneSysError:

	CMP	SysErrActive,0
	JE	@@1
	MOV	SysErrActive,0
    IF DPMIVersion
	PUSH	DS
	MOV	SI,OFFSET OldInt09
	PUSH	CS
	POP	DS
	CLD
	MOV	BL,09H
	CALL	ResetProtInt
	MOV	BL,1BH
	CALL	ResetRealInt
	MOV	BL,21H
	CALL	ResetProtInt
	MOV	BL,23H
	CALL	ResetProtInt
	MOV	BL,24H
	CALL	ResetRealInt
	POP	DS
	MOV	AX,dpmiFreeDesc
	MOV	BX,TempSelector
	INT	DPMI
    ELSE
	PUSH	DS
	MOV	SI,OFFSET OldInt09
	PUSH	CS
	POP	DS
	XOR	AX,AX
	MOV	ES,AX
	CLD
	CLI
	MOV	DI,09H*4
	MOVSW
	MOVSW
	MOV	DI,1BH*4
	MOVSW
	MOVSW
	MOV	DI,21H*4
	MOVSW
	MOVSW
	MOV	DI,23H*4
	MOVSW
	MOVSW
	MOVSW
	MOVSW
	STI
	POP	DS
    ENDIF
	MOV	AX,3301H
	MOV	DL,SaveCtrlBreak
	INT	21H
@@1:	RETF

    IF DPMIVersion

; Get real mode	interrupt vector

GetRealInt:

	MOV	AX,dpmiGetRealInt
	JMP	SHORT GetIntVector

; Get protected	mode interrupt vector

GetProtInt:

	MOV	AX,dpmiGetProtInt

; Get interrupt	vector

GetIntVector:

	INT	DPMI
	XCHG	AX,DX
	STOSW
	XCHG	AX,CX
	STOSW
	RET

; Reset	real mode interrupt vector

ResetRealInt:

	MOV	AX,dpmiGetRealInt
	INT	DPMI
	PUSH	CX
	PUSH	DX
	LODSW
	XCHG	AX,DX
	LODSW
	XCHG	AX,CX
	MOV	AX,dpmiSetRealInt
	INT	DPMI
	POP	DX
	POP	CX
	MOV	AX,dpmiFreeRMCB
	INT	DPMI
	RET

; Set real mode	interrupt vector

SetRealInt:

	MOV	AX,dpmiAllocRMCB
	MOV	DI,OFFSET RealModeRegs
	PUSH	DS
	POP	ES
	MOV	SI,DX
	MOV	DS,CX
	INT	DPMI
	PUSH	ES
	POP	DS
	MOV	AX,dpmiSetRealInt
	INT	DPMI
	RET

; Reset	protected mode interrupt vector

ResetProtInt:

	LODSW
	XCHG	AX,DX
	LODSW
	XCHG	AX,CX

; Set protected	mode interrupt vector

SetProtInt:

	MOV	AX,dpmiSetProtInt
	INT	DPMI
	RET

; Simulate an IRET in a	real mode call-back

RealModeIRET:

	CLD
	LODSW
	MOV	ES:[DI].realIP,AX
	LODSW
	MOV	ES:[DI].realCS,AX
	LODSW
	MOV	ES:[DI].realFlags,AX
	ADD	ES:[DI].realSP,6
	RET

    ENDIF

; INT 09H handler signature

	DB	'TVI9'

; INT 09H handler

Int09Handler:

	PUSH	DS
	PUSH	DI
	PUSH	AX
    IF DPMIVersion
	MOV	AX,SEG DGROUP
	MOV	DS,AX
	MOV	DS,Seg0040
    ELSE
	MOV	AX,40H
	MOV	DS,AX
    ENDIF
	push	bx
	MOV	DI,DS:KeyBufTail
	mov	bx,ds:KeyBufHead
	IN	AL,60H
	MOV	AH,DS:KeyFlags
	PUSHF
	CALL	OldInt09
	TEST	AL,80H
	JNE	@@9
	PUSH	SI
	PUSH	CX
	MOV	SI,OFFSET CS:KeyConvertTab
	MOV	CX,KeyConvertCnt
@@1:	CMP	AL,CS:[SI]
	JNE	@@2
	TEST	AH,CS:[SI+1]
	jne	@@5
;	JNE	@@3
@@2:	ADD	SI,4
	LOOP	@@1
	JMP	SHORT @@8
;@@3:	CMP	DI,DS:KeyBufTail
;	JNE	@@5
;	MOV	AX,DI
;	INC	AX
;	INC	AX
;	CMP	AX,OFFSET KeyBufEnd
;	JNE	@@4
;	MOV	AX,OFFSET KeyBufOrg
;@@4:	CMP	AX,DS:KeyBufHead
;	JE	@@8
;	MOV	DS:KeyBufTail,AX
;	MOV	DI,AX
@@5:	mov	ds:KeyBufHead,bx
;@@5:	MOV	AX,CS:[SI+2]
	mov	ax,cs:[si+2]
	MOV	DS:[DI],AX
@@8:	POP	CX
	POP	SI
;@@9:	POP	AX
@@9:	pop	bx
	pop	ax
	POP	DI
	POP	DS
	IRET

; INT 1BH handler

Int1BHandler:

    IF DPMIVersion
	CALL	RealModeIRET
	MOV	DS,ES:Seg0040
	AND	BYTE PTR DS:[71H],7FH
	MOV	ES:CtrlBreakHit,1
    ELSE
	PUSH	DS
	PUSH	AX
	XOR	AX,AX
	MOV	DS,AX
	AND	BYTE PTR DS:[471H],7FH
	MOV	AX,SEG DGROUP
	MOV	DS,AX
	MOV	CtrlBreakHit,1
	POP	AX
	POP	DS
    ENDIF
	IRET

; INT 21H handler

Int21Handler:

	PUSHF
	STI
	CMP	AH,36H
	JB	@@1
	CMP	AH,57H
	JA	@@1
	PUSH	DX
	PUSH	BX
	MOV	BL,AH
	XOR	BH,BH
	MOV	BL,CS:FuncClassTab[BX-36H]
	CALL	CS:FuncCheckTab[BX]
	POP	BX
	POP	DX
	JC	@@2
@@1:	POPF
	JMP	OldInt21
@@2:	POPF
	STI
	CMP	AH,36H
	MOV	AX,0FFFFH
	JE	@@3
	MOV	AX,5
@@3:	STC
	RETF	2

; Check	filename

CheckName:

	MOV	BX,DX
	MOV	DX,[BX]
	AND	DL,1FH
	DEC	DL
	CMP	DH,':'
	JE	CheckAbsDrive
	JMP	SHORT CheckCurDrive

; Check	handle

CheckHandle:

	MOV	BX,SP
	MOV	BX,SS:[BX+2]
	PUSH	AX
	MOV	AX,4400H
	PUSHF
	CALL	OldInt21
	POP	AX
	OR	DL,DL
	JNS	CheckAbsDrive
	JMP	SHORT CheckNothing

; Check	drive

CheckDrive:

	DEC	DL
	JNS	CheckAbsDrive

; Check	current	drive

CheckCurDrive:

	PUSH	AX
	MOV	AH,19H
	PUSHF
	CALL	OldInt21
	MOV	DL,AL
	POP	AX

; Check	absolute drive
; In	DL = Drive (0=A, 1=B, etc)
; Out	CF = 1 if drive	swap failed

CheckAbsDrive:

	CMP	DL,2
	JAE	CheckNothing
	PUSH	DS
	PUSH	AX
    IF DPMIVersion
	MOV	AX,SEG DGROUP
	MOV	DS,AX
	MOV	DS,Seg0040
	MOV	AL,DS:[104H]
    ELSE
	XOR	AX,AX
	MOV	DS,AX
	MOV	AL,DS:[504H]
    ENDIF
	CMP	AL,0FFH
	JE	@@1
	CMP	DL,AL
	JE	@@1
	PUSH	ES
	PUSH	DS
	PUSH	DI
	PUSH	SI
	PUSH	DX
	PUSH	CX
	MOV	AX,SEG DGROUP
	MOV	DS,AX
	MOV	AX,15
	PUSH	AX
	PUSH	DX
	CALL	SysErrorFunc
	POP	CX
	POP	DX
	POP	SI
	POP	DI
	POP	DS
	POP	ES
	NEG	AX
	JC	@@1
    IF DPMIVersion
	MOV	DS:[104H],DL
    ELSE
	MOV	DS:[504H],DL
    ENDIF
@@1:	POP	AX
	POP	DS

; No check required

CheckNothing:

	RET

; INT 23H and temporary	INT 10H	handler

Int10Handler:
Int23Handler:

	IRET

; INT 24H handler

Int24Handler:

	STI				;Enable	interrupts
    IF DPMIVersion
	CALL	RealModeIRET
	PUSH	DS			;Save real mode	stack pointer
	PUSH	SI
	PUSH	DI			;Save RealModeRegs pointer
	PUSH	ES			;Point DS to data segment
	POP	DS
	MOV	AL,[DI].realAX.b0	;Get drive code
	MOV	DL,[DI].realDI.b0	;Get error code
	XOR	DH,DH
	CMP	DL,9			;Printer out of	paper?
	JE	@@1			;Yes, @@1
	TEST	[DI].realAX.b1,80H	;Disk error?
	JE	@@2			;Yes, @@2
	MOV	DL,13			;Bad memory image of FAT
	PUSH	DX
	PUSH	AX
	MOV	AX,16			;Get linear address of segment
	MUL	[DI].realBP		;at real mode BP register
	MOV	CX,DX			;Move address to CX:DX
	MOV	DX,AX
	MOV	AX,dpmiSetSegBase	;Set base address of temporary
	MOV	BX,TempSelector		;selector
	INT	DPMI
	MOV	ES,BX			;ES:BX = device	header pointer
	MOV	BX,[DI].realSI
	TEST	BYTE PTR ES:[BX+5],80H	;Block device?
	POP	AX
	POP	DX
	JE	@@1			;Yes, @@0
	INC	DX			;Device	access error
@@1:	MOV	AL,0FFH			;No drive code
@@2:	PUSH	DX			;Push error code
	PUSH	AX			;Push drive code
	CALL	SysErrorFunc		;Call system error handler
	PUSH	DS
	POP	ES
	POP	DI
	POP	SI
	POP	DS
	OR	AX,AX			;Zero if retry
	MOV	DX,1			;Retry return code
	JE	@@4			;Jump if retry
	MOV	AH,54H			;Dummy function	call to	get
	INT	21H			;DOS into a stable state
	CLD
	LODSW				;Pop AX	from real mode stack
	MOV	DL,ES:[DI].realDI.b0	;Get critical error code
	ADD	DL,19			;Return	AX = 19..31
	MOV	DH,0
	CMP	AH,39H			;DOS 2.0 style function?
	JAE	@@3			;Yes, @@3
	MOV	DX,0FFFFH		;Return	AX = 0FFFFH
@@3:	LODSW				;Restore other registers
	MOV	ES:[DI].realBX,AX
	LODSW
	MOV	ES:[DI].realCX,AX
	LODSW
	MOV	ES:[DI].realDX,AX
	LODSW
	MOV	ES:[DI].realSI,AX
	LODSW
	MOV	ES:[DI].realDI,AX
	LODSW
	MOV	ES:[DI].realBP,AX
	LODSW
	MOV	ES:[DI].realDS,AX
	LODSW
	MOV	ES:[DI].realES,AX
	ADD	ES:[DI].realSP,18
	CALL	RealModeIRET		;Simulate IRET
	OR	ES:[DI].realFlags,1	;Set CF	in return flags
@@4:	MOV	ES:[DI].realAX,DX	;Store return code
    ELSE
	PUSH	ES			;Save registers
	PUSH	DS
	PUSH	BP
	PUSH	DI
	PUSH	SI
	PUSH	DX
	PUSH	CX
	PUSH	BX
	AND	DI,0FFH			;Error code in low byte
	PUSH	DI			;Save error code
	CMP	DI,9			;Printer out of	paper
	JE	@@0			;Yes, @@0
	TEST	AH,80H			;Disk error?
	JE	@@1			;Yes, @@1
	MOV	DI,13			;Bad memory image of FAT
	MOV	DS,BP			;Point DS:SI to	device header
	TEST	BYTE PTR DS:[SI+5],80H	;Block device?
	JE	@@1			;Yes, @@0
	INC	DI			;Device	access error
@@0:	MOV	AL,0FFH			;No drive code
@@1:	MOV	DX,SEG DGROUP		;Setup DS
	MOV	DS,DX
	PUSH	DI			;Push error code
	PUSH	AX			;Push drive code
	CALL	SysErrorFunc		;Call system error handler
	POP	DI			;Restore error code
	OR	AX,AX			;Zero if retry
	MOV	AX,1			;Retry return code
	JE	@@3			;Jump if retry
	ADD	SP,(8+3)*2		;Remove	saved regs and INT
	POP	AX			;Get INT 21H AX	register
	ADD	DI,19			;Return	AX = 19..31
	CMP	AH,39H			;DOS 2.0 style function?
	JAE	@@2			;Yes, @@1
	MOV	DI,0FFFFH		;Return	AX = 0FFFFH
@@2:	MOV	AH,54H			;Dummy function	call to	get
	INT	21H			;DOS into a stable state
	MOV	AX,DI			;Get return code
	MOV	BP,SP			;Set CF	in return flags
	OR	BYTE PTR [BP+20],1
@@3:	POP	BX			;Restore registers
	POP	CX
	POP	DX
	POP	SI
	POP	DI
	POP	BP
	POP	DS
	POP	ES
    ENDIF
	IRET

SYSINT_TEXT	ENDS

	END

