home *** CD-ROM | disk | FTP | other *** search
- %title "NNANSI Terminal Driver Version 5/91"
- %pagesize 60, 132
- %noconds
- JUMPS ; clarify branches -- TASM V2.0 recommended
- ;--- nnansi.asm ----------------------------------------------------------
- ; New, New ANSI terminal driver.
- ; Optimized for speed in the case of multi-character write requests.
- ; Original NANSI terminal driver (C) 1986 Daniel Kegel, Pasadena, CA
- ; Modifications by Tom Almy without restrictions.
-
- ; May be distributed for educational and personal use only
-
- ; See file NNANSI.DOC for licensing information.
-
- ; Daniel Kegel, Bellevue, Washington & Pasadena, California
- ; Revision history:
- ; 5 july 85: brought up non-ANSI portion except forgot backspace
- ; 6 july 85: split off ANSI stuff into other files, added backspace
- ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
- ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
- ; 9 aug 85: added cursor position reporting
- ; 10 aug 85: added output character translation
- ; 11 aug 85: added keyboard redefinition, some EGA 80x43 support
- ; 19 Aug 85: Fixed horrible bug in insert/delete line.
- ; 26 Aug 85: Fixed simple limit-to-one-too-few-lines bug in ins/del line;
- ; anyway, it inserts 24 lines when on line 2 now. Whether it's fixed...
- ; 4 Sept 85: Fixed bug created on 26 Aug 85; when limiting ins/del line
- ; count, we are clearing, not scrolling; fixed BIOS call to reflect this.
- ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
- ; 30 Jan 86: removed Tandy 2000 stuff, added graphics mode support
- ; 12 feb 86: added int 29h handler, added PUSHA/POPA, added direct beep,
- ; direct cursor positioning, takeover of BIOS write_tty,
- ; noticed & squashed 2 related bugs in tab expansion
- ; 30 Jan 86: Added EGA cursor patch
- ; 31 Jan 86: Disabled insert/delete char in graphics modes
- ; Implemented keyboard redefinition reset
- ; 1 Feb 86: added video_mode and max_x test after mode set
- ; 13 feb 86: Squashed them again, harder
- ; 24 feb 86: There is a bug in the timing code used by the BEEP routine.
- ; If the addition of the beep period to the
- ; BIOS low timer word results in an overflow, the beep will be
- ; supressed. Also made code compatible eith earlier versions
- ; of assembler.
-
- ; Tom Almy, Tualatin, Oregon (toma@sail.labs.tek.com) modified the NANSI
- ; version ; 2.2 code for use in EGA/VGA environments only:
- ; 8 Jan 89: Additional compilation options
- ; Scrolling via reprogramming display start (*MUCH* faster)
- ; INT29 updates display directly if not control character.
- ; Various cleanups
- ; Nov 89: Some bug fixes, customization for various cards enhanced
- ; display modes, better handling of graphic cursor, graphic
- ; characters in 16 color modes are drawn by NNANSI rather
- ; than BIOS (much faster).
- ; Sept 90: Color backgrounds and XOR mode (as BLINK) in 16 color graphic
- ; modes. VGA has 43 or 50 line modes (instead of producing 50
- ; (when 43 selected). FAST mode can be toggled via escape
- ; sequences. Lots of code clean up. Some old options incorporated
- ; (extra ANSI sequences) or deleted (output conversion).
- ; The fast graphic draw routine has been speeded up, since it
- ; was slower than some BIOSes (!). The BIOS TTY call has been
- ; redirected to the INT29 routine which has some efficiency
- ; speedups; this also saved code space.
- ; May 1991: Restored CGA and MDA support. MDA is optional. Added
- ; DOS 4.x compatibility. Added 40x43 and 40x50 text mode support.
- ; Added VESA support. Fixed bug allowing either = or ? or nothing
- ; in all set mode commands. Added enable/disable for bios
- ; write_tty takeover. Added drivers for new cards and changed
- ; some defaults. Merged source code into one file.
- ; Card id printout idea by Arend van den Brug.
-
- page
- TRUE equ 1
- FALSE equ 0
-
- INCLUDE CONFIG.INC
- page
- ; Option dependent definitions
- if VGA
- if EGA
- card_id macro
- db 'Generic EGA/VGA'
- endm
- else
- card_id macro
- db 'Generic VGA'
- endm
- endif
- else
- if EGA
- card_id macro
- db 'Generic EGA'
- endm
- else
- card_id macro
- db 'Generic NANSI Clone'
- endm
- endif
- endif
-
- IF key_redef
- buf_size equ init_buf_size ; size of parameter/redef buffer
- ELSE
- buf_size equ 20 ; size of parameter/redef buffer
- ENDIF
-
-
- IF cheap_pc
- .8086
- ELSE
- .286c
- ENDIF
-
- page
- INCLUDE DRIVERS.INC
- page
- ; Some Useful Macros
-
- IF cheap_pc
- push_all macro
- push ax
- push bx
- push cx
- push dx
- push bp
- push si
- push di
- endm
- ELSE
- push_all macro
- pusha
- endm
- ENDIF
-
- IF cheap_pc
- pop_all macro
- pop di
- pop si
- pop bp
- pop dx
- pop cx
- pop bx
- pop ax
- endm
- ELSE
- pop_all macro
- popa
- endm
- ENDIF
-
- call_video macro ; call original video interrupt
- pushf ; push flags
- call dword ptr old_vid_bios
- endm
-
- draw_gcursor macro ; draw graphic cursor
- if quick_char
- mov ax, 8f16h
- call quick_graph
- else
- mov ax, 0916h ; draw cursor at location
- mov bx, 8fh
- mov cx, 1
- call_video
- endif
- endm
- page
-
- keybuf struc ; Used in getchar
- len dw ?
- adr dw ?
- keybuf ends
-
-
- ABS40 segment at 40h
- org 1ah
- buffer_head dw ? ; Used in 'flush input buffer' dos call.
- buffer_tail dw ?
-
- org 49h
- crt_mode db ?
- crt_cols dw ?
- crt_len dw ?
- crt_start dw ?
- cursor_posn dw 8 dup (?)
- cursor_mode dw ?
- active_page db ?
- addr_6845 dw ?
- crt_mode_set db ? ; = 7 only if monochrome display adaptor
- crt_palette db ?
- org 6ch
- timer_low dw ? ; low word of time-of-day counter (18.2 hz)
- org 84h
- ega_rows db ? ; #rows-1 on display
- ega_points dw ? ; bytes per character
-
- ABS40 ends
-
- page
-
- CODE segment word public 'CODE'
- assume cs:CODE, ds:CODE
-
- ; Device Driver Header
-
- org 0
-
- dd -1 ; next device
- if dos4
- dw 08053h ; attributes
- else
- dw 8013h ; attributes
- endif
- dw strategy ; request header pointer entry
- dw interrupt ; request entry point
- db 'CON' ; device name (8 char)
- db 5 dup (20h) ; ... and 5 blanks)
-
- ; Identification- in case somebody TYPEs the assembled driver
- db 27,'[2J'
- db "NNANSI.SYS 05/91 for "
- card_id
- ife cheap_pc
- db "(80286)"
- else
- db "(80x86)"
- endif
- db 13, 10
- db 'by Tom Almy based on code (C) Daniel Kegel, Pasadena, CA 1986.'
- db 13, 10, 26
-
- even
- ;----- variable area --------------------
- org $-38 ; overlay id string with uninitialized data
- if DOS4
- org $-4
- endif
- if key_redef
- org $-2
- endif
- if MONO
- org $-2
- endif
- req_ptr label dword
- req_off dw ?
- req_seg dw ?
- f_cptr_seg dw ? ; part of fastout write buffer pointer
- cur_parm_ptr dw ? ; last byte of parm area now used
- old_vid_bios dd ? ; pointer to old video bios routine
- saved_coords dw ? ; holds XY after a SCP escape sequence
- temp_val dw ? ; just a temporary
- param_buffer dw ? ; address of first byte free for new params
- param_end dw ? ; address of end of free area
- if key_redef
- redef_end dw ? ; address of end of redefinition area
- endif
- if dos4
- old_mpx dd ? ; original mpx interrupt handler
- endif
- if MONO
- our_6845 dw ?
- endif
- no_c_flag db ? ; there is no graphic cursor on the screen.
- max_y db ? ; lines-1
- max_cur_x label word ; used to get both max & cur at once
- max_x db ? ; line width (79 for 80x25 modes)
- cur_coords label word
- cur_x db ? ; cursor position (0 = left edge)
- cur_y db ? ; (0 = top edge)
- video_mode db ? ; ROM BIOS video mode (2=BW, 3=color, etc)
- string_term db ? ; either escape or double quote
- in_num db ? ; true if between a digit and a semi in parse
- int_29_buf db ? ; character buffer for int 29 calls
- fnkeybuf db ? ; holds second byte of fn key codes
- eat_key db ? ; Eaten key (bug fix)
- cpr_buf db 8 dup (?), '['
- cpr_esc db 1bh ; descending buffer for cpr function
-
- even ; this should be redundant, if I did it right
-
- ; following four keybufs hold information about input
- ; Storage order determines priority- since the characters making up a function
- ; key code must never be separated (say, by a Control-Break), they have the
- ; highest priority, and so on. Keyboard keys (except ctrl-break) have the
- ; lowest priority.
-
- fnkey keybuf <0, fnkeybuf> ; fn key string (0 followed by scan code)
- cprseq keybuf <0> ; CPR string (ESC [ y;x R)
- brkkey keybuf <0, brkkeybuf> ; ^C
- if key_redef
- xlatseq keybuf <0> ; keyboard reassignment string
- endif
-
- escvector dw 0 ; state vector of ESCape sequencor
- brkkeybuf db 3 ; control C
- wrap_flag db 1 ; 0 = no wrap past line end
- cur_attrib db 7 ; current char attributes
- gmode_flag db 0 ; true if in graphics mode
- ; <0 for mono card
- gcursor db initgc ; true if graphic cursor enabled
- fmode db initfast ; in fast mode?
- bmode db initbiosw ; ANSI write_tty BIOS command?
- atr_flag db 0 ; 80h - blink, 8 - bright
- ; 4 - invisible 2 - reverse 1 underline
- color_flag db 7h ; fg and bg colors
-
- port_6845 equ 3d4h
- page
- ;------ xy_to_regs --------------------------------------------
- ; on entry: x in cur_x, y in cur_y
- ; on exit: dx = chars left on line, di = address
- ; Alters ax, bx.
- xy_to_regs proc near
- ; Find number of chars 'till end of line, keep in DX
- mov ax, max_cur_x
- mov bx, ax ; save max_x & cur_x for next block
- xor ah, ah ; ax = max_x
- xchg dx, ax
- mov al, bh
- xor ah, ah ; ax = cur_x
- sub dx, ax
- inc dx ; dx is # of chars till EOL
- ; Calculate DI = current address in text buffer
- mov al, bl ; al = max_x
- inc al
- mul cur_y
- add al, bh ; al += cur_x
- adc ah, 0 ; AX is # of chars into buffer
- add ax, ax
- xchg di, ax ; DI is now offset of cursor.
-
- push ds
- mov ax, ABS40
- mov ds, ax
- assume ds:ABS40
- add di, crt_start ; crt offset
- ; the offset could be non-zero because
- ; of video pages or fast scrolling.
- pop ds
- assume ds:nothing
- ret
- xy_to_regs endp
-
- page
- ;------- dos_fn_tab -------------
- ; This table is used in "interrupt" to call the routine that handles
- ; the requested function.
-
- if dos4
- max_cmd equ 24
- dos_fn_tab:
- dw dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
- dw dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
- dw badcmd, badcmd, badcmd, nopcmd, badcmd ; 12-16
- dw badcmd, badcmd ; 17-18 unassigned
- dw dosfn19
- dw badcmd, badcmd, badcmd ; 20-22 unassigned
- dw nopcmd, nopcmd
- else
- max_cmd equ 12
- dos_fn_tab:
- dw dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
- dw dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
- endif
- ;------- strategy ----------------------------------------------------
- ; DOS calls strategy with a request which is to be executed later.
- ; Strategy just saves the request.
-
- strategy proc far
- mov cs:req_off,BX
- mov cs:req_seg,ES
- ret
- strategy endp
-
- ;------ interrupt -----------------------------------------------------
- ; This is where the request handed us during "strategy" is
- ; actually carried out.
- ; Calls one of 12 subroutines depending on the function requested.
- ; Each subroutine returns with exit status in AX.
-
- interrupt proc far
-
- sti
- push_all ; preserve caller's registers
- push ds
- push es
-
-
- ; Read requested function information into registers
- lds bx,cs:req_ptr
- xor ah,ah ; clear upper part of ax
- mov al,ds:[BX+02h] ; al = function code
- ;
- ; The next instruction blows up MASM 1.0 but who cares!!
- ;
- les si,[BX+0Eh] ; ES:SI = input/output buffer addr
- mov cx,[BX+12h] ; cx = input/output byte count
-
- cmp al, max_cmd
- ja unk_command ; too big, exit with error code
-
- xchg bx, ax
- shl bx, 1 ; form index to table of words
- mov ax, cs
- mov ds, ax
- call word ptr dos_fn_tab[bx]
- int_done:
- lds bx,cs:req_ptr ; report status
- or ax, 100h ; (always set done bit upon exit)
- mov [bx+03],ax
-
- pop ES ; restore caller's registers
- pop DS
- pop_all
- ret ; return to DOS.
-
- unk_command:
- call badcmd
- jmp int_done
-
- interrupt endp
- page
- ;----- BIOS break handler -----------------------------------------
- ; Called by BIOS when Control-Break is hit (vector was set up in Init).
- ; Simply notes that a break was hit. Flag is checked during input calls.
-
- break_handler proc
- mov cs:brkkey.len, 1
- iret
- break_handler endp
-
- page
-
- ;------ badcmd -------------------------------------------------------
- ; Invalid function request by DOS.
- badcmd proc near
- mov ax, 813h ; return "Error: invalid cmd"
- ret
- badcmd endp
-
-
- ;------ nopcmd -------------------------------------------------------
- ; Unimplemented or dummy function request by DOS.
- nopcmd proc near
- xor ax, ax ; No error, not busy.
- ret
- nopcmd endp
- page
- ;------- dos function #4 ----------------------------------------
- ; Reads CX characters from the keyboard, places them in buffer at
- ; ES:SI.
- dosfn4 proc near
- jcxz dos4done
- mov di, si
- dos4lp: push cx
- call getchar
- pop cx
- stosb
- loop dos4lp
- dos4done:
- xor ax, ax ; No error, not busy.
- ret
- dosfn4 endp
- page
- ;-------- dos function #5: non-destructive input, no wait ------
- ; One-character lookahead into the keyboard buffer.
- ; If no characters in buffer, return BUSY; otherwise, get value of first
- ; character of buffer, stuff into request header, return DONE.
- dosfn5 proc near
- call peekchar
- jz dos5_busy
-
- lds bx,req_ptr
- mov [bx+0Dh], al
- xor ax, ax ; No error, not busy.
- jmp short dos5_exit
- dos5_busy:
- MOV ax, 200h ; No error, busy.
- dos5_exit:
- ret
-
- dosfn5 endp
- page
- ;-------- dos function #6: input status --------------------------
- ; Returns "busy" if no characters waiting to be read.
- dosfn6 proc near
- call peekchar
- mov ax, 200h ; No error, busy.
- jz dos6_exit
- xor ax, ax ; No error, not busy.
- dos6_exit:
- ret
- dosfn6 endp
- page
- ;-------- dos function #7: flush input buffer --------------------
- ; Clears the IBM keyboard input buffer. Since it is a circular
- ; queue, we can do this without knowing the beginning and end
- ; of the buffer; all we need to do is set the tail of the queue
- ; equal to the head (as if we had read the entire queue contents).
- ; Also resets all the device driver's stuffahead buffers.
- dosfn7 proc near
- xor ax, ax
- mov fnkey.len, ax ; Reset the stuffahead buffers.
- mov cprseq.len, ax
- mov brkkey.len, ax
- if key_redef
- mov xlatseq.len, ax
- endif
-
- mov ax, ABS40
- mov es, ax
- mov ax, es:buffer_head ; clear queue by making the tail
- mov es:buffer_tail, ax ; equal to the head
-
- xor ax, ax ; no error, not busy.
- ret
- dosfn7 endp
- page
- IF dos4
- ;------- dos function #19: Generic IOCTL
- ; DOS4 way of setting display size using MODE command.
- ; We will do more than necessary.
- ;
- dosfn19 proc near
- les bx, req_ptr ; get request block
- mov ax, es:[BX+13] ; get function code
- cmp ax, 5f03h ; is it set information?
- jz setinfo
- cmp ax, 7f03h ; is it get information?
- jz getinfo
- mov ax, 8003h ; return bad command
- ret
- getinfo:
- les bx, es:[bx+19] ; data packet to set
- push ds
- mov ax, ABS40 ; address ABS40 area
- mov ds, ax
- assume ds:ABS40
- mov word ptr es:[bx+0], 0 ; level and reserved fields
- mov word ptr es:[bx+2], 14 ; length field
- mov word ptr es:[bx+4], 0 ; flags
- mov al, crt_mode
- call set_gmode
- mov ax, 1
- cmp cs:gmode_flag, 0 ; see if graphics mode
- jz fn19txt
- inc ax
- if MONO
- cmp cs:gmode_flag, -7 ; check for mono mode
- jne fn19txt
- dec ax
- mov word ptr es:[bx+8], 0 ; monochrome
- jmp short fn19mono
- endif
- fn19txt:
- mov word ptr es:[bx+8], 4 ; say 16 colors
- fn19mono:
- mov word ptr es:[bx+6], ax ; store mode
- mov ax, crt_cols
- mov word ptr es:[bx+0eh], ax ; character columns
- mov dx, 8 ; all characters are 8 pixels wide
- mul dx
- mov word ptr es:[bx+0ah], ax ; pixel columns
- xor ax, ax
- mov al, ega_rows
- inc ax
- mov word ptr es:[bx+10h], ax ; character rows
- mov dx, ega_points
- mul dx
- mov word ptr es:[bx+0ch], ax ; pixel rows
- pop ds
- assume ds:CODE
- xor ax, ax
- ret
- fn19fail:
- mov ax, 8003h ; unknown mode, failure
- ret
- setinfo:
- les bx, es:[bx+19] ; data packet to read
- mov dx, 80h ; monochrome, don't clear screen
- cmp word ptr es:[bx+8], 0 ; set to monochrome?
- je setismono
- inc dx
- setismono:
- mov ax, es:[bx+0eh] ; character columns
- cmp ax, 40 ; 40 columns?
- je fn1940col
- cmp ax, 80
- jne fn19fail
- add dx, 2 ; mode 2 or 3
- fn1940col:
- mov ax, es:[bx+10h] ; character rows
- cmp ax, 25
- je fn1925lin
- IF VGA
- cmp ax, 50
- je fn1950lin
- ENDIF
- cmp ax, 43
- jne fn19fail
-
- IF VGA
- mov ax, 1201h ; 350 lines
- jmp short ln50join
- fn1950lin:
- mov ax, 1202h ; 400 lines
- ln50join:
- push dx
- mov bl, 30h ; set scan lines
- int 10h
- pop ax ; the mode
- ELSE
- mov ax, dx
- ENDIF
- int 10h ; set video mode
-
- mov ax, 1112h ; set 8x8 font
- mov bl, 0
- int 10h
-
- mov ax, 1200h ; load new printscreen
- mov bl, 20h
- int 10h
- IF EGA ; set the EGA cursor
- mov dx, port_6845 ; '6845' command reg
- mov ax, 070ah ; start on line 7
- out dx, ax
- mov al, 0bh ; end on line 7
- out dx, ax
- ENDIF
- jmp short clrs
-
- fn1925lin:
- IF VGA
- push dx
- mov ax, 1202h ; select 400 scan lines
- mov bl,30h ; this call ignored on EGA
- int 10h
- pop ax
- ELSE
- mov ax, dx
- ENDIF
- int 10h ; set video mode
-
- clrs: push ds ; address abs40 area
- mov ax, ABS40
- mov ds, ax
- ASSUME ds:ABS40
- mov ax, 600h ; clear display area
- xor cx, cx ; upper left corner
- mov dx, crt_cols
- dec dl ; right column
- mov dh, ega_rows ; lower row
- mov bh, cs:cur_attrib ; character attribute
- int 10h
- pop ds
- ASSUME ds:CODE
- xor ax, ax ; return success
- ret
-
- dosfn19 endp
- ENDIF
- page
- if dos4
- ;--- new_mpx ------------------------------------------------
- ; This routine acknoledges that an ansi driver is installed.
- ;
-
- new_mpx proc
- cmp ax, 1a00h ; is this for us?
- jz ackansi
- jmp dword ptr cs:old_mpx ; no -- go to next handler
- ackansi:
- mov al, 0ffh ; say we are installed
- iret
- new_mpx endp
- endif
- page
- ;--- new_vid_bios -------------------------------------------
- ; new_vid_bios takes the set cursor, get display mode, change mode, and
- ; get mode calls.
-
- ; If bios_write_tty defined, new_vid_bios replaces the write_tty call.
- ; This gives BIOS ANSI capability.
- ; However, it takes away the escape character.
- ; If this is not desired, just tell init to not take over the vector.
- ;
- ; All other calls get sent to the old video bios.
-
- new_vid_bios proc
- STI
- IF bios_write_tty
- cmp ah, 0Eh
- je nvb_write_tty
- ENDIF
- cmp Ah, 02h ; set cursor position command?
- je nvb_setcursor
- cmp Ah,0 ; change mode command?
- je nvb_smode
- cmp cs:fmode,0 ; slow mode?
- je new_vid_pass ; then pass it on
- cmp Ah, 0Fh ; get display mode command?
- je nvb_display
- cmp Ah, 06h ; clear screen command?
- je nvb_scroll
- cmp Ah, 07h ; alternative cls command?
- je nvb_scroll
- IF VESA
- cmp AX, 4f02h ; set extended display mode?
- je nvb_smode
- cmp AX, 4f03h ; get extended display mode?
- je nvb_display
- ENDIF
-
- new_vid_pass:
- jmp dword ptr cs:old_vid_bios
- IF bios_write_tty
- ; WRITE TTY SUBCOMMAND
- nvb_write_tty:
- cmp cs:bmode, 0 ; don't write with our driver?
- jz new_vid_pass
- push cx ; save register
- mov cl, cs:cur_attrib
- ; If in graphics mode, BL is new color
- cmp cs:gmode_flag, 0
- jle nvb_wt_text
- mov cs:cur_attrib, bl ; ja?
- nvb_wt_text:
- int 29h
- mov cs:cur_attrib, cl
- pop cx
- iret
- ENDIF
-
- ; GET DISPLAY MODE SUBCOMMAND
- nvb_display:
- cmp cs:gmode_flag,0 ; Graphic mode? Mono?
- jnz new_vid_pass
- push ds
- push dx
- mov dx, ABS40
- mov ds, dx
- assume ds:ABS40
- cmp crt_start,0 ; At start of mem?
- jz nvb_pass
- call move_back
- jmp nvb_pass
-
- ; SCROLL DISPLAY SUBCOMMAND
- nvb_scroll:
- push ds
- push dx
- mov dx, ABS40
- mov ds, dx
- assume ds:ABS40
- mov cs:no_c_flag, 1 ; if graphic, don't draw cursor afterwards
- cmp cs:gmode_flag,0 ; graphic mode?
- jnz nvb_pass
- cmp crt_start,0 ; not at start of mem
- jz nvb_pass
- cmp al, 0 ; scroll, not erase
- jne nvb_pass
- or cx, cx ; not entire screen?
- jne nvb_pass
- pop dx
- push dx
- inc dl
- cmp dl, byte ptr crt_cols ; same question, max columns
- ja nvb_pass ; >size is full screen, though
- cmp dh, ega_rows ; same question, max rows
- ja nvb_pass ; >size is full screen, though
- push ax ; erase is easier since we dont move screen
- xor ax,ax
- mov crt_start,0 ; reset offsets
- mov dx,port_6845
- mov al,0ch
- out dx,ax
- inc al
- out dx,ax
- pop ax
- ; jmp nvb_pass ; jump is redundant
-
- nvb_pass:
- pop dx
- assume ds:nothing
- pop ds
- jmp dword ptr cs:old_vid_bios ; now doit
-
- ; SET CURSOR SUBCOMMAND
- nvb_setcursor:
- push ds
- push dx
- mov dx, ABS40
- mov ds,dx
- assume ds:ABS40
- cmp cs:gmode_flag,0 ; Alpha mode?, Mono mode?
- jle nvb_pass
- cmp cs:no_c_flag, 0 ; inhibited cursor?
- jnz nvb_pass ; then keep inhibited
- cmp cs:gcursor, 0 ; no cursor?
- jz nvb_pass ; then don't want one now!
- push_all
- draw_gcursor
- pop_all
- assume ds:nothing
- pop dx ; restore registers
- pop ds
- call_video ; original int 10h
- push_all
- draw_gcursor ; redraw the cursor
- pop_all
- iret ; return from interrupt
-
- ; SET DISPLAY MODE SUBCOMMAND
- nvb_smode:
- call_video
- push_all
- push ds
- mov dx, ABS40
- mov ds, dx
- assume ds:ABS40
- mov al, crt_mode ; get mode and check for being graphic
- call set_gmode
- mov cs:no_c_flag, al ; if graphic, then no cursor is on screen.
- pop ds
- assume ds:nothing
- pop_all
- iret
- new_vid_bios endp
- page
- ;------ int_29 ----------------------------------------------
- ; Int 29 handles DOS quick-access putchar.
- ; Last device loaded with attribute bit 4 set gets accessed for
- ; single-character writes via int 29h instead of via interrupt.
- ; Must preserve all registers.
- ; Installed as int 29h by dosfn0 (init).
-
- int_29 proc near
- sti
- push ds
- push es
- push_all
- if fast29
- cmp al, 20h ; control char?
- jb slow_way
- cmp cs:escvector, 0 ; middle of an escape sequence?
- jnz slow_way
- mov dx, ABS40
- mov ds, dx ; set addressability
- assume ds:ABS40
- mov cx, word ptr crt_mode ; mode in cl, columns in ch
- push ax
- mov al, cl
- call set_gmode ; check for graphics mode
- pop ax
- cmp cs:gmode_flag, 0 ; slow if graphics
- jg slow_way
- ; cmp cl, 3 ; graphics mode?
- ; ja slow_way
- xor bx, bx ; get cursor position
- mov bl, active_page
- add bx, bx
- mov dx, cursor_posn[bx] ; dh has y, dl has x
- inc dl ; point to next location
- cmp dl, ch ; at edge?
- jnb slow_way
- ; we can go with it!
- mov cursor_posn[bx], dx ; update pointer
- xchg ax, bx
- mov al, dh
- mul ch ; ax has line offset
- add al, dl
- adc ah, 0 ; total offset
- mov cx, bx
- mov bx, ax ; cl has character, bx offset
-
- mov ax, crt_start
- shr ax, 1
- add bx, ax ; corrected cursor offset, either
- ; because of fast scroll or
- ; page<>0
-
- if MONO
- mov dx, addr_6845 ; update cursor location
- else
- mov dx, port_6845 ; update cursor location
- endif
- mov al,0eh ; more effective to write two bytes at a time
- mov ah,bh
- out dx,ax
- inc al
- mov ah,bl
- out dx,ax
-
- mov al, cl ; get back character
-
- dec bx
- add bx, bx ; byte offset
- mov dx, 0b800h ; address screen
- if MONO
- cmp crt_mode, 7
- jne int29_not_mono
- mov dh, 0b0h
- int29_not_mono:
- endif
- mov ds, dx
- assume ds:nothing
- mov ah, cs:cur_attrib
- mov ds:[bx], ax ; write character
- jmp short int_fin
- endif
- slow_way:
- mov cx, 1
- mov bx, cs
- mov es, bx
- mov si, offset int_29_buf
- mov byte ptr es:[si], al
- call dosfn8
- int_fin:
- pop_all
- pop es
- pop ds
- iret
- int_29 endp
-
- page
- ;------ dosfn8 -------------------------------------------------------
- ; Handles writes to the device (with or without verify).
- ; Called with
- ; CX = number of bytes to write
- ; ES:SI = transfer buffer
- ; DS = CS, so we can access local variables. NOT ANY MORE
-
- dosfn8 proc near
-
- mov cs:f_cptr_seg, es ; save segment of char ptr
-
- ; Read the BIOS buffer address/cursor position variables.
- mov ax, ABS40
- mov ds, ax
- assume ds:ABS40
-
- ; Find current video mode and screen size.
- mov ax,word ptr crt_mode ; al = crt mode; ah = # of columns
- mov cs:video_mode, al
- dec ah ; ah = max column
- mov cs:max_x, ah
-
- ; Save graphics mode flag
- call set_gmode
-
- mov al, ega_rows ; number of display rows
- mov cs:max_y, al ; set maxy value
-
- ; Find current cursor coordinates.
-
- mov al, active_page
- cbw
- add ax, ax
- xchg bx, ax
- mov ax, cursor_posn[bx]
- mov cs:cur_coords, ax
-
- if MONO
- mov ax, addr_6845 ; get the right 6845
- mov cs:our_6845, ax
- endif
-
- ; Find video buffer segment address; adjust it
- ; so the offset is zero; return in AX.
-
- mov ax, 0B800H ; this gets corrected in xy_to_regs
- ; if we are not screen 0
- if MONO
- cmp crt_mode, 7
- jne not_mono_mode
- mov ah, 0b0h
- not_mono_mode:
- endif
- push cs
- pop ds
- assume ds:CODE
- mov es, ax
- call xy_to_regs ; Set DX, DI according to cur_coords.
-
- ; | If in graphics mode, clear old pseudocursor
-
- cmp gmode_flag, 0
- jle d8_no_cp
- cmp no_c_flag, 0 ; cursor not previously drawn?
- mov no_c_flag, 0 ; (reset flag, will be drawn from now on)
- jnz d8_no_cp ; not drawn -- don't clear
- cmp gcursor, 0 ; don't clear if cursor is off, either
- jz d8_no_cp
- push cx
- push dx
- push di
- draw_gcursor
- pop di
- pop dx
- pop cx
-
- d8_no_cp:
-
- mov ah, cur_attrib
- mov ds, f_cptr_seg ; get segment of char ptr
- assume ds:nothing
- cld ; make sure we'll increment
-
- ; The Inner Loop: 12+4+4+11+14+2+19= 66 cycles/loop
- ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
- ; At that speed, it takes 32 milliseconds to fill a screen.
-
- ; Get a character, put it on the screen, repeat 'til end of line
- ; or no more characters.
- jcxz f_loopdone ; if count = 0, we're already done.
- cmp cs:escvector, 0 ; If in middle of an escape sequence,
- jz f_tloop
- jmp f_in_escape ; jump to escape sequence handler.
-
- f_tloop:; | If in graphics mode, jump to alternate loop
- ; | What a massive kludge! A better approach would have been
- ; | to collect characters for a "write n chars" routine
- ; | which would handle both text and graphics modes.
- cmp cs:gmode_flag,0
- jle f_t_cloop
- jmp f_g_cloop
-
- even ; tiny performance boost
- f_t_cloop:
- LODSB ; get char! (al = ds:[si++])
- cmp al, 28 ; is it a control char?
- jb f_control ; maybe...
- f_t_nctl:
- STOSW ; Put Char! (es:[di++] = ax)
- dec dx ; count down to end of line
- loopnz f_t_cloop ; and go back for more.
- jz f_t_at_eol ; at end of line, maybe do a crlf.
- jmp short f_loopdone ; finished execution
-
- f_looploop:
- f_ansi_exit: ; in case we switched into
- loopnz f_tloop ; a graphics mode
- jnz f_loopdone
- f_t_at_eol:
- jmp f_at_eol
-
- f_loopdone:
-
- ;--------- All done with write request -----------
- ; DI is cursor address, cursor position in cur_y, dl
-
- assume ds:ABS40
- mov ax, ABS40
- mov ds, ax
-
- ; Set cursor position in low memory.
-
- mov al,active_page
- cbw
- add ax,ax
- xchg bx,ax
- mov al, cs:max_x
- inc al
- sub al, dl
- mov ah, cs:cur_y
- mov cursor_posn[bx],ax
-
- cmp cs:gmode_flag,0
- jg pseudocursor ; In graphics mode, there is
- ; a pseudo cursor to draw.
-
- ; Write directly to 6845 cursor address register.
- mov bx, di
- shr bx, 1 ; convert word index to byte index
-
- if MONO
- mov dx, cs:our_6845 ; either mono or color card
- else
- mov dx, port_6845 ; color card
- endif
- mov al,0eh ; more effective to write two bytes at a time
- mov ah,bh
- out dx,ax
- inc al
- mov ah,bl
- out dx,ax
-
-
- ; Return to DOS.
- xor ax, ax ; No error, not busy.
- ret
-
- pseudocursor:
- cmp cs:gcursor, 0 ; graphics cursor off?
- jz nopseudo
- mov cs:no_c_flag,0 ; there is a cursor now!
- draw_gcursor
- nopseudo:
- xor ax, ax
- ret
-
- ;---- handle control characters ----
- ; Note: cur_x is not kept updated in memory, but can be
- ; computed from max_x and dx.
- ; Cur_y is kept updated in memory.
- f_escapex: ; far jump
- jmp f_escape
-
- f_control:
- cmp al, 27 ; Is it an escape?
- jz f_escapex
- cmp al, 13 ; carriage return?
- jz f_cr
- cmp al, 10 ; line feed?
- jz f_lf
- cmp al, 8 ; backspace?
- jz f_bs
- cmp al, 9 ; tab?
- jz f_tab
- cmp al, 7 ; bell
- jz f_bell
- jmp f_nctl ; then it is not a control char.
-
- f_bell: ;----- Handle bell ----------------------
- ; Use BIOS to do the beep. DX is not changed, as bell is nonprinting.
- call beep
- or al, al ; clear z
- jmp f_looploop ; Let main loop decrement cx.
-
- f_bs: ;----- Handle backspace -----------------
- ; Moves cursor back one space without erasing. No wraparound.
- cmp dl, cs:max_x ; wrap around to previous line?
- ja fbs_wrap ; yep; disallow it.
- dec di ; back up one char & attrib,
- dec di
- inc dx ; and note one more char left on line.
- fbs_wrap:
- jmp f_looploop
-
- f_cr: ;----- Handle carriage return -----------
- ; di -= cur_x<<1; set di= address of start of line
- ; dx=max_x+1; set bx= chars left in line
- mov al, cs:max_x
- inc al
- sub al, dl ; Get cur_x into ax.
- mov ah, 0
- sub di, ax
- sub di, ax
- mov dl, cs:max_x ; Full line ahead of us.
- inc dx
- mov ah, cs:cur_attrib ; restore current attribute
- or al, 1 ; clear z
- jmp f_looploop ; and let main loop decrement cx
-
- f_at_eol:
- ;----- Handle overrunning right end of screen -------
- ; cx++; compensate for double loop
- ; if (!wrap_flag) { dx++; di-=2; }
- ; else do_crlf;
- inc cx
- test cs:wrap_flag, 1
- jnz feol_wrap
- dec di
- dec di
- inc dx
- jmp f_looploop
- feol_wrap:
- ; dx=max_x+1; set bx= chars left in line
- ; di -= 2*(max_x+1);
- ; do_lf
- mov dl, cs:max_x
- inc dx
- sub di, dx
- sub di, dx
- ; fall thru to line feed routine
-
- f_lf: ;----- Handle line feed -----------------
- ; if (cur_y >= max_y) scroll; scroll screen up if needed
- ; else { cur_y++; di += max_x<<1; else increment Y
-
- mov al, cs:max_y
- cmp cs:cur_y, al
- jb flf_noscroll
- call scroll_up ; preserves bx,cx,dx,si,di
- jmp short flf_done
- flf_noscroll:
- inc cs:cur_y
- mov al, cs:max_x
- mov ah, 0
- inc ax
- add ax, ax
- add di, ax
- flf_done:
- mov ah, cs:cur_attrib ; restore current attribute
- or al, 1 ; clear z
- jmp f_looploop ; and let main loop decrement cx
-
- f_tab: ;----- Handle tab expansion -------------
- ; Get cur_x into al.
- mov al, cs:max_x
- inc al
- sub al, dl
- ; Calculate number of spaces to output.
- push cx ; save cx
- mov ch, 0
- mov cl, al ; get zero based x coordinate
- and cl, 7
- neg cl
- add cl, 8 ; 0 -> 8, 1 -> 8, ... 7 -> 1
- sub dx, cx ; update chars-to-eol, maybe set z
- pushf ; || save Z for main loop
- ; ah is still current attribute. Move CX spaces to the screen.
- mov al, ' '
- cmp cs:gmode_flag,0
- jg f_tab_putc
-
- REP STOSW
- popf ; || restore Z flag for main loop test
- pop cx ; restore cx
- jmp f_looploop ; Let main loop decrement cx.
-
- ;--------------- graphics mode support -----------------------
-
- ;---- Alternate main loop for graphics mode ----
- f_g_cloop:
- LODSB ; get char! (al = ds:[si++])
- cmp al, 28 ; is it a control char?
- jb f_g_control ; maybe...
- f_g_nctl:
- call putchar
- dec dx ; count down to end of line
- loopnz f_g_cloop ; and go back for more.
- jz f_at_eol ; at end of line; maybe do a crlf.
- jmp f_loopdone
-
- f_g_control: jmp f_control
-
- ; Tabs in graphic mode
- f_tab_putc: ; graphics mode- call putc to put the char
- add dx, cx ; move back to start of tab
- f_tp_lp:
- call putchar
- dec dx ; go to next cursor position
- loop f_tp_lp
- popf ; Z set if wrapped around EOL
- pop cx
- jmp f_looploop
-
- ;---- Where to go when a character turns out not to be special
- f_nctl:
- f_not_ansi:
- cmp cs:gmode_flag,0
- jg f_g_nctl
- jmp f_t_nctl ; text mode
-
- page
- ;---- putchar ------------------------------------------------
- ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
- ; On entry, registers set up as per xy_to_regs.
- ; Preserves all registers.
- putchar proc near
- push dx
- push cx
- push bx
- push ax
- ; 1. Set cursor position.
- mov al, cs:max_x
- inc al
- sub al, dl
- mov cs:cur_x, al
- mov dx, cs:cur_coords ; get X & Y into DX
- push ds
- mov ax, 40h
- mov ds, ax
- assume ds:ABS40
- mov cursor_posn,dx
- pop ds
- assume ds:nothing
- xor bx, bx ; choose dpy page 0
- mov ah, 2 ; chose "Set Cursor Position"
- call_video
- ; 2. Write char & attribute.
- IF quick_char
- pop ax
- push ax ; character and attribute
- call quick_graph
- ELSE
- mov cx, 1
- pop ax ; get char in AL
- push ax
- mov bl, ah ; attribute in BL
- mov bh, 0
- mov ah, 9
- call_video
- ENDIF
- pop ax
- pop bx
- pop cx
- pop dx
- ret
- putchar endp
- page
- IF quick_char
- quick_graph proc near
- ; this code has been reworked for much greater speed.
-
- ; ah= mode, al= char, ax,bx,cx,dx destroyed
- gmode_test yesQuick
-
- mov bl,ah
- xor bh,bh
- mov cx, 1
- mov ah, 9
- call_video ; do it the old way
- ret
-
- yesQuick:
- push ds
- mov bx, 40h
- mov ds, bx
- assume ds:ABS40 ; address abs segment
- push es
- push bp
- push si
- push di ; save some registers
- push ax ; save char and mode
-
- mov ax, crt_cols
- mov cx, ega_points ; pixel rows in character
- mov bp, ax ; save number of columns=#bytes
- mul byte ptr (cursor_posn+1)
- mul cx ; (ignore upper product in DX)
- add al, byte ptr (cursor_posn) ; y*#cols*#rows + x
- adc ah, 0 ; take care of carry
- mov si, ax ; save address in si
- xor ax, ax
- mov es, ax ; absolute zero
- les di, es: dword ptr (43h * 4) ; contents of vector 43h
- pop ax
- push ax ; get char and mode
- mul cl ; offset to character in table
- add di,ax ; di has character bit pattern start
- mov ax, 0a000h ; address of display segment
- mov ds, ax
- assume ds:nothing
-
- ; to recap: cx=#rows, bp=#columns, ds:si=display address, es:di=character addr
- mov dx, 3ceh
- mov ax, 0a05h
- out dx,ax ; set write mode 2, read mode 1
-
- mov ax, 7 ; set color dontcare register to zero
- out dx,ax
-
- pop bx ; character mode in bh
- IF gbackground
- mov bl,bh ; extract background color
- IF cheap_pc
- shr bl,1
- shr bl,1
- shr bl,1
- shr bl,1
- ELSE
- shr bl,4
- ENDIF
- or bh, bh
- jns overMode
- mov ax, 1803h ; exor mode
- out dx,ax
-
- and bx, 0f07h ; xor=blink bit
- ELSE
- or bh, bh
- jns overMode
- mov ax, 1803h ; exor mode
- out dx,ax
-
- and bx, 7f00h ; mask off xor bit
- ENDIF
- mov al, 8 ; bit mask register
- out dx, al
- inc dx
- chLoop:
- mov al, es:[di] ; get pixel pattern
- out dx, al
- and [si],bh ; update foreground
- not al
- out dx, al ; and background
- and [si],bl
- inc di
- add si, bp ; go to next character byte and line
- loop chLoop
-
- joinret:
- dec dx
- mov ax, 0ff08h ; bit mask
- out dx, ax
- mov ax, 5 ; mode register
- out dx, ax
- mov al, 3 ; (ah is zero)
- out dx, ax
- mov ax, 0f07h
- out dx, ax
-
- pop di
- pop si
- pop bp
- pop es
- pop ds
- ret
-
- overMode:
- IF gbackground
- and bx, 0f07h ; xor=blink bit
- ELSE
- and bx, 7f00h ; mask off xor bit
- ENDIF
- mov al, 8 ; bit mask register
- out dx, al
- inc dx
- ; we need to load the internal buffer with a solid
- ; background. By writing a solid background and then
- ; reading it back, we can do the job.
- mov al, 0ffh ; force set background
- out dx, al
- mov [si], bl
- mov al, [si] ; read reset pattern
- chLoop2:
- mov al, es:[di] ; get pixel pattern
- out dx, al
- mov [si],bh ; update foreground
- inc di
- add si, bp ; go to next character byte and line
- loop chLoop2
- jmp joinret
-
- quick_graph endp
- ENDIF
-
-
- ;--------------- end of graphics mode support --------------------
-
- dosfn8 endp
- page
- ;--- get_blank_attrib ------------------------------------------------
- ; Determine new attribute and character for a new blank region.
- ; Use current attribute, just disallow blink and underline.
- ; Returns result in AH, preserves all other registers.
- get_blank_attrib proc near
- IF gbackground
- cmp cs:gmode_flag,0
- jle get_attrib ; if alpha mode
- gmode_test get_attribg ; or good graphic mode, get attrib
- xor ah,ah
- ret
- get_attribg:
- mov ah, cs:cur_attrib ; must do different technique
- IF cheap_pc
- shr ah,1 ; color must be shifted into position
- shr ah,1
- shr ah,1
- shr ah,1
- ELSE
- shr ah,4
- ENDIF
- and ah,07
- ret
- get_attrib:
- mov ah, cs:cur_attrib ; the attribute
- and ah, 7fh ; but disallowing blink
- IF MONO
- cmp cs:video_mode, 7 ; monochrome?
- jne gb_aok
- cmp ah, 7 ; underline?
- jnz gb_aok
- mov ah, 7
- gb_aok:
- ENDIF
- ret
- ELSE
- mov ah, 0 ; 0 is background if graphics mode
- cmp cs:gmode_flag,0
- jg gb_aok
-
- mov ah, cs:cur_attrib
- and ah, 7fh ; disallow blink
- IF MONO
- cmp cs:video_mode, 7 ; monochrome?
- jne gb_aok
- cmp ah, 7 ; underline?
- jnz gb_aok
- mov ah, 7
- ENDIF
- gb_aok: ret
- ENDIF
- get_blank_attrib endp
-
- page
- ;---- scroll_up ---------------------------------------------------
- ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
- ; Moves screen up 1 line, fills the last line with blanks.
- ; Attribute of blanks is the current attribute sans blink and underline.
-
- scroll_up proc near
- push_all
-
- cmp cs:gmode_flag,0
- jz scroll_char
- if MONO
- jl mono_scroll
- endif
- jmp scroll_graphic
- scroll_char:
- push es
- push ds ; save all!
- mov ax, ABS40 ; address low mem via ds
- mov ds, ax
- mov ax, 0b800h ; address display via es
- mov es, ax
- assume ds:ABS40
- cmp cs:fmode,0 ; see if in fast mode
- jz slow_scroll_up
- xor ax,ax ; calc addresses
- mov al, cs:max_x
- inc ax
- mov cx, ax ; save (word) count for fill
- mov bx, ax ; and save byte count
- shl bx, 1 ; byte count
- mov cs:temp_val, bx
- mul cs:max_y ; address offset of last line (words)
- shl ax, 1 ; address offset in bytes
- mov di, ax
-
- mov ax, crt_start ; start of display
- add ax, bx ; add line size in bytes
- add di, ax ; di is now address of new last line
- cmp di, 7fffh - 264 ; is there room here?
- ja no_room_here
-
- mov crt_start, ax
- shr ax, 1 ; make into word offset
- mov bx, ax ; and put into 6845
- mov dx, port_6845
- mov al, 0ch
- out dx, ax
- inc al
- mov ah, bl
- out dx, ax
-
- mov ah, cs:cur_attrib
- and ah, 7fh ; disallow blink
- mov al, 20h ; blank
- rep stosw ; clear line
-
- assume ds:nothing
- pop ds
- pop es
- pop_all
-
- add di, cs:temp_val
- ret
-
- no_room_here:
- pop ds ; restore registers
- pop es
- pop_all
- call move_back ; go to buffer start
- sub di, cs:temp_val
- jmp scroll_up ; try again
-
- if MONO
- mono_scroll:
- push es
- push ds ; save all!
- mov ax, ABS40 ; address low mem via ds
- mov ds, ax
- mov ax, 0b000h ; address display via es
- mov es, ax
- endif
- slow_scroll_up:
- assume ds:ABS40
- mov di, crt_start ; offset of display (because of
- ; different page)
- mov ds, ax ; ds is now display
- assume ds:nothing
- xor ax,ax ; calc addresses
- mov al, cs:max_x
- inc ax
- mov bx, ax ; save (word) count
- shl ax, 1 ; byte count
- mov si, ax ; start address is second line
- add si, di ; adjust start address by any offset
- mov ax, bx
- mul cs:max_y ; number of words to move
- mov cx, ax
- rep movsw ; move them!
- mov cx, bx ; words to clear
- mov ah, cs:cur_attrib
- and ah, 7fh ; disallow blink
- mov al, 20h ; blank
- rep stosw ; clear line
- pop ds
- pop es
- pop_all
- ret
-
- scroll_graphic:
-
- gmode_test scrOurself
- mov bh, 0
- mov al, 1 ; AL is number of lines to scroll.
- mov ah, 6 ; BIOS: scroll up
- xor cx, cx
- mov dl, cs:max_x ; lower-rite-x
- mov dh, cs:max_y ; lower-rite-y (zero based)
- call_video ; call BIOS to scroll a rectangle.
-
- scrret:
- pop_all
- ret
-
- scrOurself: ; try scrolling screen ourself!
- push es
- push ds
-
- mov dx, 3ceh ; set write mode 1
- mov ax, 105h
- out dx, ax
-
- mov ax, 40h ; address abs40 segment
- mov ds, ax
- assume ds:ABS40
- mov ax, crt_cols ; calculate length of line in bytes
- mul byte ptr ega_points
- mov si, ax ; source of move
- xor dx,dx
- mov dl, ega_rows
- mul dx ; number of bytes to move
- mov cx, ax
- mov ax, si ; save bytes in line for later
-
- mov bx, 0a000h ; address display
- mov ds, bx
- mov es, bx
-
- xor di, di ; destination of move
- rep movsb ; scroll
-
- mov cx, ax ; bytes in line = bytes to clear
-
- mov dx, 3ceh
- mov ax, 05h ; return to write mode 0
- out dx, ax
-
- IF gbackground
- mov ah, cs:cur_attrib
- IF cheap_pc
- shr ah,1
- shr ah,1
- shr ah,1
- shr ah,1
- ELSE
- shr ah,4
- ENDIF
- and ah,07 ; background color
- mov al,0
- out dx,ax ; set color to write
-
- mov ax,0f01h ; set mask
- out dx,ax
-
- rep stosb ; clear the line
-
- mov ax,0001 ; reset mask
- out dx,ax
- ELSE
- xor ax, ax
- rep stosb ; clear the line
- ENDIF
-
- pop ds ; restore registers and return
- pop es
- jmp scrret
-
- scroll_up endp
- page
- ;-----move_back --------------------------------------------
- ; This routine moves the display to offset zero.
- ; alters:
- ; cs:temp_val = original crt_start value
- ; crt_start = 0
- ; controller reset properly
- move_back proc near
- push ds
- push es
- push_all
- mov ax, ABS40
- mov ds, ax
-
- assume ds:ABS40
- mov al, ega_rows
- inc al
- mul byte ptr crt_cols ; words to move
- mov cx, ax
- mov si, crt_start
- mov cs:temp_val, si ; save this value
- xor di, di
- mov crt_start, di
- mov bx, cursor_posn ; y in bh, x in bl
- mov al, byte ptr crt_cols
- mul bh
- add al, bl
- adc ah, 0
- xchg bx, ax ; save cursor position in bx
-
- mov ax, 0B800h
- mov es, ax
- mov ds, ax
-
- mov dx, cx
- add dx, cx ; see if overlapping
- cmp dx, si
- ja slow_move
- join_move:
- cld
- rep movsw ; move data
-
- mov dx, port_6845
- mov al, 0ch ; reset offset
- xor ah,ah
- out dx, ax
- inc al
- out dx, ax
- inc al
- mov ah, bh
- out dx, ax
- inc al
- mov ah, bl
- out dx, ax
- ; sti
- assume ds:nothing
- pop_all
- pop es
- pop ds
- ret
-
- slow_move: ; we gotta move to another spot first
- push cx ; save length
- dec dx ; length-2
- dec dx
- add si, dx ; point to end
- mov di, 7FFEh ; safe location -- as safe as we can get
- std
- rep movsw ; move from far end in case of overlap
- ; (may happen on large displays)
- mov dx, port_6845
- mov si, di ; source becomes destination
- inc si ; take care of last decrement
- inc si
- mov cx, si
- shr cx, 1 ; word offset to start of new area
- mov al, 0Ch ; display at this new location
- mov ah, ch
- out dx, ax
- inc al
- mov ah, cl
- out dx, ax
- pop cx ; reset all registers
- xor di, di ; destination is zero
- jmp join_move ; NOW move to destination
-
- move_back endp
- page
- if key_redef
- ;---- lookup -----------------------------------------------
- ; Called by getchar, peekchar, and key to see if a given key has
- ; been redefined.
- ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
- ; Returns with Z cleared if no redefinition; otherwise,
- ; Z is set, SI points to redefinition string, CX is its length.
- ; Preseves AL, all but CX and SI.
- ; Redefinition table organization:
- ; Strings are stored in reversed order, first char last.
- ; The word following the string is the character to be replaced;
- ; the next word is the length of the string sans header.
- ; param_end points to the last byte used by the parameter buffer;
- ; redef_end points to the last word used by the redef table.
-
- lookup proc near
- mov si, redef_end ; Start at end of table, move down.
- or al, al
- jz lu_lp
- mov ah, 0 ; clear extraneous scan code
- lu_lp: cmp si, param_end
- jbe lu_notfound ; If below redef table, exit.
- mov cx, [si]
- cmp ax, [si-2] ; are you my mommy?
- jz lu_gotit
- sub si, 4
- sub si, cx ; point to next header
- jmp lu_lp
- lu_notfound:
- or si, si ; clear Z
- jmp short lu_exit
- lu_gotit:
- sub si, 2
- sub si, cx ; point to lowest char in memory
- cmp al, al ; set Z
- lu_exit:
- ret
- lookup endp
- endif
- page
- ;---- searchbuf --------------------------------------------
- ; Called by getchar and peekchar to see if any characters are
- ; waiting to be gotten from sources other than BIOS.
- ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
- searchbuf proc near
- ; Search the stuffahead buffers.
- if key_redef
- mov cx, 4 ; number of buffers to check for chars
- else
- mov cx, 3
- endif
- mov bx, offset fnkey - 4
- sbloop: add bx, 4 ; point to next buffer record
- mov si, [bx].len
- or si, si ; empty?
- loopz sbloop ; if so, loop.
- ret
- searchbuf endp
- page
- ;---- getchar -----------------------------------------------
- ; Returns AL = next char.
- ; Trashes AX, BX, CX, BP, SI.
- getchar proc near
- gc_searchbuf:
- ; See if any chars are waiting in stuffahead buffers.
- call searchbuf
- jz gc_trykbd ; No chars? Try the keyboard.
- ; A nonempty buffer was found.
- dec [bx].len
- dec si
- mov bp, [bx].adr ; get pointer to string
- mov al, byte ptr ds:[bp][si]; get the char
- ; Recognize function key sequences, move them to highest priority
- ; queue.
- sub si, 1 ; set carry if si=0
- jc gc_nofnkey ; no chars left -> nothing to protect.
- cmp bx, offset fnkey
- jz gc_nofnkey ; already highest priority -> done.
- or al, al
- jnz gc_nofnkey ; nonzero first byte -> not fnkey.
- ; Found a function key; move it to highest priority queue.
- dec [bx].len
- mov ah, byte ptr ds:[bp][si]; gec [bx].len
- mov ah, byte ptr ds:[bp][si]; get the second byte of fn key code
- gc_fnkey:
- mov fnkey.len, 1
- mov fnkeybuf, ah ; save it.
- gc_nofnkey:
- ; Valid char in AL. Return with it.
- jmp short gcdone
-
- gc_trykbd:
- ; Actually get a character from the keyboard.
- mov ah, 0
- int 16h ; BIOS returns with char in AX
- ; If it's Ctrl-break, it has already been taken care of.
- or ax, ax
- jz gc_trykbd
-
- if key_redef
- ; Look in the reassignment table to see if it needs translation.
- call lookup ; Z=found; CX=length; SI=ptr
- jnz gc_noredef
- ; Okay; set up the reassignment, and run thru the translation code.
- mov xlatseq.len, cx
- mov xlatseq.adr, si
- jmp gc_searchbuf
- endif
- gc_noredef:
- ; Is it a function key?
- cmp al, 0
- jz gc_fnkey ; yep- special treatment.
- gcdone: ret ; with character in AL.
-
- getchar endp
- page
- ;---- peekchar -----------------------------------------------
- ; Returns Z if no character ready, AL=char otherwise.
- ; Trashes AX, BX, CX, BP, SI.
- peekchar proc near
- call searchbuf
- jz pc_trykbd ; No chars? Try the keyboard.
- ; A nonempty buffer was found.
- dec si
- mov bp, [bx].adr ; get pointer to string
- mov al, byte ptr ds:[bp][si]; get the char
- ; Valid char from buffer in AL. Return with it.
- jmp short pcdone
- pc_trykbd:
- ; Actually peek at the keyboard.
- mov ah, 1
- int 16h ; BIOS returns with char in AX
- jz pcexit
- ; If it's control-break, it's already been taken care of.
- or ax, ax
- jnz pc_notbrk
- mov ah, 0
- int 16h ; so get rid of it!
- jmp short pc_trykbd
- pc_notbrk:
- if key_redef
- ; Look in the reassignment table to see if it needs translation.
- call lookup ; Z=found; CX=length; SI=ptr
- jnz pcdone ; Nope; just return the char.
- ; Okay; get the first code to be returned.
- add si, cx
- mov al, [si-1]
- endif
- pcdone: or ah, 1 ; NZ; char ready!
- pcexit: ret ; with character in AL, Z true if no char waiting.
- peekchar endp
- page
- ;----- set_gmode ------------------------------------------------
- ; Set gmode_flag based on mode byte in register al
- set_gmode proc near
- if MONO
- cmp al, 7 ; mono?
- je gmode_mono
- gmode_code
- ret
- gmode_mono:
- neg al
- mov cs:gmode_flag, al ; set gmode_flag to neg value
- ret
- else
- gmode_code ; a macro in nnansi_d.asm
- ret
- endif
- set_gmode endp
-
-
- ;---- beep ------------------------------------------------------
- ; Beep speaker; period given by beep_div, duration by beep_len.
- ; Preserves all registers.
-
- beep_div equ 1300 ; fairly close to IBM beep
- beep_len equ 3 ; 3/18 sec- shorter than IBM
-
- beep proc near
- push_all
-
- mov al, 10110110b ; select 8253
- mov dx, 43h ; control port address
- out dx, al
- dec dx ; timer 2 address
- mov ax, beep_div
- jmp $+2
- out dx, al ; low byte of divisor
- xchg ah, al
- jmp $+2
- out dx, al ; high byte of divisor
- mov dx, 61h
- jmp $+2
- in al, dx ; get current value of control bits
- push ax
- or al, 3
- jmp $+2
- out dx, al ; turn speaker on
-
- ; Wait for desired duration by monitoring time-of-day 18 Hz clock
- push es
- mov ax, ABS40
- mov es, ax
- assume es:ABS40
- mov bx, timer_low
- mov cx, -1
- beeplp: mov ax, timer_low
- sub ax, bx
- cmp ax, beep_len
- jg beepover
- loop beeplp
- beepover:
- pop es
- assume es:CODE
-
- ; Turn off speaker
- pop ax
- and al, not 3 ; turn speaker off
- out dx, al
- pop_all
- ret
- beep endp
-
- page
- ; A state machine implementation of the mechanics of ANSI terminal control
- ; string parsing.
- ;
- ; Entered with a jump to f_escape when driver finds an escape, or
- ; to f_in_escape when the last string written to this device ended in the
- ; middle of an escape sequence.
- ;
- ; Exits by jumping to f_ANSI_exit when an escape sequence ends, or
- ; to f_not_ANSI when a bad escape sequence is found, or (after saving state)
- ; to f_loopdone when the write ends in the middle of an escape sequence.
- ;
- ; Parameters are stored as bytes in param_buffer. If a parameter is
- ; omitted, it is stored as zero. Each character in a keyboard reassignment
- ; command counts as one parameter.
- ;
- ; When a complete escape sequence has been parsed, the address of the
- ; ANSI routine to call is found in ansi_fn_table.
- ;
- ; Register usage during parsing:
- ; DS:SI points to the incoming string.
- ; CX holds the length remaining in the incoming string.
- ; ES:DI points to the current location on the memory-mapped screen.
- ; DX is number of characters remaining on the current screen line.
- ; BX points to the current paramter byte being assembled from the incoming
- ; string. (Stored in cur_parm_ptr between device driver calls, if needed.)
- ;
- ; The registers are set as follows before calling the ANSI subroutine:
- ; AX = max(1, value of first parameter)
- ; CX = number of paramters
- ; SI = offset of second parameter from CS
- ; DS = CS
- ; ES:DI points to the current location on the memory-mapped screen.
- ; DX is number of characters remaining on the current screen line.
- ; The subroutine is free to trash AX, BX, CX, SI, and DS.
- ; It must preserve ES, and can alter DX and DI if it wants to move the
- ; cursor.
- ;
- ;------------------------------------------------------------------------
-
- assume cs:code, ds:code
-
- ;----- next_is -------------------------------------------------------
- ; Next_is is used to advance to the next state. If there are characters
- ; left in the input string, we jump immediately to the new state;
- ; otherwise, we shut down the recognizer, and wait for the next call
- ; to the device driver.
- next_is macro statename
- loop statename
- mov ax, offset statename
- jmp sleep
- endm
-
- ;----- sleep --------------------------------------------------------
- ; Remember bx and next state, then jump to device driver exit routine.
- ; Device driver will re-enter at f_in_escape upon next invocation
- ; because escvector is nonzero; parsing will then be resumed.
- sleep: mov cs:cur_parm_ptr, bx
- mov cs:escvector, ax
- jmp f_loopdone
-
- ;----- f_in_escape ---------------------------------------------------
- ; Main loop noticed that escvector was not zero.
- ; Recall value of BX saved when sleep was jumped to, and jump into parser.
- f_in_escape:
- mov bx, cs:cur_parm_ptr
- jmp word ptr cs:escvector
-
- fbr_syntax_error_gate: ; jumped to from inside f_bracket
- jmp syntax_error
-
- ;----- f_escape ------------------------------------------------------
- ; We found an escape. Next character should be a left bracket.
- f_escape:
- next_is f_bracket
-
- ;----- f_bracket -----------------------------------------------------
- ; Last char was an escape. This one should be a [; if not, print it.
- ; Next char should begin a parameter string.
- f_bracket:
- lodsb
- cmp al, '['
- jnz fbr_syntax_error_gate
- ; Set up for getting a parameter string.
- mov bx, cs:param_buffer
- mov byte ptr cs:[bx], 0
- mov cs:in_num, 0
- mov cs:eat_key, 0 ; no eaten key
- next_is f_get_args
-
- ;----- f_get_args ---------------------------------------------------
- ; Last char was a [. If the current char is a '=' or a '?', eat it.
- ; In any case, proceed to f_get_param.
- ; This is only here to strip off the strange chars that follow [ in
- ; the SET/RESET MODE escape sequence.
- f_get_args:
- lodsb
- cmp al, '='
- jz fga_ignore
- cmp al, '?'
- jz fga_ignore
- dec si ; let f_get_param fetch al again
- jmp short f_get_param
- fga_ignore:
- mov cs:eat_key, al ; save eaten key (BUG FIX!)
- next_is f_get_param
-
- ;----- f_get_param ---------------------------------------------------
- ; Last char was one of the four characters "]?=;".
- ; We are getting the first digit of a parameter, a quoted string,
- ; a ;, or a command.
- f_get_param:
- lodsb
- cmp al, '0'
- jb fgp_may_quote
- cmp al, '9'
- ja fgp_may_quote
- ; It's the first digit. Initialize current parameter with it.
- sub al, '0'
- mov byte ptr cs:[bx], al
- mov cs:in_num, 1 ; set flag for sensing at cmd exec
- next_is f_in_param
- fgp_may_quote:
- cmp al, '"'
- jz fgp_isquote
- cmp al, "'"
- jnz fgp_semi_or_cmd ; jump to code shared with f_in_param
- fgp_isquote:
- mov cs:string_term, al ; save it for end of string
- next_is f_get_string ; and read string into param_buffer
-
- ;----- f_get_string -------------------------------------
- ; Last character was a quote or a string element.
- ; Get characters until ending quote found.
- f_get_string:
- lodsb
- cmp al, cs:string_term
- jz fgs_init_next_param
- mov byte ptr cs:[bx], al
- cmp bx, cs:param_end
- adc bx, 0 ; if bx<param_end bx++;
- next_is f_get_string
- ; Ending quote was found.
- fgs_init_next_param:
- mov byte ptr cs:[bx], 0 ; initialize new parameter
- ; | Eat following semicolon, if any.
- next_is f_eat_semi
-
- ;----- f_eat_semi -------------------------------------
- ; Last character was an ending quote.
- ; If this char is a semi, eat it; else unget it.
- ; Next state is always f_get_param.
- f_eat_semi:
- lodsb
- cmp al, ';'
- jz fes_eaten
- inc cx
- dec si
- fes_eaten:
- next_is f_get_param
-
- ;----- syntax_error ---------------------------------------
- ; A character was rejected by the state machine. Exit to
- ; main loop, and print offending character. Let main loop
- ; decrement CX (length of input string).
- syntax_error:
- mov cs:escvector, 0
- mov ah, cs:cur_attrib
- jmp f_not_ANSI ; exit, print offending char
-
- ;------ f_in_param -------------------------------------
- ; Last character was a digit.
- ; Looking for more digits, a semicolon, or a command character.
- f_in_param:
- lodsb
- cmp al, '0'
- jb fgp_semi_or_cmd
- cmp al, '9'
- ja fgp_semi_or_cmd
- ; It's another digit. Add into current parameter.
- sub al, '0'
- xchg byte ptr cs:[bx], al
- push dx
- mov dl, 10
- mul dl
- pop dx
- add byte ptr cs:[bx], al
- next_is f_in_param
- ; Code common to states get_param and in_param.
- ; Accepts a semicolon or a command letter.
- fgp_semi_or_cmd:
- cmp al, ';'
- jnz fgp_not_semi
- cmp bx, cs:param_end ; prepare for next param-
- adc bx, 0 ; if bp<param_end bp++;
- ; Set new param to zero, enter state f_get_param.
- mov cs:in_num, 0 ; no longer inside number
- jmp fgs_init_next_param ; spaghetti code attack!
- fgp_not_semi:
- ; It must be a command letter.
- cmp al, '@'
- jb syntax_error
- cmp al, 'z'
- ja syntax_error
- cmp al, 'Z'
- jbe fgp_is_cmd
- cmp al, 'a'
- jb syntax_error
- ; It's a lower-case command letter.
- ; Remove hole between Z and a to save space in table.
- sub al, 'a'-'['
- fgp_is_cmd:
- ; It's a command letter. Save registers, convert letter
- ; into address of routine, set up new register usage, call routine.
- push si ; These three registers hold info
- push cx ; having to do with the input string,
- push ds ; which has no interest at all to the
- ; control routine.
-
- push cs
- pop ds ; ds is now cs
-
- sub al, '@' ; first command is @: insert chars
- cbw
- add ax, ax
- add ax, offset ansi_fn_table
- ; ax is now pointer to command routine address in table
-
- mov cx, bx
- mov si, param_buffer ; si is now pointer to parameters
- sub cx, si ;
- test in_num, 1
- jz fip_out_num
- inc cx
- fip_out_num: ; cx is now # of parameters
-
- xchg ax, bx ; save pointer to routine in bx
-
- ; Calculate cur_x from DX.
- mov al, max_x
- inc ax
- sub al, dl
- mov cur_x, al
-
- ; Get first parameter into AX; if defaulted, set it to 1.
- mov ah, 0
- lodsb
- or al, al
- jnz fgp_callem
- pushf ; save zero flag (TAA fix)
- inc ax
- popf
- fgp_callem:
- ; Finally, call the command subroutine.
- call word ptr [bx]
-
- pop ds
- pop cx
- pop si
-
- mov ah, cs:cur_attrib ; Prepare for STOSW.
- mov cs:escvector, 0 ; No longer parsing escape sequence.
- ; Set flags for reentry at loopnz
- or dx, dx ; "Any columns left on line?"
- ; Re-enter at bottom of main loop.
- jmp f_ANSI_exit
-
- page
- ; The ANSI control subroutines.
-
- ; Each routine is called with the following register usage:
- ; AX = max(1, value of first parameter)
- ; Z flag is set if first parameter is zero.
- ; CX = number of paramters
- ; SI = offset of second parameter from CS
- ; DS = CS
- ; ES:DI points to the current location on the memory-mapped screen.
- ; DX is number of characters remaining on the current screen line.
- ; The control routine is free to trash AX, BX, CX, SI, and DS.
- ; It must preserve ES, and can alter DX and DI if it wants to move the
- ; cursor.
-
- ;----------------------------------------------------------------
-
- assume cs:code, ds:code
-
- ;----- byteout ---------------------------------------------------
- ; Converts al to a decimal ASCII string (in 0..99),
- ; stores it at ES:DI++. Returns DI pointing at byte after last digit.
- ; Destroys DL.
-
- byteout proc near
- cmp al, 100 ; check for >99 case -- TAA mod
- jb goodbyteout
- push ax
- mov al, '1' ; assume value <200!
- stosb
- pop ax
- sub ax, 100
- goodbyteout:
- aam
- add ax, 3030h
- xchg ah, al
- stosb
- xchg ah, al
- stosb
- ret
- byteout endp
-
- ;----- ansi_fn_table -----------------------------------
- ; Table of offsets of terminal control subroutines in order of
- ; the character that invokes them, @..Z, a..z. Exactly 53 entries.
- ; All the subroutines are defined below in this module.
- ansi_fn_table label word
- dw ic, cup, cdn, cfw, cbk ; @, A, B, C, D
- dw nul, nul, nul, hvp, nul ; E, F, G, H, I
- dw eid, eil, il, d_l, nul ; J, K, L, M, N
- dw nul, dc, nul, nul, nul ; O, P, Q, R, S
- dw nul, nul, nul, nul, nul ; T, U, V, W, X
- dw nul, nul ; Y, Z
- dw nul, nul, nul, nul, nul ; a, b, c, d, e
- dw hvp, nul, sm, nul, nul ; f, g, h, i, j
- dw nul, rm, sgr, dsr, nul ; k, l, m, n, o
- if key_redef
- dw key, nul, nul, scp, nul ; p, q, r, s, t
- else
- dw nul, nul, nul, scp, nul ; p, q, r, s, t
- endif
- dw rcp, nul, nul, nul, nul ; u, v, w, x, y
- dw nul ; z
-
- ansi_functions proc near ; set return type to NEAR
-
-
- ;----- Cursor Motion -----------------------------------------------
-
- ;-- cursor to y,x
- hvp: or al, al ; First parameter is desired Y coordinate.
- jz hvp_yok
- dec ax ; Convert to zero-based coordinates.
- hvp_yok:mov cur_y, al
- ; Get second parameter, if it is there, and set X with it.
- xor ax, ax
- cmp cx, 2 ; was there a second parameter?
- jb hvp_xok
- lodsb ; yes.
- or al, al
- jz hvp_xok
- dec ax ; convert to zero-based coordinates.
- hvp_xok:mov cur_x, al
-
- ; Clip to maximum coordinates.
- hvp_set:
- mov ax, cur_coords ; al = x, ah = y
- cmp al, max_x
- jbe hvp_sxok
- mov al, max_x
- mov cur_x, al
- hvp_sxok:
- cmp ah, max_y
- jbe hvp_syok
- mov al, max_y
- mov cur_y, al
- hvp_syok:
- ; Set values of DX and DI accordingly.
- call xy_to_regs
- ret
-
- ;-- cursor forward --
- cfw: add cur_x, al
- jmp hvp_set
-
- ;-- cursor back -----
- cbk: sub cur_x, al
- jae hvp_set
- mov cur_x, 0
- jmp hvp_set
-
- ;-- cursor down -----
- cdn: add cur_y, al
- jmp hvp_set
-
- ;-- cursor up -------
- cup: sub cur_y, al
- jae hvp_set
- mov cur_y, 0
- jmp hvp_set
-
- ;-- save cursor position --------------------------------------
- scp: mov ax, cur_coords
- mov saved_coords, ax
- ret
-
- ;-- restore cursor position -----------------------------------
- rcp: mov ax, saved_coords
- mov cur_coords, ax
- jmp hvp_set ; Clip in case we have switched video modes.
-
- ;-- set graphics rendition ------------------------------------
- ; Modifies the color in which new characters are written.
-
-
- sgr: dec si ; get back pointer to first parameter
- or cx, cx ; Did he give any parameters?
- jnz sgr_loop
- mov byte ptr [si], 0 ; no parameters, so fake
- inc cx ; one with the default value.
- ; For each parameter
- sgr_loop:
- lodsb ; al = next parameter
- ; Search color table
- push cx
-
- cmp al, 0 ; not reset?
- jnz sgr_continue
- mov color_flag, 7 ; finish up normally
- sgr_continue:
- mov cx, colors
- mov bx, offset color_table-3
- sgr_search:
- add bx, 3
- cmp al, byte ptr [bx]
- loopnz sgr_search ; until match found or done
- jnz sgr_loopx
-
- ; If parameter named a known color, set the current
- ; color variable.
- cmp al, 30
- jge sgr_color
- mov ax, [bx+1]
- and atr_flag, al
- or atr_flag, ah
- jmp short sgr_loopx
- sgr_color:
- mov ax, [bx+1]
- and color_flag, al
- or color_flag, ah
- sgr_loopx:
- pop cx
- loop sgr_loop ; until no more parameters.
-
- mov al, color_flag
- mov ah, atr_flag
- test ah, 4 ; invisible
- jz sgr_notinv
- mov ah, al ; then fade to background
- if cheap_pc
- ror al,1 ; swap nibbles
- ror al,1
- ror al,1
- ror al,1
- else
- ror al,4
- endif
- and ax, 7007h
- or al, ah
- jmp short sgr_done
- sgr_notinv:
- test ah, 2 ; reverse?
- jz sgr_notrev
- if cheap_pc
- ror al,1 ; swap nibbles
- ror al,1
- ror al,1
- ror al,1
- else
- ror al,4
- endif
- sgr_notrev:
- test ah, 1 ; underline?
- jz sgr_notund
- cmp gmode_flag, 0 ; monochrome display?
- jl sgr_mono_und
- and al, 0f0h
- or al, 1 ; blue chars on color display
- jmp short sgr_notund
-
- sgr_mono_und:
- cmp al, 7 ; can only underline if white on black
- jne sgr_notund
- mov al, 1
- sgr_notund:
- and ah, 88h ; get bold and blink attributes
- or al, ah ; and merge them in
- sgr_done:
- mov cur_attrib, al ; save the new attribute
- ; ret
-
- ;----- nul ---------------------------------------------
- ; No-action ansi sequence; called when unknown command given.
- nul: ret
-
-
- ;-- erase in line ----------------------------------------
- ; Uses BIOS to scroll away a one-line rectangle
- eil: push dx
- mov cx, cur_coords
- mov dh, ch
- jmp short scrollem
-
- ;-- erase in display -------------------------------------
- ; Uses BIOS to scroll away all of display
- eid:
- jz eid_toend ; first parms=0 -- erase to end
- or cx,cx ; no args?
- jz eid_toend ; then erase to end
- cmp al, 1 ; erase to beginning?
- jz eid_tostart
- cmp al, 2
- jnz eid_ignore ; param must be two
- mov cur_coords, 0
- call xy_to_regs
- cmp cs:fmode,0 ; see if running in fast mode
- jz eid_slow
- xor di, di ; offset will be zero after clearing
- eid_slow:
- push dx
- xor cx, cx
- mov dh, max_y
- scrollem:
- call get_blank_attrib
- mov bh, ah
- mov dl, max_x
- eid_process:
- mov ax, 600h
- int 10h ; use int 10 so screen repositions itself
- ; call_video rather than "call_video"
- eid_done:
- pop dx
- eid_ignore:
- ret
-
- eid_toend: ; erase following lines then go back and erase in line
- push dx
- call get_blank_attrib
- mov bh,ah
- mov ax, 600h
- mov dh, max_y
- mov dl, max_x
- mov ch, cur_y
- inc ch
- cmp ch, dh ; don't erase if no following
- ja eid_nopost
- xor cl,cl
- call_video
- eid_nopost:
- mov cx, cur_coords
- mov dh, ch
- jmp short scrollem
-
-
- eid_tostart: ; erase preceeding lines then go back and erase in line
- push dx
- call get_blank_attrib
- mov bh,ah
- mov dh, cur_y
- dec dh
- js eid_nopre ; don't erase if no preceeding
- mov ax,600h
- xor cx,cx
- mov dl, max_x
- call_video
- eid_nopre:
- mov dx, cur_coords
- dec dl
- js eid_done
- mov ch, dh
- xor cl, cl
- jmp eid_process
-
- ;-- device status report --------------------------------
- ; Stuffs an escape, a left bracket, current Y, semicolon, current X,
- ; a capital R, and a carriage return into input stream.
- ; The coordinates are 1 to 3 decimal digits each.
-
- dsr: push di
- push dx
- push es
- mov ax, cs
- mov es, ax
- std ; Store string in reversed order for fun
- mov di, offset cpr_esc - 2
- mov al, cur_y
- inc al ; convert to one-based coords
- call byteout ; row
- mov al, ';' ; ;
- stosb
- mov al, cur_x
- inc al ; convert to one-based coords
- call byteout ; column
- mov al, 'R' ; R ANSI function 'Cursor Position Report'
- stosb
- mov al, 13
- mov word ptr cprseq.adr, di ; save pointer to last char in string
- stosb ; send a carriage return, too
- mov ax, offset cpr_esc
- sub ax, di ; ax is # of characters in string
- mov word ptr cprseq.len, ax ; pass info to the getchar routine
- cld
- pop es
- pop dx
- pop di
- ret
- if key_redef
- ;-- keyboard reassignment -------------------------------
- ; Key reassignment buffer is between param_end and redef_end+2, exclusive.
- ; When it shrinks or grows, param_end is moved.
- ; Format of an entry is as follows:
- ; highest address -> length:word (may be 0)
- ; key to replace:word (either hi or low byte is zero)
- ; .
- ; . new key value, "length" bytes long
- ; .
- ; lowest address -> next entry, or free space.
- ; If no arguments are given, keyboard is reset to default condition.
- ; Otherwise, first parameter (or first two, if first is zero) defines
- ; the key whose value is to be changed, and the following parameters
- ; define the key's new, possibly zero-length, value.
-
- key:
- ; Is this a reset?
- or cx, cx
- jz key_init
- ; Get the first (or first two) parameters
- cld
- dec si ; point to first param
- dec cx ; Assume it's a fn key, get two params
- dec cx
- lodsw
- or al, al ; Is it a function key?
- jz key_fnkey
- ; It's not a function key- put second param back
- inc cx
- dec si
- key_fnkey:
- ; Key to redefine now in AX. If it's already redefined,
- ; lookup will set Z, point SI to redef string, set CX to its length.
- push di
- push es
- push cx
- push si
-
- std ; moving up, must move from top down
- push ds
- pop es ; string move must have ES=DS
- call lookup ; rets Z if redefined...
- jnz key_newkey
- ; It's already defined. Erase its old definition- i.e., move
- ; region param_end+1..SI-1 upwards CX+4 bytes, add CX+4 to param_end.
- add cx, 4
- mov bp, param_end ; save old value in bp...
- add param_end, cx
- dec si ; start at (SI-1)
- mov di, si
- add di, cx ; move to (start + CX+4)
- mov cx, si
- sub cx, bp ; length of region old_param_end+1..start
- rep movsb
- key_newkey:
- ; Key not redefined. See if there's enough room to redefine it.
- pop si ; get back pointer to redef string
- pop cx ; get back number of bytes in redef string
- mov di, param_end ; hi byte of new redef record, hi byte of len
- sub di, 4 ; hi byte of new data field
- mov bx, di
- sub bx, cx ; hi byte of remaining buffer space
- sub bx, 16 ; better be at least 16 bytes room
- cmp bx, param_buffer
- jb key_popem ; nope- forget it.
- ; Nothing in the way now!
- mov [di+3], cx ; save length field
- mov [di+1], ax ; save name field
- jcxz key_nullstring
- key_saveloop: ; save data field
- movsb
- add si, 2 ; input string ascending, output descending
- loop key_saveloop
- key_nullstring:
- mov param_end, di ; save adr of new hi byte of free area
- key_popem:
- pop es
- pop di
-
- key_exit:
- cld
- ret
-
- key_init:
- ; Build the default redefinition table:
- ; control-printscreen -> control-P
- push es
- push ds
- pop es
- std
- mov di, redef_end
- mov ax, 1
- stosw
- mov ax, 7200h ; control-printscreen
- stosw
- mov al, 16 ; control P
- stosb
- mov param_end, di ; save new bottom of redef table
- pop es
- jmp key_exit
- endif
-
-
- ;---- Delete/Insert Lines -------------------------------
- ; AL is number of lines to delete/insert.
- ; Preserves DX, DI; does not move cursor.
-
- d_l: ; Delete lines.
- mov ah, 6 ; BIOS: scroll up
- jmp short il_open
-
- il: ; Insert lines.
- mov ah, 7 ; BIOS: scroll down
-
- il_open:
- ; Whether inserting or deleting, limit him to (max_y - cur_y) lines;
- ; if above that, we're just clearing; set AL=0 so BIOS doesn't burp.
- mov bh, max_y
- sub bh, cur_y
- cmp al, bh
- jbe il_ok ; DRK 9/4...
- mov al, 0 ; he tried to move too far
- il_ok:
- push ax
- call get_blank_attrib
- mov bh, ah ; color to use on new blank areas
- pop ax ; AL is number of lines to scroll.
-
- mov cl, 0 ; upper-left-x of data to scroll
- mov ch, cur_y ; upper-left-y of data to scroll
- push dx
- mov dl, max_x ; lower-rite-x
- mov dh, max_y ; lower-rite-y (zero based)
- call_video ; call BIOS to scroll a rectangle.
- pop dx
- ret ; done.
-
- ;-- Insert / Delete Characters ----------------------------
- ; AL is number of characters to insert or delete.
- ; Preserves DX, DI; does not move cursor.
-
- ic: mov ch, 1 ; 1 => swap dest & source below
- jmp short dc_ch
-
- dc: mov ch, 0
-
- dc_ch:
- cmp cs:gmode_flag,0
- jg dc_ret ; | if in graphics mode, ignore.
-
-
- ; AL = number of chars to ins or del (guarenteed nonzero).
- ; Limit him to # of chars left on line.
- cmp al, dl
- jbe dc_cok
- mov al, dl
- dc_cok:
- push di ; DI is current address of cursor
- xchg ax, cx ; CX gets # of chars to ins/del
- mov bp, cx ; BP gets # of columns to clear.
-
- ; Set up source = destination + cx*2, count = dx - cx
- mov ch, 0 ; make it a word
- mov si, di
- add si, cx
- add si, cx
- neg cl
- add cl, dl
- mov ch, 0 ; CX = # of words to transfer
- cld ; REP increments si & di
-
- ; If this is an insert, then flip transfer around in both ways.
- test ah, 1
- jz dc_noswap
- xchg di, si ; source <-> dest
- std ; up <-> down
- mov ax, cx ; make move over same range
- dec ax
- add ax, ax ; AX=dist from 1st to last byte.
- add di, ax ; Start transfer at high end of block
- add si, ax ; instead of low end.
- dc_noswap:
- ; Move those characters.
- push es
- pop ds
- rep movsw
- mov cx, bp
- ; Figure out what color to make the new blanks.
- call get_blank_attrib
- mov al, ' '
- ; Blank out vacated region.
- rep stosw
-
- ; All done.
- cld ; restore normal REP state and
- pop di ; cursor address.
- dc_ret: ret
-
-
- ;---- set / reset mode ---------------------------------------
- ; Sets graphics/text mode; also sets/resets "no wrap at eol" mode.
- ; also set/reset graphic cursor mode
- rm: mov cl, 0 ; reset
- jmp short sm_rs
-
- sm: mov cl, 0ffh ; set
- sm_rs:
- ; Is it "wrap at eol" ?
- cmp al, 7
- jnz sm_notwrap
- mov wrap_flag, cl ; true = wrap at EOL
- ret
- sm_notwrap:
- ; We will make this smarter by requiring the correct lead character,
- ; except for wrap at eol, which has been badly documented in the MSDOS
- ; manuals.
- cmp eat_key, '=' ; set mode -- display mode
- jz sm_notbios
- cmp eat_key, '?' ; set mode of togglable attribute?
- jnz sm_done
- ; Is it set/reset graphic cursor mode?
- cmp al,99
- jnz sm_notgcursor
- mov gcursor, cl
- ret
- sm_notgcursor:
- ; Is it set/reset fast mode?
- cmp al,98
- jnz sm_notspeedy
- mov fmode, cl
- cmp cl,0 ; mode now fast?
- jnz sm_done
- call move_back
- sm_done: ret
- sm_notspeedy:
- ; Is it set/reset bios write?
- cmp al,97
- jnz sm_done
- if bios_write_tty
- mov bmode, cl
- endif
- ret
- sm_notbios:
- ; we can now do 43 lines on a VGA display, if desired, as well as 50.
- ; of course an EGA display will only do 43.
- IF MONO
- cmp video_mode, 7 ; mono mode?
- je sm_done
- ENDIF
- IF VGA
- cmp al, 43
- je is43or50
- cmp al, 50
- jne sm_video ; set to whatever it happens to be
- is43or50:
- dec al
- cmp al, max_y
- je sm_done ; no change in # lines
- cmp video_mode, 3 ; display mode > 3?
- ja sm_done
- cmp al, 49 ; 50 line mode?
- mov ax, 1201H ; set 350 lines on EGA
- jne sm_is43
- mov ax, 1202h ; select 400 scan lines
- sm_is43:
- mov bl,30h ; this call ignored on EGA
- call_video
-
- ELSE
- cmp al, 43
- jne sm_video ; set to whatever it happens to be
-
- cmp max_y, 42 ; no change in # lines?
- je sm_done
- cmp video_mode, 3 ; must be currently mode <= 3
- ja sm_done
- ENDIF
- mov ah,0
- mov al, video_mode ; select the video mode.
- call_video
-
- mov ax,1112h ; Load 8x8 font
- mov bl,0 ; (instead of 8x14 or 8x16)
- call_video
-
- mov ax, 1200h ; Load new printscreen
- mov bl, 20h
- call_video
-
- IF EGA ; set the EGA cursor
- mov dx, port_6845 ; '6845' command reg
- mov ax, 070ah ; start on line 7
- out dx, ax
- mov al, 0bh ; end on line 7
- out dx, ax
- ENDIF
- jmp short sm_home
-
-
- sm_video:
- ; It must be a video mode. Call BIOS.
- ; Save graphics mode flag
- IF VGA
- cmp al, 3 ; On VGA, modes 0-3, set 400 scan lines
- ja no_scan_change
- push ax
- mov ax, 1202h ; select 400 scan lines
- mov bl,30h ; this call ignored on EGA
- call_video
- pop ax
- no_scan_change:
- ENDIF
- IF VESA
- cmp al, 80h ; not a vesa mode?
- jb normal_mode
- xor bx, bx
- mov bl, al
- add bx, 8080h ; set msb and put mode in range
- mov ax, 4f02h
- int 10h ; can't use call_video, because often
- ; VESA compatibility driver is used and
- ; loaded after us.
- jmp short sm_home
- normal_mode:
- ENDIF
- mov ah, 0 ; "set video mode"
- or al, 80h ; but don't erase screen, since colors wrong.
- call_video
- sm_home:
- ; Read the BIOS buffer address/cursor position variables.
- mov ax, ABS40
- push ds
- mov ds, ax
- assume ds:ABS40
-
- ; Find current video mode and screen size.
- mov ax,word ptr crt_mode ; al = crt mode; ah = # of columns
- mov cl, ega_rows ; cl = max row
- pop ds
- assume ds:code
- call set_gmode ; set gmode based on video mode
- mov video_mode, al
- dec ah ; ah = max column
- mov max_x, ah
- mov max_y, cl
-
- ; Since cursor may end up in illegal position, it's best to
- ; just go home after switching video modes.
- mov cur_coords, 0
- call xy_to_regs
-
- jmp eid_slow ; then clear the screen
-
- ansi_functions endp ; end dummy procedure block
-
-
-
- ;-------- Color table -----------------------------------------
- ; Used in "set graphics rendition"
-
- colors equ 28 ; number of colors in table
-
- color_table:
- ; The first set attributes rather than colors
- db 0, 000h,00h ; all attribs off; normal.
- db 1, 0ffh,08h ; bold
- db 2, 0f7h,00h ; dim (not bold)
- db 4, 0ffh,01h ; underline
- db 5, 0ffh,80h ; blink
- db 7, 0ffh,02h ; reverse
- db 8, 0ffh,04h ; invisible
-
- db 22,0f7h,00h ; un-bold, un-dim
- db 24,0feh,00h ; un-underline
- db 25,07fh,00h ; un-blink
- db 27,0fdh,00h ; unreverse
- db 28,0fbh,00h ; un-invisible
-
- ; These set the colors
- db 30,0f8h,00h ; black foreground
- db 31,0f8h,04h ; red
- db 32,0f8h,02h ; green
- db 33,0f8h,06h ; yellow
- db 34,0f8h,01h ; blue
- db 35,0f8h,05h ; magenta
- db 36,0f8h,03h ; cyan
- db 37,0f8h,07h ; white
-
- db 40,08fh,00h ; black background
- db 41,08fh,40h ; red
- db 42,08fh,20h ; green
- db 43,08fh,60h ; yellow
- db 44,08fh,10h ; blue
- db 45,08fh,50h ; magenta
- db 46,08fh,30h ; cyan
- db 47,08fh,70h ; white
-
- page
- ;-------- dos function # 0 : init driver ---------------------
- ; Initializes device driver interrupts and buffers, then
- ; passes ending address of the device driver to DOS.
- ; Since this code is only used once, the buffer can be set up on top
- ; of it to save RAM. Placed at end so that any excess can be deleted
- ; after running
-
- dosfn0 proc near
- assume cs:code, ds:code
- ; The following check for MDA/CGA courtesy of Arend van den Brug
- ; initialise ega_rows to normal value
- ; to enable MDA/CGA support
- mov ax, ABS40
- mov ds, ax
- assume ds:ABS40
- mov al, ega_rows
- or al, al ; not already filled in ?
- jnz set_kb
- mov ega_rows, 24 ; then set it to the normal 25 lines
-
- set_kb:
- ; Install BIOS keyboard break handler.
- xor ax, ax
- mov ds, ax
- assume ds:NOTHING
- mov bx, 6Ch
- mov word ptr [BX],offset break_handler
- mov [BX+02], cs
- ; Install INT 29 quick putchar.
- mov bx, 0a4h
- mov word ptr [bx], offset int_29
- mov [bx+2], cs
- IF dos4
- ; Install INT 2Fh multiplex interrupt, saving old vector.
- mov bx, 02fh * 4
- mov ax, [bx]
- mov word ptr cs:old_mpx, ax
- mov ax, [bx+2]
- mov word ptr cs:old_mpx[2], ax
- mov word ptr [bx], offset new_mpx
- mov word ptr [bx+2], cs
- ENDIF
-
- ; Install INT 10h video bios replacement, saving old vector.
- mov bx, 40h
- mov ax, [bx]
- mov word ptr cs:old_vid_bios, ax
- mov ax, [bx+2]
- mov word ptr cs:old_vid_bios[2], ax
- mov word ptr [bx], offset new_vid_bios
- mov word ptr [bx+2], cs
-
- push cs
- pop ds
- push cs
- pop es ; es=cs so we can use stosb
- assume ds:code
- cld ; make sure stosb increments di
-
- ; Calculate addresses of start and end of parameter/redef buffer.
- ; The buffer occupies the same area of memory as this code!
- ; ANSI parameters are accumulated at the lower end, and
- ; keyboard redefinitions are stored at the upper end; the variable
- ; param_end is the last byte used by params (changes as redefs added);
- ; redef_end is the last word used by redefinitions.
- mov di, offset dosfn0
- mov param_buffer, di
- add di, buf_size
- mov param_end, di ; addr of last byte in free area
- inc di
-
- ; Announce our presence
- mov ax,cs
- mov bx,offset Ident1
- call hexcnv
-
- mov si, offset welcome
- msg_loop:
- lodsb
- cmp al,0
- je msg_done
- int 29h
- jmp msg_loop
-
- msg_done:
-
- IF key_redef
- ; Build the default redefinition table:
- ; control-printscreen -> control-P
- ; (Must be careful not to write over ourselves here!)
- mov al, 16 ; control P
- stosb
- mov ax, 7200h ; control-printscreen
- stosw
- mov ax, 1 ; length field
- mov redef_end, di ; address of last used word in table
- stosw
- endif
-
- ; Return ending address of this device driver.
- ; Status is in AX.
- lds si, req_ptr
- mov word ptr [si+0Eh], di
- mov [si+10h], cs
-
- xor ax, ax
- ; Return exit status in ax.
- ret
-
- welcome:
- db 27,'[33;1m'
- db "NNANSI.SYS for "
- card_id
- if cheap_pc
- db " (XT class processor)"
- else
- db " (AT class processor)"
- endif
- db 13, 10
- db 'By Tom Almy, version 05/91'
- db 13,10,10
- db 'Based on NANSI.SYS V2.2'
- db 13,10
- db '(C) Daniel Kegel, Pasadena, CA 1986.'
- db 13,10
- db 'License NANSI.SYS by sending $10.00(US) to Daniel Kegel,'
- db 13,10
- db '221 Fairview Ave, South Pasadena, CA 91030, USA.'
- db 13, 10, 13, 10
- db 'NNANSI driver loaded at '
- Ident1 db 'XXXX:0000'
- db 13, 10, 27, '[0m', 0
-
- dosfn0 endp
-
- hexcnv proc near
- ; AX= value
- ; BX= pointer to destination string
- ; AX, BX destroyed, all other registers intact.
- push cx
- push dx
- mov cx, 4
- hexcnv_loop:
- if cheap_pc
- rol ax, 1
- rol ax, 1
- rol ax, 1
- rol ax, 1
- else
- rol ax, 4
- endif
- mov dl, al
- and dl, 0fh
- add dl, '0'
- cmp dl, '9'
- jbe hexcnv_isdigit
- add dl, 'A'-'9'-1
- hexcnv_isdigit:
- mov [bx], dl
- inc bx
- loop hexcnv_loop
- pop dx
- pop cx
- ret
-
- hexcnv endp
-
- code ends
- end
-