📄 keeper.asm
字号:
VECTORS SEGMENT AT 0H ;Set up segment to intercept Interrupts
ORG 9H*4 ;The keyboard Interrupt
KEYBOARD_INT LABEL DWORD
ORG 1CH*4 ;Timer Interrupt
TIMER_VECTOR LABEL DWORD
VECTORS ENDS
SCREEN SEGMENT AT 0B000H ;A dummy segment to use as the
SCREEN ENDS ;Extra Segment
ROM_BIOS_DATA SEGMENT AT 40H ;BIOS statuses held here, also keyboard buffer
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_DATA ENDS
CODE_SEG SEGMENT
ASSUME CS:CODE_SEG
ORG 100H ;ORG = 100H to make this into a .COM file
FIRST: JMP LOAD_KEEPER ;First time through
COPY_RIGHT DB '(C)1985 S.HOLZNER' ;Ascii autograph
PAD DB 20*102 DUP(0) ;Memory storage for pad
PAD_CURSOR DW 9*102 ;Current position in pad
ATTRIBUTE DB 112 ;Pad Attribute -- reverse video
LINE_ATTRIBUTE DB 240 ;Flashing Rev video
OLD_ATTRIBUTE DB 7 ;Original screen attrib: normal
PAD_OFFSET DW 0 ;Chooses 1st 250 bytes or 2nd
FIRST_POSITION DW ? ;Position of 1st char on screen
TRIGGER_FLAG DW 0 ;Trigger on or off
FULL_FLAG DB 0 ;Buffer Full Flag
LINE DW 9 ;Line number, 0-9
SCREEN_SEG_OFFSET DW 0 ;0 for mono, 8000H for graphics
IO_CHAR DW ? ;Holds addr of Put or Get_Char
STATUS_PORT DW ? ;Video controller status port
OLD_KEYBOARD_INT DD ? ;Location of old kbd interrupt
FINISHED_FLAG DB 1 ;If not finished,f buffer
COMMAND_INDEX DW 1 ;Stores positior timer)
ROM_TIMER DD 1 ;The Timer interrupt's address
OLD_HEAD DW 0
KEEPER PROC NEAR ;The keyboard interrupt will now come here.
ASSUME CS:CODE_SEG
PUSH AX ;Save the used registers for good form
PUSH BX
PUSH CX
PUSH DX
PUSH DI
PUSH SI
PUSH DS
PUSH ES
PUSHF ;First, call old keyboard interrupt
CALL OLD_KEYBOARD_INT
ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in
MOV BX,ROM_BIOS_DATA
MOV DS,BX
MOV BX,TAIL ;Point to current tail
CMP BX,HEAD ;If at head, kbd int has deleted char
JE BYE ;So leave
MOV DX,HEAD
SUB DX,2 ;Point to just read in character
CMP DX,OFFSET BUFFER ;Did we undershoot buffer?
JAE NOWRAP ;Nope
MOV DX,OFFSET BUFFER_END ;Yes -- move to buffer top
SUB DX,2 ;Compare two bytes back from head
NOWRAP: CMP DX,TAIL ;If it's the tail, buffer is full
JNE NOTFULL ;We're OK, jump to NotFull
CMP FULL_FLAG,1 ;Check if keyboard buffer full
JE BYE ;Yep, leave
MOV FULL_FLAG,1 ;Oops, full, set flag and take
JMP CHK ; this last character
NOTFULL:MOV FULL_FLAG,0 ;Always reset Full_Flag when buff clears
CHK: CMP TRIGGER_FLAG,0 ;Is the window on (triggered?)
JNE SUBT ;Yep, keep going
MOV DX,OLD_HEAD ;Check position of buffer head
CMP DX,HEAD
JNE CONT
MOV OLD_HEAD,0
BYE: JMP OUT
CONT: MOV DX,HEAD
MOV OLD_HEAD,DX
SUBT: SUB BX,2 ;Point to just read in character
CMP BX,OFFSET BUFFER ;Did we undershoot buffer?
JAE NO_WRAP ;Nope
MOV BX,OFFSET BUFFER_END ;Yes -- move to buffer top
SUB BX,2 ;
NO_WRAP:MOV DX,[BX] ;Char in DX now
;------ CHAR IN DX NOW -------
CMP FINISHED_FLAG,0
JE IN
CMP DX,310EH ;Default trigger is a ^N here.
JNE NOT_TRIGGER ;No
MOV TAIL,BX
NOT TRIGGER_FLAG ;Switch Modes
CMP TRIGGER_FLAG,0 ;Trigger off?
JNE TRIGGER_ON ;No, only other choice is on
TRIGGER_OFF:
MOV OLD_HEAD,0 ;Reset old head
MOV AH,OLD_ATTRIBUTE ;Get ready to restore screen
MOV ATTRIBUTE,AH ;Pad and blinking line set to orig.
MOV LINE_ATTRIBUTE,AH ; values
MOV PAD_OFFSET,10*102 ;Point to 2nd half of pad
LEA AX,PUT_CHAR ;Make IO call Put_Char as it scans
MOV IO_CHAR,AX ;over all locations in pad on screen
CALL IO ;Restore screen
CMP LINE,9 ;Was the window turned off without
JE IN ; using up-down keys? If so, exit
MOV AX,LINE ;No, there is a line to stuff in
MOV CL,102 ; keyboard buffer
MUL CL ;Find its location in Pad
MOV COMMAND_INDEX,AX ;And send to Put
CALL PUT ;Which will do actual stuffing
IN: JMP OUT ;Done
TRIGGER_ON: ;Window just turned on
MOV LINE,9 ;Set blinking line to bottom
MOV PAD_OFFSET,10*102 ;Point to screen storage part of pad
LEA AX,GET_CHAR ;Make IO use Get_char so current screen
MOV IO_CHAR,AX ;is stored
CALL IO ;Store Screen
CALL DISPLAY ;And put up the pad
JMP OUT ;Done here.
NOT_TRIGGER:
TEST TRIGGER_FLAG,1 ;Is Trigger on?
JZ RUBOUT_TEST
MOV TAIL,BX ;Yes, delete this char from buffer
UP: CMP DX,4800H ;An Up cursor key?
JNE DOWN ;No, try Down
DEC LINE ;Move blinker up one line
CMP LINE,0 ;At top? If so, reset
JGE NOT_TOP
MOV LINE,9
NOT_TOP:CALL DISPLAY ;Display result
JMP OUT ;And leave
DOWN: CMP DX,5000H ;Perhaps Down cusor key pushed
JNE IN ;If not, ignore key
INC LINE ;If so, move down one
CMP LINE,9 ;If at bottom, wrap to top
JLE NOT_BOT
MOV LINE,0
NOT_BOT:CALL DISPLAY ;Show results
JMP OUT ;And exit
RUBOUT_TEST:
CMP DX,0E08H ;Is it a Rubout?
JNE CHAR_TEST ;No -- try carriage return-line feed
MOV BX,PAD_CURSOR ;Yes -- get current pad location
CMP BX,9*102 ;Are we at beginning of last line?
JLE NEVER_MIND ;Yes -- can't rubout past beginning
SUB PAD_CURSOR,2 ;No, rubout this char
MOV PAD[BX-2],20H ;Move a space in instead (3920H)
MOV PAD[BX-1],39H
NEVER_MIND:
JMP OUT ;Done here.
CHAR_TEST:
CMP DL,13 ;Is this a carriage return?
JE PLUG ;If yes, plug this line into Pad
CMP DL,32 ;If this char < Ascii 32, delete line
JGE PLUG
MOV PAD_CURSOR,9*102 ;Clear the current line
MOV CX,51
MOV BX,9*102
CLEAR: MOV WORD PTR PAD[BX],0
ADD BX,2
LOOP CLEAR
JMP OUT ;And exit
PLUG: MOV BX,PAD_CURSOR ;Get current pad location
CMP BX,10*102-2 ;Are we past the end of the pad?
JGE CRLF_TEST ;Yes -- throw away char
MOV WORD PTR PAD[BX],DX ;No -- move ASCII code into pad
ADD PAD_CURSOR,2 ;Increment pad location
CRLF_TEST:
CMP DX,1C0DH ;Is it a carriage return-line feed?
JNE OUT ;No -- put it in the pad
CALL CRLF ;Yes -- move everything up in pad
OUT: POP ES ;Having done Pushes, here are the Pops
POP DS
POP SI
POP DI
POP DX
POP CX
POP BX
POP AX
IRET ;An interrupt needs an IRET
KEEPER ENDP
DISPLAY PROC NEAR ;Puts the whole pad on the screen
PUSH AX
MOV ATTRIBUTE,112 ;Use reverse video
MOV LINE_ATTRIBUTE,240
MOV PAD_OFFSET,0 ;Use 1st 250 bytes of pad memory
LEA AX,PUT_CHAR ;Make IO use Put-Char so it does
MOV IO_CHAR,AX
CALL IO ;Put result on screen
POP AX
RET ;Leave
DISPLAY ENDP
CRLF PROC NEAR ;This handles carriage returns
PUSH BX ;Push everything conceivable
PUSH CX
PUSH DI
PUSH SI
PUSH DS
PUSH ES
ASSUME DS:CODE_SEG ;Set DS to Code_Seg here
PUSH CS
POP DS
ASSUME ES:CODE_SEG ;And ES too
PUSH DS
POP ES
LEA DI,PAD ;Get ready to move contents of Pad
MOV SI,DI ; up one line
ADD SI,102 ;DI-top line, SI-one below top line
MOV CX,9*51
MOV BX,PAD_CURSOR ;But first finish line with a 0
CMP BX,9*102+2 ; as a flag letting Put know line is
JE POPS ; done.
MOV WORD PTR PAD[BX],0
REP MOVSW ;Move up Pad contents
MOV CX,51 ;Now fill the last line with spaces
MOV AX,3920H
REP STOSW ;Using Stosw
POPS: MOV PAD_CURSOR,9*102 ;And finally reset Cursor to beginning
POP ES ; of the last line again.
POP DS
POP SI
POP DI
POP CX
POP BX
DONE: RET ;And out.
CRLF ENDP
GET_CHAR PROC NEAR ;Gets a char from screen and advances position
ASSUME ES:SCREEN,DS:ROM_BIOS_DATA
PUSH DX
MOV SI,2 ;Loop twice, once for char, once for attribute
MOV DX,STATUS_PORT ;Get ready to read video controller status
G_WAIT_LOW: ;Start waiting for a new horizontal scan -
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -