359 lines
14 KiB
NASM
359 lines
14 KiB
NASM
|
TITLE INTERRUPT - BASCOM software interrupt calling routine
|
||
|
PAGE 56,132
|
||
|
;***
|
||
|
; INTERRUPT - BASCOM software interrupt calling routine
|
||
|
;
|
||
|
; Copyright <C> 1986, 1987 Microsoft Corporation
|
||
|
;
|
||
|
;Purpose:
|
||
|
; Allows a BASIC program to invoke an interrupt through a CALL statement.
|
||
|
;
|
||
|
; INTERRUPT allows BASIC to set AX,BX,CX,DX,BP,SI,DI, and the flags
|
||
|
; before the call. INTERRUPTX also allows DS and ES to be set.
|
||
|
; Both routines will return the values of the registers upon the
|
||
|
; completion of a successful call. If the interrupt could not
|
||
|
; be generated (due to a bad interrupt number or an illegal array)
|
||
|
; then the interrupt number will be set to -1 to indicate an error.
|
||
|
;
|
||
|
;******************************************************************************
|
||
|
;
|
||
|
;Note:
|
||
|
; The DOSSEG, .MODEL, .CODE, and .DATA? directives used in this program
|
||
|
; are part of the simplified segment system of MASM 5.0. If you have
|
||
|
; an earlier version of MASM, you must modify the source to define
|
||
|
; the segments required by Microsoft high-level languages. These
|
||
|
; segments are discussed in Appendix C of "Learning and Using QuickBASIC."
|
||
|
;
|
||
|
|
||
|
DOSSEG ;requires MASM 5.0 or higher
|
||
|
.MODEL medium
|
||
|
|
||
|
; Define all publicly accessible routines.
|
||
|
|
||
|
PUBLIC INTERRUPT, INTERRUPTX
|
||
|
|
||
|
; Frame structure definition
|
||
|
|
||
|
ARG1 = 0AH ;pointer to first of three arguments
|
||
|
ARG2 = 08H ;pointer to second of three arguments
|
||
|
ARG3 = 06H ;pointer to third of three arguments
|
||
|
|
||
|
; Frame temp variables
|
||
|
|
||
|
UCODE_FLGS = -02H ;user code flag register value
|
||
|
UCODE_DS = -04H ;user code DS register value
|
||
|
REG_NUM = -06H ;number of regs used (INTERRUPT=8, INTERRUPTX=10)
|
||
|
INT_ES = -08H ;INT ES register value
|
||
|
INT_DS = -0AH ;INT DS register value
|
||
|
INT_FLGS = -0CH ;INT flags register value
|
||
|
INT_DI = -1EH ;INT DI register value
|
||
|
INT_SI = -10H ;INT SI register value
|
||
|
INT_BP = -12H ;INT BP register value
|
||
|
INT_DX = -14H ;INT DX register value
|
||
|
INT_CX = -16H ;INT CX register value
|
||
|
INT_BX = -18H ;INT BX register value
|
||
|
INT_AX = -1AH ;INT AX register value
|
||
|
OLD_SI = -1CH ;save old SI for interpreter
|
||
|
OLD_DI = -1EH ;save old DI for interpreter
|
||
|
|
||
|
FRM_SIZ = -1EH ;negative size of frame temporaries
|
||
|
|
||
|
; Locations past frame allocation used to recover post-INT BP value.
|
||
|
|
||
|
INT_BP_TMP = -22H ;temp location for INT BP register value
|
||
|
|
||
|
;***
|
||
|
; INTERRUPT, and INTERRUPTX - BASCOM software interrupt calling interface
|
||
|
;
|
||
|
; Purpose:
|
||
|
; To allow a BASIC Compiler program to perform any software
|
||
|
; interrupt. The interrupt is executed with the registers
|
||
|
; set to values specified in a register variable. The post-
|
||
|
; interrupt values of the registers are then stored in
|
||
|
; another register variable.
|
||
|
;
|
||
|
; CALL INTERRUPT[X] (int_no AS INTEGER,
|
||
|
; inreg AS RegType[X],
|
||
|
; outreg AS RegType[X])
|
||
|
;
|
||
|
; Inputs:
|
||
|
; int_no = interrupt number (range 0 to 255) to execute
|
||
|
; inreg and outreg are register variables of type RegType[X]
|
||
|
; defined as follows;
|
||
|
;
|
||
|
; TYPE RegType
|
||
|
; ax AS INTEGER
|
||
|
; bx AS INTEGER
|
||
|
; cx AS INTEGER
|
||
|
; dx AS INTEGER
|
||
|
; bp AS INTEGER
|
||
|
; si AS INTEGER
|
||
|
; di AS INTEGER
|
||
|
; flags AS INTEGER
|
||
|
; END TYPE
|
||
|
;
|
||
|
;
|
||
|
; TYPE RegTypeX
|
||
|
; ax AS INTEGER
|
||
|
; bx AS INTEGER
|
||
|
; cx AS INTEGER
|
||
|
; dx AS INTEGER
|
||
|
; bp AS INTEGER
|
||
|
; si AS INTEGER
|
||
|
; di AS INTEGER
|
||
|
; flags AS INTEGER
|
||
|
; ds AS INTEGER
|
||
|
; es AS INTEGER
|
||
|
; END TYPE
|
||
|
;
|
||
|
; Outputs:
|
||
|
; If no error:
|
||
|
; int_no = unchanged (range 0 to 255)
|
||
|
; outreg: This array will be set to the post-interrupt
|
||
|
; register values. It has the same structure
|
||
|
; as inreg.
|
||
|
; If error:
|
||
|
; int_no = -1
|
||
|
; outreg unchanged. INT call is not performed.
|
||
|
; error occurs:
|
||
|
; first argument not 0 to 255 (2^8-1)
|
||
|
; second or third arguments not 0 to 1048575 (2^20-1)
|
||
|
; (VARPTR will always be in this range)
|
||
|
;
|
||
|
; Modifies:
|
||
|
; All, except BP, DS, and flags.
|
||
|
; Also, possible side effects of INT call.
|
||
|
;
|
||
|
; Exceptions:
|
||
|
; INT 24H call may result from some INT 21H MS-DOS calls.
|
||
|
;
|
||
|
;******************************************************************************
|
||
|
|
||
|
.CODE
|
||
|
|
||
|
INTERRUPT PROC FAR
|
||
|
|
||
|
PUSH BP ;save BASCOM frame pointer on stack
|
||
|
MOV BP,SP ;establish program frame reference
|
||
|
ADD SP,FRM_SIZ ;allocate working space for frame
|
||
|
MOV WORD PTR [BP].REG_NUM,08H ;eight regs used (not DS or ES)
|
||
|
JMP SHORT INTERRUPT_COMMON ;jump to common code
|
||
|
|
||
|
INTERRUPT ENDP
|
||
|
|
||
|
|
||
|
INTERRUPTX PROC FAR
|
||
|
|
||
|
PUSH BP ;save BASCOM frame pointer on stack
|
||
|
MOV BP,SP ;establish program frame reference
|
||
|
ADD SP,FRM_SIZ ;allocate working space for frame
|
||
|
MOV WORD PTR [BP].REG_NUM,0AH ;ten regs used (including DS and ES)
|
||
|
|
||
|
; Save a copy of the processor flags, SI, DI, and DS in the stack frame.
|
||
|
|
||
|
INTERRUPT_COMMON:
|
||
|
MOV [BP].OLD_SI,SI ;save old SI for interpreter
|
||
|
MOV [BP].OLD_DI,DI ;save old DI for interpreter
|
||
|
MOV [BP].UCODE_DS,DS;save DS for interpreter
|
||
|
PUSHF ;push the flags on the stack
|
||
|
POP [BP].UCODE_FLGS ;put value in the stack frame
|
||
|
|
||
|
; Move eight or ten words (depending if executing INTERRUPT or INTERRUPTX)
|
||
|
; of the integer input array from the far pointer computed to the frame.
|
||
|
|
||
|
MOV SI,[BP].ARG2 ;and array offset - pointer in DS:SI
|
||
|
LEA DI,[BP].INT_AX ;get start of temporary register storage.
|
||
|
MOV CX,[BP].REG_NUM ;eight or ten words to move
|
||
|
CLD ;movement is to higher memory
|
||
|
PUSH SS
|
||
|
POP ES
|
||
|
REP MOVSW ;move the array into the stack frame
|
||
|
|
||
|
; Save stack frame pointer to recover its value after the INT call.
|
||
|
|
||
|
PUSH BP ;saved to first word past the stack frame
|
||
|
|
||
|
; Create a two-instruction program on the stack to execute the
|
||
|
; INT call requested and return with stack cleanup.
|
||
|
;
|
||
|
; INT XX (hex: CD XX)
|
||
|
; RETF 06 (hex: CA 06 00)
|
||
|
;
|
||
|
; In the case of INT 25 and 26 (which leave a word of flags on the stack)
|
||
|
; We generate:
|
||
|
;
|
||
|
; INT XX (hex: CD XX)
|
||
|
; ADD SP,2 (hex: 83 C4 02)
|
||
|
; RETF 08 (hex: CA 08 00)
|
||
|
;
|
||
|
MOV SI,[BP].ARG1 ;[SI] = ptr to first CALL arg - interrupt #
|
||
|
MOV BX,[SI] ;[BL] = get integer value of INT type
|
||
|
OR BH,BH ;test if in range, 00 to FFH is legal
|
||
|
JZ NO_INT_ERROR ;if not, then error - jump
|
||
|
JMP INT_ERROR ;long jump to error routine
|
||
|
NO_INT_ERROR:
|
||
|
|
||
|
CMP BL,25H ;Interrupt 25 request?
|
||
|
JZ Int2526 ;Jump if so
|
||
|
CMP BL,26H ;Interrupt 26 request?
|
||
|
JNZ IntNorm ;Jump if other, "normal" interrupt
|
||
|
Int2526:
|
||
|
MOV AX,8 ;[AX] = argument of RETF instruction
|
||
|
PUSH AX
|
||
|
MOV AX,0CA02H ;[AX] = RETF opcode, & arg to ADD SP
|
||
|
PUSH AX
|
||
|
MOV AX,0C483H ;[AX] = ADD SP, opcode
|
||
|
PUSH AX
|
||
|
JMP SHORT IntInstruct
|
||
|
|
||
|
IntNorm:
|
||
|
XOR AX,AX ;value of second word past frame
|
||
|
PUSH AX ;put on stack - 00 byte of RETF and filler
|
||
|
MOV AX,06CAH ;value of third word past frame
|
||
|
PUSH AX ;put on stack - CA 06 bytes of RETF
|
||
|
IntInstruct:
|
||
|
MOV AH,BL ;move interrupt number to upper byte of AX
|
||
|
MOV AL,0CDH ;value of fourth word past frame
|
||
|
PUSH AX ;put on stack - CD XX bytes of INT XX
|
||
|
|
||
|
; Push far pointer of return address after the stack program
|
||
|
; executes, which is INT_RET in this code segment.
|
||
|
|
||
|
PUSH CS ;push current code segment for return segment
|
||
|
MOV AX,OFFSET INT_RET ;offset just after stack program call
|
||
|
PUSH AX ;push value for return offset
|
||
|
|
||
|
; Push far pointer pointer to the start of the stack program.
|
||
|
; The stack program will be entered by executing a RETF after the
|
||
|
; registers are set up.
|
||
|
|
||
|
PUSH SS ;push current stack segment for starting ptr
|
||
|
MOV AX,SP ;get current stack offset
|
||
|
ADD AX,6 ;move past the last three stack entries
|
||
|
PUSH AX ;push offset for starting ptr of stack program
|
||
|
|
||
|
; Move the input array values from the stack to their actual registers.
|
||
|
|
||
|
MOV AX,[BP].INT_FLGS ;get input flag register value
|
||
|
AND AX,0000111111010101B ;mask out undefined 8086 flags
|
||
|
PUSH AX ;push masked flag register value
|
||
|
|
||
|
MOV AX,[BP].INT_AX ;set up input AX value
|
||
|
MOV BX,[BP].INT_BX ;set up input BX value
|
||
|
MOV CX,[BP].INT_CX ;set up input CX value
|
||
|
MOV DX,[BP].INT_DX ;set up input DX value
|
||
|
|
||
|
MOV SI,[BP].INT_SI ;set up input SI value
|
||
|
MOV DI,[BP].INT_DI ;set up input DI value
|
||
|
|
||
|
; For DS and ES, leave in the compiler data segment values if:
|
||
|
; executing INTERRUPT; or executing INTERRUPTX with array values of -1.
|
||
|
|
||
|
CMP WORD PTR [BP].REG_NUM,08H ;test if executing INTERRUPT
|
||
|
JE INT_ES_DEF ;if so, then use both default values
|
||
|
|
||
|
CMP [BP].INT_DS,0FFFFH ;test if default DS to be used
|
||
|
JE INT_DS_DEF ;if so, then leave it unchanged
|
||
|
MOV DS,[BP].INT_DS ;set up input DS value
|
||
|
INT_DS_DEF:
|
||
|
CMP [BP].INT_ES,0FFFFH ;test if default ES to be used
|
||
|
JE INT_ES_DEF ;if so, then leave it unchanged
|
||
|
MOV ES,[BP].INT_ES ;set up input ES value
|
||
|
INT_ES_DEF:
|
||
|
|
||
|
MOV BP,[BP].INT_BP ;set up input BP value
|
||
|
;must be last move using BP
|
||
|
|
||
|
POPF ;set up input flag register value
|
||
|
|
||
|
; With all registers set according to the input array, execute the
|
||
|
; stack program.
|
||
|
;
|
||
|
; The following RETF pops the last two stack entries, which are
|
||
|
; interpreted as a far pointer to the stack program.
|
||
|
;
|
||
|
; The stack program executes the INT XX call which changes the
|
||
|
; registers (flags included) to the values to be put into the
|
||
|
; output array.
|
||
|
;
|
||
|
; The stack program then executes the RETF 06 instruction which
|
||
|
; does two operations. First, the next two entries on stack are
|
||
|
; popped and interpreted as a far ptr return address, which points
|
||
|
; the code at INT_RET in this code segment. Second, the stack
|
||
|
; pointer is then adjusted by six bytes to remove the six-byte
|
||
|
; program from the stack.
|
||
|
|
||
|
RET ;far return to execute stack program, etc.
|
||
|
INT_RET:
|
||
|
|
||
|
; The stack should now contain only the first entry past the
|
||
|
; frame, the value of the stack frame pointer itself. First
|
||
|
; save the BP value from the INT call, then get the old value
|
||
|
; to reference the frame.
|
||
|
|
||
|
PUSH BP ;save post-INT value of BP
|
||
|
MOV BP,SP ;temporary frame is second word past frame
|
||
|
MOV BP,[BP+02H] ;get real frame reference value
|
||
|
|
||
|
; Put post-INT value of all registers into the frame variables
|
||
|
; to be subsequently written into the output array.
|
||
|
|
||
|
PUSHF ;put flags on the stack
|
||
|
POP [BP].INT_FLGS ;put in post-INT flag register value
|
||
|
|
||
|
PUSH [BP].UCODE_FLGS ;get old copy of flags from frame
|
||
|
POPF ;and restore the old flag values
|
||
|
|
||
|
MOV [BP].INT_AX,AX ;put in post-INT AX value
|
||
|
MOV [BP].INT_BX,BX ;put in post-INT BX value
|
||
|
MOV [BP].INT_CX,CX ;put in post-INT CX value
|
||
|
MOV [BP].INT_DX,DX ;put in post-INT DX value
|
||
|
|
||
|
MOV AX,[BP].INT_BP_TMP ;get post-INT BP value (one entry past frame)
|
||
|
MOV [BP].INT_BP,AX ;put in post-INT BP value
|
||
|
|
||
|
MOV [BP].INT_SI,SI ;put in post-INT SI value
|
||
|
MOV [BP].INT_DI,DI ;put in post-INT DI value
|
||
|
|
||
|
MOV [BP].INT_DS,DS ;put in post-INT DS value
|
||
|
MOV [BP].INT_ES,ES ;put in post-INT ES value
|
||
|
|
||
|
; Move frame register values to the output array whose
|
||
|
; far pointer is in the frame.
|
||
|
|
||
|
MOV DS,[BP].UCODE_DS;replace original DS value
|
||
|
|
||
|
LEA SI,[BP].INT_AX ;get start of register area in frame
|
||
|
|
||
|
PUSH DS
|
||
|
POP ES
|
||
|
MOV DI,[BP].ARG3 ;get output array offset
|
||
|
MOV CX,[BP].REG_NUM ;eight or ten words to move
|
||
|
CLD ;movement is toward upper memory
|
||
|
REP MOVSW ;perform the transfer
|
||
|
|
||
|
; Clean up stack to remove frame. Remove CALL arguments with RETF.
|
||
|
|
||
|
MOV SI,[BP].OLD_SI ;replace old SI for interpreter
|
||
|
MOV DI,[BP].OLD_DI ;replace old DI for interpreter
|
||
|
MOV SP,BP ;deallocate temporary frame variables
|
||
|
POP BP ;return compiler frame pointer
|
||
|
RET 06 ;remove three CALL arguments and far return
|
||
|
|
||
|
; If error, then restore DS, set int_no to -1 to report error,
|
||
|
; clean up, and exit.
|
||
|
|
||
|
INT_ERROR:
|
||
|
MOV SI,[BP].ARG1 ;ptr to first CALL arg - interrupt number
|
||
|
MOV [SI],0FFFFH ;set interrupt number to -1 for error
|
||
|
MOV SI,[BP].OLD_SI ;replace old SI for interpreter
|
||
|
MOV DI,[BP].OLD_DI ;replace old DI for interpreter
|
||
|
MOV DS,[BP].UCODE_DS;replace original DS value
|
||
|
MOV SP,BP ;deallocate temporary frame variables
|
||
|
POP BP ;return compiler frame pointer
|
||
|
RET 06 ;remove three CALL arguments and far return
|
||
|
|
||
|
INTERRUPTX ENDP
|
||
|
|
||
|
END
|