VECTORS SEGMENT AT 0H ;Set up segment to intercept Interrupts ORG 9H*4 ;The keyboard Interrupt KEYBOARD_INT_VECTOR LABEL DWORD ORG 1CH*4 ;Timer Interrupt TIMER_VECTOR LABEL DWORD VECTORS ENDS ROM_BIOS_DATA SEGMENT AT 40H ;The ROM BIOS data area in low memory ORG 1AH ;This is where the keyboard buffer is. ROM_BUFFER_HEAD DW ? ;The position of the buffer's head ROM_BUFFER_TAIL DW ? ;And tail. KB_BUFFER DW 16 DUP (?) ;Reserve space for the buffer itself KB_BUFFER_END LABEL WORD ;Buffer's end is stored here. ROM_BIOS_DATA ENDS CODE_SEG SEGMENT ;Begin the Code segment holding the programs ASSUME CS:CODE_SEG ORG 100H ;Com files start at ORG 100H BEGIN: JMP INIT_VECTORS ;Skip over data area COPY_RIGHT DB '(C) 1984 S. Holzner' ;The Author's signature KEYS DW 30 DUP(0) ;The keys we replace FINISHED_FLAG DB 1 ;If not finished, timer will stuff buffer COMMANDS DW 1530 DUP(0) ;Scan and ASCII codes of commands COMMAND_INDEX DW 1 ;Stores position in command (for timer) ROM_KEYBOARD_INT DD 1 ;Called to interpret keyboard signals ROM_TIMER DD 1 ;The Timer interrupt's address INTERCEPT_KEYBOARD_INT PROC NEAR ;Here it is. ASSUME DS:NOTHING ;Free DS PUSH DS ;Save all used registers PUSH SI PUSH DI PUSH DX PUSH CX PUSH BX PUSH AX PUSHF ;Pushf for Keyboard Int's IRET CALL ROM_KEYBOARD_INT ;Have new key put into keyboard buffer ASSUME DS:ROM_BIOS_DATA ;Set up to point at keyboard buffer. MOV AX,ROM_BIOS_DATA MOV DS,AX MOV BX,ROM_BUFFER_TAIL ;Was there a character? If Tail equals CMP BX,ROM_BUFFER_HEAD ; Head then no real character typed. JNE NEWCHAR JMP NO_NEW_CHARACTERS ;Jump out, no new characters. NEWCHAR:SUB BX,2 ;Move back two bytes from tail; CMP BX,OFFSET KB_BUFFER ;Do we have to wrap? JAE NO_WRAP ;No MOV BX,OFFSET KB_BUFFER_END ;Wrap by moving two bytes SUB BX,2 ; before buffer end. NO_WRAP:MOV AX,[BX] ;Get the character into AX CMP FINISHED_FLAG,1 ;Done stuffing the buffer with last command? JE FIN ;Yes, proceed JMP NO_NEW_CHARACTERS ;No, leave. FIN: MOV FINISHED_FLAG,1 ;Assume we'll finish LEA SI,KEYS ;Point source index at keys to replace MOV CX,30 ;Loop over all of them LOOPER: CMP AX,CS:[SI] ;Match to given key (in AX)? JE FOUND ;Yes, key found, continue on. ADD SI,2 ;Point to next key to check it. LOOP LOOPER ;Go back for next one. JMP NO_NEW_CHARACTERS ;Loop finished without match - leave. FOUND: CLI ;Turn off hardware (timer, keyboard) Interrupts LEA SI,COMMANDS ;Set up to read command NEG CX ;Find the location of first word of command ADD CX,30 MOV AX,CX MOV CX,102 MUL CL ADD SI,AX MOV COMMAND_INDEX,SI ;And move it into Command_Index STUFF: MOV AX,CS:[SI] ;Here we go - get ready to stuff word in buffer. ADD SI,2 ;Point to the command's next character CMP AX,0 ;Is it a zero? (End of command) JE NO_NEW_CHARACTERS ;Yes, leave with Finished_Flag=1 MOV DX,BX ;Find position in buffer from BX ADD DX,2 ;Move to next position for this word CMP DX,OFFSET KB_BUFFER_END ;Are we past the end? JL NO_WRAP2 ;No, don't wrap MOV DX,OFFSET KB_BUFFER ;Wrap NO_WRAP2: CMP DX,ROM_BUFFER_HEAD ;Buffer full but not yet done? JE BUFFER_FULL ;Time to leave, set Finished_Flag=0. ADD COMMAND_INDEX,2 ;Move to next word in command MOV [BX],AX ;Put it into the buffer right here. ADD BX,2 ;Point to next space in buffer CMP BX,OFFSET KB_BUFFER_END ;Wrap here? JL NO_WRAP3 ;No, readjust buffer tail MOV BX,OFFSET KB_BUFFER ;Yes, wrap NO_WRAP3: MOV ROM_BUFFER_TAIL,BX ;Reset buffer tail JMP STUFF ;Back to stuff in another character. BUFFER_FULL: ;If buffer is full, let timer take over MOV FINISHED_FLAG,0 ; by setting Finished_Flag to 0. NO_NEW_CHARACTERS: POP AX ;Restore everything before departure. POP BX POP CX POP DX POP DI POP SI POP DS STI IRET ;An interrupt deserves an IRET INTERCEPT_KEYBOARD_INT ENDP ASSUME DS:CODE_SEG INTERCEPT_TIMER PROC NEAR ;This completes filling the buffer PUSHF ;Store used flags PUSH DS ;Save DS since we'll change it PUSH CS ;Put current value of CS into DS POP DS CALL ROM_TIMER ;Make obligatory call PUSHF CMP FINISHED_FLAG,1 ;Do we have to do anything? JE OUT ;No, leave CLI ;Yes, start by clearing interrupts PUSH DS ;Save these. PUSH SI PUSH DX PUSH BX PUSH AX ASSUME DS:ROM_BIOS_DATA ;Point to the keyboard buffer again. MOV AX,ROM_BIOS_DATA MOV DS,AX MOV BX,ROM_BUFFER_TAIL ;Prepare to put charaters in at tail MOV FINISHED_FLAG,1 ;Assume we'll finish MOV SI,COMMAND_INDEX ;Find where we left ourselves STUFF2: MOV AX,CS:[SI] ;The same stuff loop as above. ADD SI,2 ;Point to next command character. CMP AX,0 ;Is it zero? (end of command) JNE OVER ;No, continue. JMP NO_NEW_CHARACTERS2 ;Yes, leave with Finished_Flag=1 OVER: MOV DX,BX ;Find position in buffer from BX ADD DX,2 ;Move to next position for this word CMP DX,OFFSET KB_BUFFER_END ;Are we past the end? JL NO_WRAP4 ;No, don't wrap MOV DX,OFFSET KB_BUFFER ;Do the Wrap rap. NO_WRAP4: CMP DX,ROM_BUFFER_HEAD ;Buffer full but not yet done? JE BUFFER_FULL2 ;Time to leave, come back later. ADD COMMAND_INDEX,2 ;Point to next word of command. MOV [BX],AX ;Put into buffer ADD BX,2 ;Point to next space in buffer CMP BX,OFFSET KB_BUFFER_END ;Wrap here? JL NO_WRAP5 ;No, readjust buffer tail MOV BX,OFFSET KB_BUFFER ;Yes, wrap NO_WRAP5: MOV ROM_BUFFER_TAIL,BX ;Reset buffer tail JMP STUFF2 ;Back to stuff in another character BUFFER_FULL2: MOV FINISHED_FLAG,0 ;Set flag to not-done-yet. NO_NEW_CHARACTERS2: POP AX ;Restore these. POP BX POP DX POP SI POP DS OUT: POPF ;And Exit. POP DS IRET ;With customary IRET INTERCEPT_TIMER ENDP INIT_VECTORS PROC NEAR ;Rest Interrupt vectors here ASSUME DS:VECTORS PUSH DS MOV AX,VECTORS MOV DS,AX CLI ;Don't allow interrupts MOV AX,KEYBOARD_INT_VECTOR ;Get and store old interrupt address MOV ROM_KEYBOARD_INT,AX MOV AX,KEYBOARD_INT_VECTOR[2] MOV ROM_KEYBOARD_INT[2],AX MOV KEYBOARD_INT_VECTOR,OFFSET INTERCEPT_KEYBOARD_INT MOV KEYBOARD_INT_VECTOR[2],CS ;And put ours in place. MOV AX,TIMER_VECTOR ;Now same for timer MOV ROM_TIMER,AX MOV AX,TIMER_VECTOR[2] MOV ROM_TIMER[2],AX MOV TIMER_VECTOR,OFFSET INTERCEPT_TIMER MOV TIMER_VECTOR[2],CS ;And intercept that too. STI ASSUME DS:ROM_BIOS_DATA MOV AX,ROM_BIOS_DATA MOV DS,AX MOV BX,OFFSET KB_BUFFER ;Clear the keyboard buffer. MOV ROM_BUFFER_HEAD,BX MOV ROM_BUFFER_TAIL,BX MOV DX,OFFSET INIT_VECTORS ;Prepare to attach in memory INT 27H ;And do so. INIT_VECTORS ENDP CODE_SEG ENDS END BEGIN ;End Begin so that we jump there first.