I am developing a 16 bit DOS program that can read data that is coming through a Communication Port on a PC. I want to use this program to receive data from a weighing scale through the pc's Keyboard Buffer. Once the data is in the keyboard buffer I will be able to access it in an application program. I am able to link the program using the command "ML readmass.asm /AT". When I run it is first displaying gabbage followed by the Copywrite information and then the keyboard freezes.
The idea is that once the program is loaded into memory, when I simultaneously press the SHIFT and DOLLAR keys the data should be transmitted to the keyboard and should appear on the screen. Where am I going wrong?
Attached is the program code.
;*****************************************************************************
;* *
;* Asynchronous reader program for electronic scale *
;* READMASS.COM TSR *
;* *
;*****************************************************************************
rom_bios segment at 40h ; BIOS stats held here, also kb buffer
org 65h
modeset db ?
org 1ah
head dw ? ; unread chars go from head to tail
tail dw ?
buffer dw 16 dup (?) ; the buffer itself
buffer_end label word
rom_bios ends
;============== Code segment ==================================================
rmcode segment para 'CODE'
org 2ch
environ label word
org 80h
params label byte
org 82h
parami label byte
org 100h
begin:
jmp init_vectors
;-------------- Miscellaneous storage areas ----------------------------------
timer1 dw 07000 ; receive timeout
rxaddr label dword ; receive data address
rxoff dw 0
rxseg dw 0
rxchars dw 0 ; 0=waiting for 1st char
; 255=in receive
cbuffer db 64 dup(?) ; control buffer
reentry db 0 ; set to 255 if busy
cs_value dw 0 ; our code segment value
old_int_9 label dword ; int 9 server before us
rom_keyboard dw 2 dup (0)
hotkey dw 00524h ; read scale on key
comport db 0 ; default com port
;******************** Keyboard Handler Section *******************************
newint9 proc far
assume cs:rmcode
push ax
push bx
push cx
push dx
push di
push si
push ds
push es
pushf
call old_int_9
assume cs:nothing
assume ds:rom_bios
mov ax,ds
mov ds,ax
mov bx,tail
cmp bx,head
je exiti1
sub bx,2
cmp bx,offset buffer
jae no_wraprx
mov bx,offset buffer_end
sub bx,2
no_wraprx: mov dx,[bx]
assume ds:rmcode
push cs
pop ds
cmp dx,hotkey
jnz exiti1
assume ds:rom_bios
mov ax,40h
mov ds,ax
mov tail,bx ; remove F12 key from kb buf
assume ds:rmcode
push cs
pop ds
;mov al,5 ; send ENQ to scale
;mov ah,1 ; transmit port service
;mov dh,0 ; clear top
;mov dl,comport ; get port
;int 14h ; send it
mov timer1,0ffffh ; seed timeout +- 2 second
waitfor:
jmp going ; in loop
;waitfor: dec timer1
;cmp timer1,0
;jnz going
;mov rxchars,0
;mov ah,0eh ; beep
;mov al,07 ; bell char
;mov bh,0 ; tell main screen
;int 10h
;jmp exiti1
going: mov ah,3 ; receive ready service
mov dh,0 ; clear top
mov dl,comport ; which port
int 14h ; check for char, if any
and ah,1 ; mask for data ready
cmp ah,1 ; test for data ready
jnz waitfor ; no, wait for it
mov al,0 ; set to 0 so we can test for char in
mov ah,2 ; receive service
mov dh,0 ; clear top
mov dl,comport ; which port
int 14h ; read char
jmp overs
exiti1: jmp exiti
overs: mov bx,rxchars ; are we in receive or 1st char
cmp bx,0 ; 1st char
jnz inrecv ; no, already receiving
;cmp al,2 ; is it STX
cmp al,32 ; is it STX
jnz waitfor
mov rxchars,255 ; yes, it is STX - set in receive
mov rxoff,offset cbuffer ; rx into buffer
mov rxseg,cs ; this segment
mov cx,15 ; clear buffer
clearbuf: les bx,rxaddr ; get current receive address
mov byte ptr es:[bx],0 ; save character
inc rxoff
loop clearbuf
mov rxoff,offset cbuffer ; rx into buffer
mov rxseg,cs ; this segment
inrecv: les bx,rxaddr ; get current receive address
mov byte ptr es:[bx],al ; save character
;cmp al,3 ; is it ETX?
cmp al,103 ; is it ETX?
jz recvdone ; yes, end of packet
inc rxoff ; point to next byte in buffer
jmp waitfor ; no, wait for rest
;-------------- End of packet - decide what to do ----------------------------
recvdone: mov cx,12 ; move 12 digits to kb buffer
mov si,11 ; src cbuffer offset
cli ; no ints during kb io
loopie1: mov al,cbuffer[si] ; get char from cbuffer
call transl
assume ds:rmcode
mov ax,cs:cs_value
mov ds,ax
inc si ; point to next
loop loopie1 ; till cx = 0
sti ; all done
; this section is for the SCSI PLC action
assume cs:nothing
mov dx,02a8h ; port for SCSI
mov al,1
out dx,al ; send code to PLC
mov cx,0ffffh ; delay period
loop97: loop loop97
mov cx,0ffffh ; delay period
loop98: loop loop98
mov cx,0ffffh ; delay period
loop99: loop loop99
assume cs:nothing
mov dx,02a8h ; port for SCSI
mov al,0
out dx,al ; send code to PLC
; this section is for the SCSI PLC action
; this section is for the COMX PLC action
; mov al,5 ; send ENQ to scale
; mov ah,1 ; transmit port service
; mov dh,0 ; clear top
; mov dl,3 ; set port 0-3 0=COM1 .. 3=COM4
; int 14h ; send it
; this section is for the COMX PLC action
mov rxchars,0 ; reset start of packet
exiti: pop es
pop ds
pop si
pop di
pop dx
pop cx
pop bx
pop ax
iret
newint9 endp
;============== Miscellaneous routines =======================================
checkint proc near
push ax
push bx
push es
mov ah,35h
mov al,63h
int 21h
cmp bx,01fffh
jnz away
mov bx,es
cmp bx,0a000h
jnz away
stc
jmp awayc
away: clc
awayc: pop es
pop bx
pop ax
retn
checkint endp
transl proc near
assume ds:rom_bios
push ax
mov ax,ds
mov ds,ax
pop ax
cmp al,'0' ; 0?
jnz next1 ; no, try next
mov dx,0b30h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next1: cmp al,'1' ; 1?
jnz next2 ; no, try next
mov dx,0231h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next2: cmp al,'2' ; 2?
jnz next3 ; no, try next
mov dx,0332h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next3: cmp al,'3' ; 3?
jnz next4 ; no, try next
mov dx,0433h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next4: cmp al,'4' ; 4?
jnz next5 ; no, try next
mov dx,0534h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next5: cmp al,'5' ; 5?
jnz next6 ; no, try next
mov dx,0635h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next6: cmp al,'6' ; 6?
jnz next7 ; no, try next
mov dx,0736h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next7: cmp al,'7' ; 7?
jnz next8 ; no, try next
mov dx,0837h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next8: cmp al,'8' ; 8?
jnz next9 ; no, try next
mov dx,0938h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
next9: cmp al,'9' ; 9?
jnz nextspc ; no, try next
mov dx,0a39h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
nextspc: cmp al,' ' ; space?
jnz nextdot ; no, try next
mov dx,0b30h ; mov 0
; mov dx,3920h ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
nextdot: cmp al,'.' ; .?
jnz nextcomma ; no, try next
mov dx,342eh ; set up code
call putbuf ; put the char in kb buffer
jmp alldone ; done, read next of 7
nextcomma: cmp al,',' ; ,?
jnz alldone ; no, all done
mov dx,342eh ; mov .
; mov dx,332ch ; set up code
call putbuf ; put the char in kb buffer
alldone: retn
transl endp
putbuf proc near
mov bx,tail ; get tail
add bx,2 ; check next pos
cmp bx,offset buffer_end ; for end of buffer
jle no_wrap ; no, still room for 2
mov bx,offset buffer ; yes, wrap around
add bx,2 ; point to next pos
no_wrap: mov word ptr [bx-2],dx ; copy scancode into kb buf
cmp bx,offset buffer_end ; for end of buffer
jne no_wrap1 ; no, not at end
mov bx,offset buffer ; yes, wrap around
no_wrap1: mov tail,bx ; set new tail
retn
putbuf endp
;============== end of resident part ========================
dbuffer db 0
;============== INITIALISATION ===============================================
init_vectors: jmp init
msg0 db 0dh,0ah,0ah,'READMASS scale driver',0dh,0ah
db ' Adapted for KI20 scale and SCSI PLC control.',0dh,0ah
db ' Copyright Kasonya A.J. ',0dh,0ah,'$'
helpmsg db 0dh,0ah,'Syntax: READMASS [P=1|2|3|4] [/A] [/H]',0dh,0ah
db ' where: P=X - com port to use COM1: thru COM4:',0dh,0ah
db ' /A - ask to install',0dh,0ah
db ' /H - display this help message',0dh,0ah
db 'The hotkey to read the scale is F12.',0dh,0ah
db 'If READMASS is called without parameters, it will install on ',0dh,0ah
db 'COM1: 2400 odd 7bits 1 stop bit',0dh,0ah,0ah,'$'
badparm3 db 'Unknown parameter encountered',0dh,0ah,'$'
askinst db 'Should READMASS be installed? (Y/N) $'
operterm db 'Operator termination',0dh,0ah,'$'
alrins db 0dh,0ah,'Program already installed!',0dh,0ah,'$'
termmsg db 'READMASS not installed!',0dh,0ah,0ah,'$'
replymsg db ' ',0dh,0ah,0ah,'$'
assume ds:rmcode,es:nothing
init: mov cs_value,cs ; set code segment value
mov dx,offset msg0 ; print greeting message
call dmsg
push cs
pop ds
call checkint ; see if already installed
jnc init0001 ; no, carry-on
mov dx,offset alrins ; yes, say so and quit
jmp init0900
;-------------- Interpret command tail ---------------------------------------
init0001: mov al,[params] ; check for parameters
cmp al,0 ; none, use defaults
jz init0500
init0002: mov bx,offset parami ; get command line address
jmp init0011
init0010: inc bx
init0011: cmp byte ptr [bx],' ' ; intervening blank?
je init0010 ; yes - jump over
cmp byte ptr [bx],0dh ; end of line?
jne init0015 ; no
jmp init0500 ; finish off initialisation
init0015: cmp word ptr [bx],'A/' ; ask for install
je init0050
cmp word ptr [bx],'a/' ; ask for install
je init0050
init0016: cmp word ptr [bx],'H/' ; ask for help
je init0100
cmp word ptr [bx],'h/' ; ask for help
je init0100
cmp word ptr [bx],'?/' ; ask for help
je init0100
init0017: mov dx,offset badparm3 ; prepare for bad parameter
call xportprm ; try for a transport parameter
jnc init0010 ; was a transport parameter
jmp init0900 ; bad choice
;-------------- Process /A switch --------------------------------------------
init0050: add bx,1 ; point to next
init0060: mov dx,offset askinst ; asking message
call dmsg ; display message
mov ah,0 ; get a keyboard character
int 16h ; using BIOS
push ax
mov replymsg,al ; put reply in message
mov dx,offset replymsg
call dmsg
pop ax
and al,0dfh ; convert reply to uppercase
cmp al,'Y'
jne init0070
jmp init0010 ; it must be installed
init0070: cmp al,'N'
jne init0060 ; didn't understand the question
mov dx,offset operterm
jmp init0900 ; terminate
;-------------- Process /H switch --------------------------------------------
init0100: mov dx,offset helpmsg ; help message
jmp init0900 ; terminate
;-------------- Finalise initialisation --------------------------------------
init0500: mov dx,offset commsg
call dmsg ; display signon message
call xpinit ; initialise transport
assume ds:rmcode
mov ax,cs
mov ds,ax
mov al,9 ; set int 9
mov ah,35h
int 21h
mov rom_keyboard,bx
mov bx,es
mov rom_keyboard[2],bx
mov ah,25h
mov al,9
mov dx,offset newint9
int 21h
mov ah,25h ; set int 63h to A000:1FFF
mov al,63H
mov bx,0a000h
mov ds,bx
mov dx,01fffh
int 21h
mov dx,offset init_vectors ; TSR
int 27h
;-------------- Bad parameter - terminate initialisation ---------------------
init0900: call dmsg ; display given message
mov dx,offset termmsg
call dmsg ; display given message
mov ah,4ch
mov al,0
int 21h
;-------------- Process transport parameters ---------------------------------
commsg db 'Using serial port com'
portno db '1:',0dh,0ah,'$'
badport db 'Can only have P=1 or P=2 or P=3 or P=4',0dh,0ah,'$'
xportprm proc near
cmp word ptr es:[bx],'=P'
je xprm0000
cmp word ptr es:[bx],'=p'
jne xprm0005
xprm0000: add bx,2 ; point to next
mov al,byte ptr es:[bx] ; get the character
cmp al,'1' ; is it com1?
je xprm0010 ; yes
cmp al,'2' ; is it com2?
je xprm0010 ; yes
cmp al,'3' ; is it com3?
je xprm0010 ; yes
cmp al,'4' ; is it com4?
je xprm0010 ; yes
mov dx,offset badport
xprm0005: stc ; indicate bad parameter
retn ; and return to caller
xprm0010: mov portno,al ; set for display
sub al,'1' ; make it in range 0 - 3
mov comport,al ; set port number
clc ; indicate no error
retn ; done
xportprm endp
;-------------- Initialise transport -----------------------------------------
xpinit proc near
mov al,10101010b ; set for 2400/o/7/1
mov dh,0 ; clear top
mov dl,comport ; get port number
mov ah,0 ; set com params
int 14h ; do it
retn
xpinit endp
;-------------- Display message ----------------------------------------------
dmsg proc near
mov ah,9
int 21h
retn
dmsg endp
rmcode ends
end
Hi Kasonya,
In the order encountered:
The ASSUME directive does not set any segment register values -- it tells MASM which segment to associate with a segment register. The programmer must provide code to actually load the appropriate value into the segment register.
For MASM 6.0+, the ASSUME directive is not necessary when you change code segments.
When an interrupt handler is called the only segment register with a known value is CS, and it will have the value that was specified by DS when the vector was set.
Most instructions that access data use DS by default. In your interrupt handler (at run time) DS is set to some unknown value, so to access old_int_9 you must first load the segment address of the resident data into DS, or use a CS segment override. The same goes for any other data that the handler accesses.
To correctly simulate an Interrupt instruction with a far call, you should execute a CLI after pushing the flags, and before the far call.
To load the address of the rom_bios segment into DS, I think you would need to use the SEG operator, something like:
mov ax,seg rom_bios
mov ds,ax
You appear to be setting the Interrupt 63h vector to a location in the display buffer. What happens if some process actually calls Interrupt 63h, expecting a valid handler there?
Why is checkint in the resident part?
There are probably other errors that I am failing to see. Why a TSR? Why pass the data through the keyboard buffer? Why not include the code to receive the data directly in the application?
HI MichaelW,
The application language that I am using is Progress 4GL and as far as I know has no routine for accessing a Serial Port. The TSR program would enable me to get the scale reading from the Keyboard Buffer instead of an operator manually getting the result from the scale and capturing it on the keyboard. Thanks for your suggestions I will try them.
I have a few more suggestions.
Instead of placing the keys directly into the keyboard buffer, it might be easier to use the BIOS Interrupt 16h function (AH=05h).
Depending on how long it takes to read the data from the scale, there could be a reentrancy problem with the calls to Interrupt 14h from the Interrupt 9 handler.
Because of potential problems with other methods of determining if a TSR was installed, I eventually started adding a paragraph-aligned signature to the resident part, and coding the loader to search for the signature. By paragraph-aligning the signature, and searching for a match to the first byte before checking for a full match, the search could be completed in a negligible amount of time (searching the bottom 1MB of the address space). There turned out to be a problem with the search finding an aligned copy of the signature in the DOS disk buffers, but this was easily corrected by coding the search routine to modify the signature in the loaded program before it performed the search. If the search found no match, the loader would then install the TSR with the modified signature. For a signature I normally used the program name and version number.
A complex TSR can be very difficult to debug. If I were doing this, I think I would use a normal program to develop and debug the handler and associated components. The program could install the handler and then wait in a loop, monitoring the state of whatever, and then uninstall the handler before terminating.
Kasonya,
Does Progress 4GL run under DOS or Windows or just Linux/Unix? Or are you asking about a client program/machine that only interacts with Progress 4GL? If so, does the client machine run DOS or Windows or something else?
farrier
farrier,
I am asking about a client program that only interacts with the Progress 4GL. The client machine is runing MSDOS and connects to the Progress Server through Inet Software. The scale is attached to the client machine. The progress server is running on Sun Solaris Unix. The Sun Solaris server is connected to a Windows 2000 Network. In addtion connecting through Inet software you can also connect to the Solaris server using Telnet.
Kasonya.
Kasonya,
If you do not have control over the DOS program source code, you may have to do something like what you have described. I do a very similar thing in a Windows program. I watch every key stroke while in the dialog used for weighing trucks, and if a W key is pressed and the cursor is in the weight field, I read the serial port in question and set the contents of the weight field accordingly. You have described something very similar, but I'm not qualified to offer help with a DOS TSR, serial port reader and keyboard stuffer. If I can help in any way, I will try.
Good luck,
farrier
The attachment contains two 16-bit DOS programs that demonstrate one method of responding to a hotkey and passing an arbitrary sequence of keystrokes to another 16-bit DOS program, all without involving a TSR. The method works OK under Windows 2000, and probably under Windows XP, and it avoids most of the problems with developing and debugging a TSR.
; -------------------------------------------------------------
; This is the MASM 6+ source for a 16-bit DOS program that
; installs temporary handlers for interrupts 15h and 16h
; before executing another 16-bit DOS program. The interrupt
; 15h handler monitors for the hot key (Shift+$). When the
; hot key is detected the interrupt 16h handler switches to
; a mode where it intercepts calls (from the executed program)
; to the BIOS Get Keystroke, Check For Keystroke, Get Enhanced
; Keystroke, or Check For Enhanced Keystroke functions, and
; returns a predefined sequence of keystroke data.
;
; Although this program does pass its command line (and
; environment) to the executed program, it does not use the
; command line itself.
; -------------------------------------------------------------
; These parameters defined here for easy edit.
;
PROGRAM_NAME EQU "showkeys.com"
HOTKEY_SCAN_CODE EQU 5
HOTKEY_SHIFT_MASK EQU 11b
; Declare the LoadExec structure used by the DOS Load
; and Execute Program function.
;
LoadExec STRUCT
leEnvironment dw 0 ; environment-block segment
leCommandTail dd 0 ; address of command tail
leFCB_1 dd 0 ; address of default FCB #1
leFCB_2 dd 0 ; address of default FCB #1
LoadExec ENDS
.model tiny ; generate a COM file
.386 ; enable 32-bit instructions
.stack ; default size (will be changed)
.code ; start code segmemt
.startup ; for tiny, expands to ORG 100h
; Place the data in the code segment for easy
; access from the interrupt handlers.
jmp @F ; jump around data
; Define structure var using the default initializers.
;
LoadArgs LoadExec <>
progName db PROGRAM_NAME,0
msgExec db "Error executing child program, $"
msgExit db "press any key to exit...$"
prevIsr15h dd 0 ; previous Interrupt 15h handler
prevIsr16h dd 0 ; previous Interrupt 16h handler
fActive dw 0 ; set when active
firstKey dw 2348h ; H
dw 1769h ; i
dw 3920h ;
dw 1454h ; T
dw 2368h ; h
dw 1265h ; e
dw 1372h ; r
dw 1265h ; e
dw 0221h ; !
lastKey dw 1c0dh ; <enter>
nextKey dw firstKey ; offset of next key in sequence
@@:
; Move stack and release unneeded memory.
mov bx,OFFSET theEnd
mov sp,bx ; move stack
shr bx,4 ; convert to paragraphs
inc bx ; correct for remainder
mov ah,4Ah ; set memory block size
int 21h ; (ES specifies memory block)
; Hook Interrupt 15h.
mov ax,3515h ; get interrupt vector
int 21h ; returns handler address in ES:BX
mov WORD PTR prevIsr15h,bx
mov WORD PTR prevIsr15h+2,es
mov ax,2515h ; set interrupt vector
mov dx,OFFSET Isr15h ; DS:DX -> new interrupt handler
int 21h
; Hook Interrupt 16h.
mov ax,3516h ; get interrupt vector
int 21h ; returns handler address in ES:BX
mov WORD PTR prevIsr16h,bx
mov WORD PTR prevIsr16h+2,es
mov ax,2516h ; set interrupt vector
mov dx,OFFSET Isr16h ; DS:DX -> new interrupt handler
int 21h
; Initialize LoadArgs such that child program will receive
; exact copy of our environment, commandTail, and FCB's.
mov LoadArgs.leEnvironment,0
mov WORD PTR LoadArgs.leCommandTail,80h
mov WORD PTR LoadArgs.leCommandTail+2,ds
mov WORD PTR LoadArgs.leFCB_1,5Ch
mov WORD PTR LoadArgs.leFCB_1+2,ds
mov WORD PTR LoadArgs.leFCB_2,6Ch
mov WORD PTR LoadArgs.leFCB_2+2,ds
; Execute program directly as child.
mov dx,OFFSET progName ; DS:DX -> path
push ds
pop es
mov bx,OFFSET LoadArgs ; ES:BX -> LoadExec structure
mov ax,4B00h ; load and execute
int 21h
.if CARRY?
mov dx,OFFSET msgExec
mov ah,9 ; write string to stdout
int 21h
.endif
;Clean up and exit.
push ds
mov ax,2515h ; set interrupt vector
lds dx,prevIsr15h ; DS:DX -> interrupt handler
int 21h ; restore original handler
pop ds
push ds
mov ax,2515h ; set interrupt vector
lds dx,prevIsr16h ; DS:DX -> interrupt handler
int 21h ; restore original handler
pop ds
mov dx,OFFSET msgExit
mov ah,9 ; write string to stdout
int 21h
mov ah,10h ; get keystroke (will wait)
int 16h
.exit
; -------------------------------------------------------------
; Interrupt 15h handler. This handler tests for the hot key
; and, when it is detected, sets the active flag, absorbs the
; hot key, and returns directly to the caller (which will
; always be the BIOS as this program was the last to hook
; interrupt 15h). If the hot key is not detected this handler
; chains to the previous handler.
; -------------------------------------------------------------
CF EQU 1 ; bit value for carry flag
Isr15h proc
push bx
push es
.if ah == 4fh ; keyboard intercept function?
.if al == HOTKEY_SCAN_CODE
push 40h
pop es
push 17h
pop bx ; ES -> BIOS shift flags byte
test BYTE PTR es:[bx],HOTKEY_SHIFT_MASK
.if !ZERO?
mov cs:fActive,1
; Clear the carry flag in the return flags (the copy
; of the flags that INT pushed onto the stack) so
; the BIOS will ignore the hot key. The IRET will
; pop these flags into the flags register. The
; contents of the stack, at this point:
; SP+8 -> return flags
; SP+6 -> return CS
; SP+4 -> return IP
; SP+2 -> preserved BX
; SP -> preserved ES
;
mov bx,sp
xor BYTE PTR ss:[bx+8],CF
pop es
pop bx
iret ; return directly to caller
.endif
.endif
.endif
pop es
pop bx
jmp cs:prevIsr15h ; chain to previous handler
Isr15h endp
; -------------------------------------------------------------
; Interrupt 16h handler. This handler tests the state of the
; active flag and, if it is set, returns the next key in the
; predefined keystroke sequence. When all of the keys in the
; sequence have been returned, this handler clears the active
; flag and reinitializes the sequence start address. If the
; active flag is not set this handler simply chains to the
; previous handler.
; -------------------------------------------------------------
ZF EQU 01000000b ; bit value for zero flag
Isr16h proc
.if cs:fActive
push bx
mov bx,cs:nextKey
.if ah == 1 || ah == 11h
mov ax,cs:[bx] ; set return ascii and scan codes
; Clear the zero flag in the return flags (the copy of
; the flags that INT pushed onto the stack) to indicate
; that a keystroke is available. The IRET will pop
; these flags into the flags register. The contents of
; the stack, at this point:
; SP+6 -> return flags
; SP+4 -> return CS
; SP+2 -> return IP
; SP -> preserved BX
;
mov bx,sp
xor BYTE PTR ss:[bx+6],ZF
pop bx
iret ; return directly to caller
.elseif ah == 0 || ah == 10h
mov ax,cs:[bx] ; set return ascii and scan codes
add cs:nextKey,2 ; adjust address to next key
.if cs:nextKey > OFFSET lastKey
mov cs:fActive,0
mov cs:nextKey,OFFSET firstKey
.endif
pop bx
iret ; return directly to caller
.endif
.endif
jmp cs:prevIsr16h ; chain to previous handler
Isr16h endp
; -------------------------------------------------------------
; Allocate a new stack.
; -------------------------------------------------------------
even ; specify word alignment
dw 100h dup(0) ; allocate 256-words
theEnd: ; label end of program
; -------------------------------------------------------------
end
[attachment deleted by admin]