home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
pcmag
/
vol5n18.arc
/
VISITYPE.ASM
next >
Wrap
Assembly Source File
|
1987-12-13
|
15KB
|
316 lines
VECTORS SEGMENT AT 0H ;Set up segment to intercept Interrupts
ORG 9H*4 ;The keyboard Interrupt
KEYBOARD_INT LABEL WORD
ORG 1CH*4 ;Timer Interrupt
TIMER_VECTOR LABEL WORD
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_BUFFER ;First time through
COPY_RIGHT DB 'Copyright 1986 Ziff-Davis Publishing Co.'
BUFF DW 0
BUFF2 DW 159 DUP(0)
PAD_OFFSET DW 0 ;Chooses 1st 160 bytes or 2nd
SCREEN_SEG_OFFSET DW 0 ;0 for mono, 8000H for graphics
IO_CHAR DW 1 ;Holds addr of Put or Get_Char
OLD_HEAD DW 1 ;To check for typeahead.
DISPLAY_ON DB 0 ;0 --> Off.
STATUS_PORT DW 0 ;Video controller status port
NEAR_ATTRIB_FLAG DB 0 ;Used in Put_Char
OLD_KEYBOARD_INT_LABEL LABEL DWORD
OLD_KEYBOARD_INT DW 0 ;Location of old kbd interrupt
DW 0
ROM_TIMER_LABEL LABEL DWORD
ROM_TIMER DW 0 ;The Timer interrupt's address
DW 0
BUFSTUFF 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_LABEL
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
JNE CONT
JMP OUT ;So leave
CONT: MOV DX,TAIL ;Read a char -- head advances.
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
NOWRAP: MOV BX,DX
MOV CX,[BX] ;Get key in CX
CMP CX,BUFF ;Is it where we were before?
JNE T10
MOV BX,HEAD ;Has the head moved?
CMP BX,OLD_HEAD
JE T11 ;If yes, we have moved.
T10: CMP BUFF2,0
JNE REMOVE ;If there's something in BUFF2,
T11: CMP DX,HEAD ; remove char in kbd buffer.
JNE REMOVE
JMP OUT ;Do nothing this pass.
;More than one char in buffer -- Remove One!
REMOVE: MOV BX,DX
MOV TAIL,DX ;Remove character by adjusting tail.
MOV DX,[BX] ;Store character in buffer.
MOV CX,80
MOV BX,0 ;Find end of visitype buffer.
CHECK: CMP BUFF2[BX],0
JE BUFEND
ADD BX,2
LOOP CHECK
CMP DX,0E08H ;Was this key a rubout?
JNE OUT ;No, and buffer filled -- leave.
MOV BX,158 ;Yes, buff full but rubout last char.
MOV WORD PTR BUFF[BX],0
MOV BX,HEAD
MOV OLD_HEAD,BX ;Store this for next time.
MOV DX,[BX] ;Always load BUFF.
MOV BUFF,DX
JMP OUT ;Can't hold more than 80!
BUFEND: CMP DX,0E08H ;Rubout (and buffer not full)?
JNE NODEL ;No, don't del.
DEL: SUB BX,2 ;Yes, delete last key.
CMP BX,0FFFEH ;Gone too far?
JL OUT
JNE PADDEL
MOV CX,TAIL ;Del the one char in kdb buffer
MOV HEAD,CX ; by making tail = head.
MOV BUFF,0
JMP SHORT CHKDIS
PADDEL: MOV DX,0 ;DX --> 0 if we are deleting.
NODEL: MOV BUFF2[BX],DX ;Load key in Visitype buffer.
MOV BX,HEAD
MOV OLD_HEAD,BX ;And store the old head to check later.
MOV DX,[BX] ;Always reload BUFF.
MOV BUFF,DX
CHKDIS: CMP DISPLAY_ON,0 ;Are we on?
JNE FLASH ;Yes, call DISPLAY
MOV DISPLAY_ON,0FFH ;Store what's on the screen first.
MOV PAD_OFFSET,160
LEA AX,GET_CHAR ;Make IO use Get-Char so it does.
MOV IO_CHAR,AX
CALL IO ;Get top line from screen.
FLASH: CALL DISPLAY ;Display VISITYPE's top line.
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
BUFSTUFF ENDP
DISPLAY PROC NEAR ;Puts the whole pad on the screen
PUSH AX
MOV NEAR_ATTRIB_FLAG,0
MOV PAD_OFFSET,0 ;Use 1st 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
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 -
IN AL,DX ;Make sure the video controller scan status
TEST AL,1 ;is low
JNZ G_WAIT_LOW
G_WAIT_HIGH: ;After port has gone low, it must go high
IN AL,DX ;before it is safe to read directly from
TEST AL,1 ;the screen buffer in memory
JZ G_WAIT_HIGH
MOV AX,ES:[DI] ;Do the move from the screen, one byte at a time
INC DI ;Move to next screen location
DEC SI ;Decrement loop counter
CMP SI,0 ;Are we done?
JE LEAVE ;Yes
MOV BUFF[BX],AX ;No -- put char we got into BUFF.
JMP G_WAIT_LOW ;Do it again
LEAVE: ADD BX,2
POP DX
RET
GET_CHAR ENDP
PUT_CHAR PROC NEAR ;Puts one char on screen and advances position
PUSH DX
CLI
MOV AH,BYTE PTR BUFF[BX]
MOV SI,2 ;Loop twice, once for char, once for attribute
MOV DX,STATUS_PORT ;Get ready to read video controller status
P_WAIT_LOW: ;Start waiting for a new horizontal scan -
IN AL,DX ;Make sure the video controller scan status
TEST AL,1 ;is low
JNZ P_WAIT_LOW
P_WAIT_HIGH: ;After port has gone low, it must go high
IN AL,DX ;before it is safe to write directly to
TEST AL,1 ;the screen buffer in memory
JZ P_WAIT_HIGH
MOV ES:[DI],AH ;Move to screen, one byte at a time
MOV AH,BYTE PTR BUFF[BX+1]
CMP NEAR_ATTRIB_FLAG,0
JNE INCDI
MOV AH,BYTE PTR BUFF[BX+161]
INCDI: INC DI ;Point to next screen postion
DEC SI ;Decrement loop counter
JNZ P_WAIT_LOW ;If not zero, do it one more time
ADD BX,2
POP DX
STI
RET ;Exeunt
PUT_CHAR ENDP
IO PROC NEAR ;This scans over screen positions on top line.
ASSUME ES:SCREEN ;Use screen as extra segment
MOV BX,SCREEN
MOV ES,BX
MOV DI,SCREEN_SEG_OFFSET ;DI will be pointer to screen postion
MOV BX,PAD_OFFSET ;BX will be location pointer
MOV CX,80
CHAR_LOOP:
CALL IO_CHAR ;Call Put-Char or Get-Char
LOOP CHAR_LOOP ;If not zero, scan over next character
RET ;Finished
IO ENDP
ASSUME DS:CODE_SEG
INTERCEPT_TIMER PROC NEAR ;This completes filling the buffer
;IF NO KEYS IN BUFFER, PUT NEXT ONE IN.
PUSH DS ;Save DS since we'll change it
PUSH CS ;Put current value of CS into DS
POP DS
PUSHF
CALL ROM_TIMER_LABEL ;Make obligatory call
CMP BUFF2,0
JNE GO ;No, leave
JMP OUT1
GO: CLI ;Yes, start by clearing interrupts
PUSH ES
PUSH DS ;Save these.
PUSH SI
PUSH DI
PUSH DX
PUSH CX
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,TAIL ;Prepare to put characters in at tail
CMP HEAD,BX ;If kbd buff not empty, leave.
JNE FINSTUFF
STUFF: MOV AX,WORD PTR BUFF2 ;Get the char to put in kbd buffer.
MOV CX,79 ;Now slide the rest over.
MOV BX,0
SLIDE: MOV DX,BUFF[BX+2] ;Do this word by word.
MOV BUFF[BX],DX
ADD BX,2
INC SI
LOOP SLIDE ;Slides slides BUFF to the left.
MOV WORD PTR BUFF2[BX-2],0
MOV DX,HEAD ;Store this to check if user is typing
MOV OLD_HEAD,DX ; while we drain BUFF.
MOV DX,TAIL ;Find position in buffer from BX
ADD DX,2 ;Move to next position for this word
CMP DX,OFFSET BUFFER_END ;Are we past the end?
JL NO_WRAP ;No, don't wrap
MOV DX,OFFSET BUFFER ;Do the Wrap rap.
NO_WRAP:CMP DX,HEAD ;Buffer full but not yet done?
JE FINSTUFF ;Time to leave, come back later.
MOV BX,TAIL ;Prepare to put characters in at tail
MOV [BX],AX ;Put into buffer
MOV TAIL,DX ;Reset buffer tail
FINSTUFF:CMP BUFF2,0
JNE DIS ;Should we restore the screen?
MOV BUFF,0
MOV DISPLAY_ON,0
MOV PAD_OFFSET,160 ;Use 1st 160 bytes of memory
LEA AX,PUT_CHAR ;Make IO use Put-Char so it does
MOV IO_CHAR,AX
MOV NEAR_ATTRIB_FLAG,0FFH
CALL IO ;Put result on screen
JMP SHORT ODIS
DIS: CALL DISPLAY
ODIS: POP AX ;Restore these.
POP BX
POP CX
POP DX
POP DI
POP SI
POP DS
POP ES
OUT1: POP DS
IRET ;With customary IRET
INTERCEPT_TIMER ENDP
LOAD_BUFFER PROC NEAR ;This procedure intializes everything
ASSUME DS:VECTORS ;The data segment will be the Interrupt area
MOV AX,VECTORS
MOV DS,AX
MOV AX,KEYBOARD_INT ;Get the old interrupt service routine
MOV OLD_KEYBOARD_INT,AX ;address and put it into our location
MOV AX,KEYBOARD_INT[2] ;OLD_KEYBOARD_INT so we can call it.
MOV OLD_KEYBOARD_INT[2],AX
MOV KEYBOARD_INT,OFFSET BUFSTUFF
MOV KEYBOARD_INT[2],CS
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.
ASSUME DS:ROM_BIOS_DATA
MOV AX,ROM_BIOS_DATA
MOV DS,AX
MOV BX,OFFSET BUFFER ;Clear the keyboard buffer to start.
MOV HEAD,BX
MOV OLD_HEAD,BX
MOV TAIL,BX
MOV AH,15 ;Ask for service 15 of INT 10H
INT 10H ;This tells us how display is set up
MOV STATUS_PORT,03BAH ;Assume this is a monochrome display
TEST AL,4 ;Is it?
JNZ EXIT ;Yes - jump out
MOV SCREEN_SEG_OFFSET,8000H ;No - set up for graphics display
MOV STATUS_PORT,03DAH
EXIT: MOV DX,OFFSET LOAD_BUFFER ;Set up everything but LOAD_BUFFER to
INT 27H ;stay and attach itself to DOS
LOAD_BUFFER ENDP
CODE_SEG ENDS
END FIRST ;END "FIRST" so 8088 will go to FIRST first.