	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

scSpaceKey	EQU	39H
scInsKey	EQU	52H
scDelKey	EQU	53H
scBackKey	EQU	0EH

; 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	scBackKey,kbAltKey
        DW	0800H

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
	MOV	DI,DS:KeyBufTail
	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	@@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	AX,CS:[SI+2]
	MOV	DS:[DI],AX
@@8:	POP	CX
	POP	SI
@@9:	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
