home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / SCREEN / NNANS591.ZIP / NNANSI.ASM < prev    next >
Encoding:
Assembly Source File  |  1991-06-13  |  76.9 KB  |  3,169 lines

  1. %title    "NNANSI Terminal Driver Version 5/91"
  2. %pagesize    60, 132
  3. %noconds
  4. JUMPS    ; clarify branches -- TASM V2.0 recommended
  5. ;--- nnansi.asm ----------------------------------------------------------
  6. ; New, New ANSI terminal driver.
  7. ; Optimized for speed in the case of multi-character write requests.
  8. ; Original NANSI terminal driver (C) 1986 Daniel Kegel, Pasadena, CA
  9. ; Modifications by Tom Almy without restrictions.
  10.  
  11. ; May be distributed for educational and personal use only
  12.  
  13. ; See file NNANSI.DOC for licensing information.
  14.  
  15. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  16. ; Revision history:
  17. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  18. ; 6  july 85: split off ANSI stuff into other files, added backspace
  19. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  20. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  21. ; 9  aug 85:  added cursor position reporting
  22. ; 10 aug 85:  added output character translation
  23. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  24. ; 19 Aug 85:  Fixed horrible bug in insert/delete line.
  25. ; 26 Aug 85:  Fixed simple limit-to-one-too-few-lines bug in ins/del line;
  26. ;  anyway, it inserts 24 lines when on line 2 now.  Whether it's fixed...
  27. ; 4 Sept 85:  Fixed bug created on 26 Aug 85; when limiting ins/del line
  28. ;  count, we are clearing, not scrolling; fixed BIOS call to reflect this.
  29. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  30. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  31. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  32. ;          direct cursor positioning, takeover of BIOS write_tty,
  33. ;          noticed & squashed 2 related bugs in tab expansion
  34. ; 30 Jan 86:  Added EGA cursor patch
  35. ; 31 Jan 86:  Disabled insert/delete char in graphics modes
  36. ;          Implemented keyboard redefinition reset
  37. ;  1 Feb 86:  added video_mode and max_x test after mode set
  38. ; 13 feb 86:  Squashed them again, harder
  39. ; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
  40. ;          If the addition of the beep period to the
  41. ;          BIOS low timer word results in an overflow, the beep will be
  42. ;          supressed. Also made code compatible eith earlier versions
  43. ;          of assembler.
  44.  
  45. ; Tom Almy, Tualatin, Oregon (toma@sail.labs.tek.com) modified the NANSI 
  46. ;  version ; 2.2 code for use in EGA/VGA environments only:
  47. ; 8 Jan 89:   Additional compilation options
  48. ;             Scrolling via reprogramming display start (*MUCH* faster)
  49. ;          INT29 updates display directly if not control character.
  50. ;          Various cleanups
  51. ; Nov 89:     Some bug fixes, customization for various cards enhanced
  52. ;             display modes, better handling of graphic cursor, graphic
  53. ;             characters in 16 color modes are drawn by NNANSI rather
  54. ;             than BIOS (much faster).
  55. ; Sept 90:    Color backgrounds and XOR mode (as BLINK) in 16 color graphic
  56. ;          modes. VGA has 43 or 50 line modes (instead of producing 50
  57. ;          (when 43 selected). FAST mode can be toggled via escape 
  58. ;          sequences. Lots of code clean up. Some old options incorporated
  59. ;          (extra ANSI sequences) or deleted (output conversion).
  60. ;             The fast graphic draw routine has been speeded up, since it
  61. ;          was slower than some BIOSes (!). The BIOS TTY call has been
  62. ;             redirected to the INT29 routine which has some efficiency
  63. ;          speedups; this also saved code space.
  64. ; May 1991:   Restored CGA and MDA support. MDA is optional. Added 
  65. ;          DOS 4.x compatibility. Added 40x43 and 40x50 text mode support.
  66. ;          Added VESA support. Fixed bug allowing either = or ? or nothing
  67. ;          in all set mode commands. Added enable/disable for bios 
  68. ;          write_tty takeover. Added drivers for new cards and changed
  69. ;          some defaults. Merged source code into one file.
  70. ;           Card id printout idea by Arend van den Brug.
  71.  
  72. page
  73. TRUE    equ    1
  74. FALSE    equ    0
  75.  
  76.     INCLUDE CONFIG.INC
  77. page
  78. ; Option dependent definitions
  79. if VGA
  80. if EGA
  81. card_id macro
  82.     db    'Generic EGA/VGA'
  83. endm
  84. else
  85. card_id macro
  86.     db    'Generic VGA'
  87. endm
  88. endif
  89. else
  90. if EGA
  91. card_id macro
  92.     db    'Generic EGA'
  93. endm
  94. else
  95. card_id macro
  96.     db    'Generic NANSI Clone'
  97. endm
  98. endif
  99. endif
  100.  
  101. IF key_redef
  102. buf_size    equ    init_buf_size    ; size of parameter/redef buffer
  103. ELSE
  104. buf_size    equ    20        ; size of parameter/redef buffer 
  105. ENDIF
  106.  
  107.  
  108. IF    cheap_pc
  109.     .8086
  110. ELSE
  111.     .286c
  112. ENDIF
  113.  
  114. page
  115.     INCLUDE DRIVERS.INC
  116. page
  117. ; Some Useful Macros
  118.  
  119. IF    cheap_pc
  120. push_all    macro
  121.     push    ax
  122.     push    bx
  123.     push    cx
  124.     push    dx
  125.     push    bp
  126.     push    si
  127.     push    di
  128.     endm
  129. ELSE
  130. push_all    macro
  131.     pusha
  132.     endm
  133. ENDIF
  134.  
  135. IF    cheap_pc
  136. pop_all macro
  137.     pop    di
  138.     pop    si
  139.     pop    bp
  140.     pop    dx
  141.     pop    cx
  142.     pop    bx
  143.     pop    ax
  144.     endm
  145. ELSE
  146. pop_all macro
  147.     popa
  148.     endm
  149. ENDIF
  150.  
  151. call_video macro    ; call original video interrupt
  152.     pushf        ; push flags
  153.     call    dword ptr old_vid_bios
  154. endm
  155.  
  156. draw_gcursor macro        ; draw graphic cursor
  157. if quick_char
  158.     mov    ax, 8f16h
  159.     call    quick_graph
  160. else
  161.     mov    ax, 0916h        ; draw cursor at location
  162.     mov    bx, 8fh
  163.     mov    cx, 1
  164.     call_video
  165. endif
  166. endm
  167.     page
  168.  
  169. keybuf    struc                ; Used in getchar
  170. len    dw    ?
  171. adr    dw    ?
  172. keybuf    ends
  173.  
  174.  
  175. ABS40    segment at 40h
  176.     org    1ah
  177. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  178. buffer_tail    dw    ?
  179.  
  180.     org    49h
  181. crt_mode    db    ?
  182. crt_cols    dw    ?
  183. crt_len        dw    ?
  184. crt_start    dw    ?
  185. cursor_posn    dw    8 dup (?)
  186. cursor_mode    dw    ?
  187. active_page    db    ?
  188. addr_6845    dw    ?
  189. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  190. crt_palette    db    ?
  191.     org    6ch
  192. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  193.     org    84h
  194. ega_rows    db    ?    ; #rows-1 on display
  195. ega_points    dw    ?    ; bytes per character
  196.  
  197. ABS40    ends
  198.  
  199.     page
  200.  
  201. CODE    segment word public 'CODE'
  202. assume    cs:CODE, ds:CODE
  203.  
  204.     ; Device Driver Header
  205.  
  206.     org    0
  207.  
  208.     dd    -1            ; next device
  209. if dos4
  210.     dw    08053h            ; attributes
  211. else
  212.     dw    8013h            ; attributes
  213. endif
  214.     dw    strategy        ; request header pointer entry
  215.     dw    interrupt        ; request entry point
  216.     db    'CON'            ; device name (8 char)
  217.     db    5 dup (20h)        ;  ... and 5 blanks)
  218.  
  219.     ; Identification- in case somebody TYPEs the assembled driver
  220.     db    27,'[2J'
  221.     db    "NNANSI.SYS 05/91 for "
  222.     card_id
  223.     ife    cheap_pc
  224.     db    "(80286)"
  225.     else
  226.     db    "(80x86)"
  227.     endif
  228.     db    13, 10
  229.     db    'by Tom Almy based on code (C) Daniel Kegel, Pasadena, CA 1986.'
  230.     db    13, 10, 26
  231.  
  232.     even
  233. ;----- variable area --------------------
  234.     org    $-38        ; overlay id string with uninitialized data
  235. if DOS4
  236.     org    $-4
  237. endif
  238. if key_redef
  239.     org    $-2
  240. endif
  241. if MONO
  242.     org    $-2
  243. endif
  244. req_ptr label    dword
  245. req_off dw    ?
  246. req_seg dw    ?
  247. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  248. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  249. old_vid_bios    dd    ?    ; pointer to old video bios routine
  250. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  251. temp_val    dw    ?    ; just a temporary
  252. param_buffer    dw    ?    ; address of first byte free for new params
  253. param_end    dw    ?    ; address of end of free area
  254. if key_redef
  255. redef_end    dw    ?    ; address of end of redefinition area
  256. endif
  257. if dos4
  258. old_mpx        dd    ?    ; original mpx interrupt handler
  259. endif
  260. if MONO
  261. our_6845    dw    ?
  262. endif
  263. no_c_flag    db    ?    ; there is no graphic cursor on the screen.
  264. max_y        db    ?    ; lines-1
  265. max_cur_x    label    word    ; used to get both max & cur at once
  266. max_x        db    ?    ; line width (79 for 80x25 modes)
  267. cur_coords    label    word
  268. cur_x        db    ?    ; cursor position (0 = left edge)
  269. cur_y        db    ?    ;          (0 = top edge)
  270. video_mode    db    ?    ; ROM BIOS video mode (2=BW, 3=color, etc)
  271. string_term    db    ?    ; either escape or double quote
  272. in_num        db    ?    ; true if between a digit and a semi in parse
  273. int_29_buf    db    ?    ; character buffer for int 29 calls
  274. fnkeybuf    db    ?    ; holds second byte of fn key codes
  275. eat_key        db    ?    ; Eaten key (bug fix)
  276. cpr_buf        db    8 dup (?), '['
  277. cpr_esc        db    1bh    ; descending buffer for cpr function
  278.  
  279.     even    ; this should be redundant, if I did it right
  280.  
  281. ; following four keybufs hold information about input
  282. ; Storage order determines priority- since the characters making up a function
  283. ; key code must never be separated (say, by a Control-Break), they have the
  284. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  285. ; lowest priority.
  286.  
  287. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  288. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  289. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  290. if key_redef
  291. xlatseq keybuf    <0>        ; keyboard reassignment string
  292. endif
  293.  
  294. escvector    dw    0    ; state vector of ESCape sequencor
  295. brkkeybuf    db    3    ; control C
  296. wrap_flag    db    1    ; 0 = no wrap past line end
  297. cur_attrib    db    7    ; current char attributes
  298. gmode_flag    db    0    ; true if in graphics mode
  299.                 ; <0 for mono card
  300. gcursor        db    initgc    ; true if graphic cursor enabled
  301. fmode        db    initfast    ; in fast mode?
  302. bmode        db    initbiosw    ; ANSI write_tty BIOS command?
  303. atr_flag    db    0    ; 80h - blink, 8 - bright
  304.                 ; 4 - invisible 2 - reverse 1 underline
  305. color_flag    db    7h    ; fg and bg colors
  306.  
  307. port_6845    equ    3d4h
  308. page
  309. ;------ xy_to_regs --------------------------------------------
  310. ; on entry: x in cur_x, y in cur_y
  311. ; on exit:  dx = chars left on line, di = address
  312. ; Alters ax, bx.
  313. xy_to_regs    proc    near
  314.     ; Find number of chars 'till end of line, keep in DX
  315.     mov    ax, max_cur_x
  316.     mov    bx, ax            ; save max_x & cur_x for next block
  317.     xor    ah, ah            ; ax = max_x
  318.     xchg    dx, ax
  319.     mov    al, bh
  320.     xor    ah, ah            ; ax = cur_x
  321.     sub    dx, ax
  322.     inc    dx            ; dx is # of chars till EOL
  323.     ; Calculate DI = current address in text buffer
  324.     mov    al, bl            ; al = max_x
  325.     inc    al
  326.     mul    cur_y
  327.     add    al, bh            ; al += cur_x
  328.     adc    ah, 0            ; AX is # of chars into buffer
  329.     add    ax, ax
  330.     xchg    di, ax            ; DI is now offset of cursor.
  331.  
  332.     push    ds
  333.     mov    ax, ABS40
  334.     mov    ds, ax
  335.     assume    ds:ABS40
  336.     add    di, crt_start        ; crt offset
  337.                     ; the offset could be non-zero because
  338.                     ; of video pages or fast scrolling.
  339.     pop    ds
  340.     assume    ds:nothing
  341.     ret
  342. xy_to_regs    endp
  343.  
  344. page
  345. ;------- dos_fn_tab -------------
  346. ; This table is used in "interrupt" to call the routine that handles
  347. ; the requested function.
  348.  
  349. if dos4
  350. max_cmd equ    24
  351. dos_fn_tab:
  352.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  353.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  354.     dw    badcmd, badcmd, badcmd, nopcmd, badcmd ; 12-16 
  355.     dw    badcmd, badcmd    ; 17-18 unassigned
  356.     dw    dosfn19
  357.     dw    badcmd, badcmd, badcmd    ; 20-22 unassigned
  358.     dw    nopcmd, nopcmd
  359. else
  360. max_cmd equ    12
  361. dos_fn_tab:
  362.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  363.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  364. endif
  365. ;------- strategy ----------------------------------------------------
  366. ; DOS calls strategy with a request which is to be executed later.
  367. ; Strategy just saves the request.
  368.  
  369. strategy    proc    far
  370.     mov    cs:req_off,BX
  371.     mov    cs:req_seg,ES
  372.     ret
  373. strategy    endp
  374.  
  375. ;------ interrupt -----------------------------------------------------
  376. ; This is where the request handed us during "strategy" is
  377. ; actually carried out.
  378. ; Calls one of 12 subroutines depending on the function requested.
  379. ; Each subroutine returns with exit status in AX.
  380.  
  381. interrupt    proc    far
  382.  
  383.     sti
  384.     push_all            ; preserve caller's registers
  385.     push    ds
  386.     push    es
  387.     
  388.  
  389.     ; Read requested function information into registers
  390.     lds    bx,cs:req_ptr
  391.     xor    ah,ah            ; clear upper part of ax
  392.     mov    al,ds:[BX+02h]        ; al = function code
  393. ;
  394. ; The next instruction blows up MASM 1.0 but who cares!!
  395. ;
  396.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  397.     mov    cx,[BX+12h]        ; cx = input/output byte count
  398.  
  399.     cmp    al, max_cmd
  400.     ja    unk_command        ; too big, exit with error code
  401.  
  402.     xchg    bx, ax
  403.     shl    bx, 1            ; form index to table of words
  404.     mov    ax, cs
  405.     mov    ds, ax
  406.     call    word ptr dos_fn_tab[bx]
  407. int_done:
  408.     lds    bx,cs:req_ptr        ; report status
  409.     or    ax, 100h        ; (always set done bit upon exit)
  410.     mov    [bx+03],ax
  411.  
  412.     pop    ES            ; restore caller's registers
  413.     pop    DS
  414.     pop_all
  415.     ret                ; return to DOS.
  416.  
  417. unk_command:
  418.     call    badcmd
  419.     jmp    int_done
  420.  
  421. interrupt    endp
  422.     page
  423. ;----- BIOS break handler -----------------------------------------
  424. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  425. ; Simply notes that a break was hit.  Flag is checked during input calls.
  426.  
  427. break_handler    proc
  428.     mov    cs:brkkey.len, 1
  429.     iret
  430. break_handler    endp
  431.  
  432.     page
  433.  
  434. ;------ badcmd -------------------------------------------------------
  435. ; Invalid function request by DOS.
  436. badcmd    proc    near
  437.     mov    ax, 813h        ; return "Error: invalid cmd"
  438.     ret
  439. badcmd    endp
  440.  
  441.  
  442. ;------ nopcmd -------------------------------------------------------
  443. ; Unimplemented or dummy function request by DOS.
  444. nopcmd    proc    near
  445.     xor    ax, ax            ; No error, not busy.
  446.     ret
  447. nopcmd    endp
  448.     page
  449. ;------- dos function #4 ----------------------------------------
  450. ; Reads CX characters from the keyboard, places them in buffer at
  451. ; ES:SI.
  452. dosfn4    proc    near
  453.     jcxz    dos4done
  454.     mov    di, si
  455. dos4lp: push    cx
  456.     call    getchar
  457.     pop    cx
  458.     stosb
  459.     loop    dos4lp
  460. dos4done:
  461.     xor    ax, ax            ; No error, not busy.
  462.     ret
  463. dosfn4    endp
  464.     page
  465. ;-------- dos function #5: non-destructive input, no wait ------
  466. ; One-character lookahead into the keyboard buffer.
  467. ; If no characters in buffer, return BUSY; otherwise, get value of first
  468. ; character of buffer, stuff into request header, return DONE.
  469. dosfn5    proc    near
  470.     call    peekchar
  471.     jz    dos5_busy
  472.  
  473.     lds    bx,req_ptr
  474.     mov    [bx+0Dh], al
  475.     xor    ax, ax            ; No error, not busy.
  476.     jmp    short dos5_exit
  477. dos5_busy:
  478.     MOV    ax, 200h        ; No error, busy.
  479. dos5_exit:
  480.     ret
  481.  
  482. dosfn5    endp
  483.     page
  484. ;-------- dos function #6: input status --------------------------
  485. ; Returns "busy" if no characters waiting to be read.
  486. dosfn6    proc    near
  487.     call    peekchar
  488.     mov    ax, 200h        ; No error, busy.
  489.     jz    dos6_exit
  490.     xor    ax, ax            ; No error, not busy.
  491. dos6_exit:
  492.     ret
  493. dosfn6    endp
  494.     page
  495. ;-------- dos function #7: flush input buffer --------------------
  496. ; Clears the IBM keyboard input buffer.     Since it is a circular
  497. ; queue, we can do this without knowing the beginning and end
  498. ; of the buffer; all we need to do is set the tail of the queue
  499. ; equal to the head (as if we had read the entire queue contents).
  500. ; Also resets all the device driver's stuffahead buffers.
  501. dosfn7    proc    near
  502.     xor    ax, ax
  503.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  504.     mov    cprseq.len, ax
  505.     mov    brkkey.len, ax
  506. if key_redef
  507.     mov    xlatseq.len, ax
  508. endif
  509.  
  510.     mov    ax, ABS40
  511.     mov    es, ax
  512.     mov    ax, es:buffer_head    ; clear queue by making the tail
  513.     mov    es:buffer_tail, ax    ; equal to the head
  514.  
  515.     xor    ax, ax            ; no error, not busy.
  516.     ret
  517. dosfn7    endp
  518.     page
  519. IF dos4
  520. ;------- dos function #19: Generic IOCTL
  521. ; DOS4 way of setting display size using MODE command.
  522. ; We will do more than necessary.
  523. ;
  524. dosfn19    proc    near
  525.     les    bx, req_ptr    ; get request block
  526.     mov    ax, es:[BX+13]    ; get function code
  527.     cmp    ax, 5f03h    ; is it set information?
  528.     jz    setinfo
  529.     cmp    ax, 7f03h    ; is it get information?
  530.     jz    getinfo
  531.     mov    ax, 8003h    ; return bad command
  532.     ret
  533. getinfo: 
  534.     les    bx, es:[bx+19]    ; data packet to set
  535.     push    ds
  536.     mov    ax, ABS40    ; address ABS40 area
  537.     mov    ds, ax
  538.     assume ds:ABS40
  539.     mov    word ptr es:[bx+0], 0    ; level and reserved fields
  540.     mov    word ptr es:[bx+2], 14    ; length field
  541.     mov    word ptr es:[bx+4], 0    ; flags
  542.     mov    al, crt_mode
  543.     call    set_gmode
  544.     mov    ax, 1
  545.     cmp    cs:gmode_flag, 0    ; see if graphics mode
  546.     jz    fn19txt
  547.     inc    ax
  548. if MONO
  549.     cmp    cs:gmode_flag, -7    ; check for mono mode
  550.     jne    fn19txt
  551.     dec    ax
  552.     mov    word ptr es:[bx+8], 0    ; monochrome
  553.     jmp    short fn19mono
  554. endif
  555. fn19txt:
  556.     mov    word ptr es:[bx+8], 4    ; say 16 colors
  557. fn19mono:
  558.     mov    word ptr es:[bx+6], ax    ; store mode
  559.     mov    ax, crt_cols
  560.     mov    word ptr es:[bx+0eh], ax    ; character columns
  561.     mov    dx, 8            ; all characters are 8 pixels wide
  562.     mul    dx
  563.     mov    word ptr es:[bx+0ah], ax    ; pixel columns
  564.     xor    ax, ax
  565.     mov    al, ega_rows
  566.     inc    ax
  567.     mov    word ptr es:[bx+10h], ax    ; character rows
  568.     mov    dx, ega_points
  569.     mul    dx
  570.     mov    word ptr es:[bx+0ch], ax    ; pixel rows
  571.     pop    ds
  572.     assume ds:CODE
  573.     xor    ax, ax
  574.     ret
  575. fn19fail:
  576.     mov    ax, 8003h    ; unknown mode, failure
  577.     ret
  578. setinfo: 
  579.     les    bx, es:[bx+19]    ; data packet to read
  580.     mov    dx, 80h        ; monochrome, don't clear screen
  581.     cmp    word ptr es:[bx+8], 0    ; set to monochrome?
  582.     je    setismono
  583.     inc    dx
  584. setismono:
  585.     mov    ax, es:[bx+0eh]    ; character columns
  586.     cmp    ax, 40        ; 40 columns?
  587.     je    fn1940col
  588.     cmp    ax, 80
  589.     jne    fn19fail
  590.     add    dx, 2        ; mode 2 or 3
  591. fn1940col:
  592.     mov    ax, es:[bx+10h]    ; character rows
  593.     cmp    ax, 25
  594.     je    fn1925lin
  595. IF VGA
  596.     cmp    ax, 50
  597.     je    fn1950lin
  598. ENDIF
  599.     cmp    ax, 43
  600.     jne    fn19fail
  601.  
  602. IF VGA
  603.     mov    ax, 1201h    ; 350 lines
  604.     jmp    short ln50join
  605. fn1950lin:
  606.     mov    ax, 1202h    ; 400 lines
  607. ln50join:
  608.     push    dx
  609.     mov    bl, 30h        ; set scan lines
  610.     int     10h
  611.     pop    ax        ; the mode
  612. ELSE
  613.     mov    ax, dx
  614. ENDIF
  615.     int    10h        ; set video mode
  616.  
  617.     mov    ax, 1112h    ; set 8x8 font
  618.     mov    bl, 0
  619.     int     10h
  620.  
  621.     mov    ax, 1200h    ; load new printscreen
  622.     mov    bl, 20h
  623.     int     10h
  624. IF EGA    ; set the EGA cursor
  625.     mov    dx, port_6845    ; '6845' command reg
  626.     mov    ax, 070ah    ; start on line 7
  627.     out    dx, ax
  628.     mov    al, 0bh        ; end on line 7
  629.     out    dx, ax
  630. ENDIF
  631.     jmp     short clrs
  632.  
  633. fn1925lin:
  634. IF VGA
  635.     push    dx
  636.     mov    ax, 1202h    ; select 400 scan lines
  637.     mov    bl,30h        ; this call ignored on EGA
  638.     int    10h
  639.     pop    ax
  640. ELSE
  641.     mov    ax, dx
  642. ENDIF
  643.     int    10h        ; set video mode
  644.  
  645. clrs:    push    ds        ; address abs40 area
  646.     mov    ax, ABS40
  647.     mov    ds, ax
  648.     ASSUME    ds:ABS40
  649.     mov    ax, 600h        ; clear display area
  650.     xor    cx, cx            ; upper left corner
  651.     mov    dx, crt_cols
  652.     dec    dl            ; right column
  653.     mov    dh, ega_rows        ; lower row
  654.     mov    bh, cs:cur_attrib    ; character attribute
  655.     int    10h
  656.     pop    ds
  657.     ASSUME    ds:CODE
  658.     xor    ax, ax            ; return success
  659.     ret
  660.  
  661. dosfn19    endp
  662. ENDIF
  663.     page
  664. if dos4
  665. ;--- new_mpx ------------------------------------------------
  666. ; This routine acknoledges that an ansi driver is installed.
  667. ;
  668.  
  669. new_mpx    proc
  670.     cmp    ax, 1a00h    ; is this for us?
  671.     jz    ackansi
  672.     jmp    dword ptr cs:old_mpx    ; no -- go to next handler
  673. ackansi:
  674.     mov    al, 0ffh    ; say we are installed
  675.     iret
  676. new_mpx endp
  677. endif
  678.     page
  679. ;--- new_vid_bios -------------------------------------------
  680. ; new_vid_bios takes the set cursor, get display mode, change mode, and
  681. ; get mode calls.
  682.  
  683. ; If bios_write_tty defined, new_vid_bios replaces the write_tty call.
  684. ; This gives BIOS ANSI capability.
  685. ; However, it takes away the escape character.
  686. ; If this is not desired, just tell init to not take over the vector.
  687. ; All other calls get sent to the old video bios.
  688.  
  689. new_vid_bios    proc
  690.     STI
  691. IF bios_write_tty
  692.     cmp    ah, 0Eh
  693.     je    nvb_write_tty
  694. ENDIF
  695.     cmp    Ah, 02h     ; set cursor position command?
  696.     je    nvb_setcursor
  697.     cmp    Ah,0        ; change mode command?
  698.     je    nvb_smode
  699.     cmp    cs:fmode,0    ; slow mode?
  700.     je    new_vid_pass    ; then pass it on
  701.     cmp    Ah, 0Fh     ; get display mode command?
  702.     je    nvb_display
  703.     cmp    Ah, 06h        ; clear screen command?
  704.     je    nvb_scroll
  705.     cmp    Ah, 07h        ; alternative cls command?
  706.     je    nvb_scroll
  707. IF VESA    
  708.     cmp    AX, 4f02h    ; set extended display mode?
  709.     je    nvb_smode
  710.     cmp    AX, 4f03h    ; get extended display mode?
  711.     je    nvb_display
  712. ENDIF
  713.  
  714. new_vid_pass:
  715.     jmp    dword ptr cs:old_vid_bios
  716. IF bios_write_tty
  717. ; WRITE TTY SUBCOMMAND
  718. nvb_write_tty:
  719.     cmp    cs:bmode, 0    ; don't write with our driver?
  720.     jz    new_vid_pass
  721.     push    cx        ; save register
  722.     mov    cl, cs:cur_attrib
  723.     ; If in graphics mode, BL is new color
  724.     cmp    cs:gmode_flag, 0
  725.     jle    nvb_wt_text
  726.     mov    cs:cur_attrib, bl    ; ja?
  727. nvb_wt_text:
  728.     int    29h
  729.     mov    cs:cur_attrib, cl
  730.     pop    cx
  731.     iret
  732. ENDIF
  733.  
  734. ; GET DISPLAY MODE SUBCOMMAND
  735. nvb_display:
  736.     cmp    cs:gmode_flag,0    ; Graphic mode? Mono?
  737.     jnz    new_vid_pass
  738.     push    ds
  739.     push    dx
  740.     mov    dx, ABS40
  741.     mov    ds, dx
  742.     assume    ds:ABS40
  743.     cmp    crt_start,0    ; At start of mem?
  744.     jz    nvb_pass
  745.     call    move_back
  746.     jmp    nvb_pass
  747.  
  748. ; SCROLL DISPLAY SUBCOMMAND
  749. nvb_scroll:
  750.     push    ds
  751.     push    dx
  752.     mov    dx, ABS40
  753.     mov    ds, dx
  754.     assume    ds:ABS40
  755.     mov    cs:no_c_flag, 1    ; if graphic, don't draw cursor afterwards
  756.     cmp    cs:gmode_flag,0    ; graphic mode?
  757.     jnz    nvb_pass
  758.     cmp    crt_start,0    ; not at start of mem
  759.     jz    nvb_pass
  760.     cmp    al, 0        ; scroll, not erase
  761.     jne    nvb_pass
  762.     or    cx, cx        ; not entire screen?
  763.     jne    nvb_pass
  764.     pop    dx
  765.     push    dx
  766.     inc    dl
  767.     cmp    dl, byte ptr crt_cols    ; same question, max columns
  768.     ja    nvb_pass        ; >size is full screen, though
  769.     cmp    dh, ega_rows        ; same question, max rows
  770.     ja    nvb_pass        ; >size is full screen, though
  771.     push    ax        ; erase is easier since we dont move screen
  772.     xor    ax,ax
  773.     mov    crt_start,0    ; reset offsets
  774.     mov    dx,port_6845
  775.     mov    al,0ch
  776.     out    dx,ax
  777.     inc    al
  778.     out    dx,ax
  779.     pop    ax
  780. ;    jmp    nvb_pass    ; jump is redundant
  781.  
  782. nvb_pass:
  783.     pop    dx
  784.     assume    ds:nothing
  785.     pop    ds
  786.     jmp    dword ptr cs:old_vid_bios    ; now doit
  787.  
  788. ; SET CURSOR SUBCOMMAND
  789. nvb_setcursor:
  790.     push    ds
  791.     push    dx
  792.     mov    dx, ABS40
  793.     mov    ds,dx
  794.     assume    ds:ABS40
  795.     cmp    cs:gmode_flag,0    ; Alpha mode?, Mono mode?
  796.     jle    nvb_pass
  797.     cmp    cs:no_c_flag, 0    ; inhibited cursor?
  798.     jnz    nvb_pass    ; then keep inhibited
  799.     cmp    cs:gcursor, 0    ; no cursor?
  800.     jz    nvb_pass    ; then don't want one now!
  801.     push_all
  802.     draw_gcursor
  803.     pop_all
  804.     assume    ds:nothing
  805.     pop    dx            ; restore registers
  806.     pop    ds
  807.     call_video            ; original int 10h
  808.     push_all
  809.     draw_gcursor            ; redraw the cursor
  810.     pop_all
  811.     iret                ; return from interrupt
  812.  
  813. ; SET DISPLAY MODE SUBCOMMAND
  814. nvb_smode:
  815.     call_video
  816.     push_all
  817.     push    ds
  818.     mov    dx, ABS40
  819.     mov    ds, dx
  820.     assume    ds:ABS40
  821.     mov    al, crt_mode    ; get mode and check for being graphic
  822.     call    set_gmode
  823.     mov    cs:no_c_flag, al ; if graphic, then no cursor is on screen.
  824.     pop    ds
  825.     assume    ds:nothing
  826.     pop_all
  827.     iret
  828. new_vid_bios    endp
  829.     page
  830. ;------ int_29 ----------------------------------------------
  831. ; Int 29 handles DOS quick-access putchar.
  832. ; Last device loaded with attribute bit 4 set gets accessed for
  833. ; single-character writes via int 29h instead of via interrupt.
  834. ; Must preserve all registers.
  835. ; Installed as int 29h by dosfn0 (init).
  836.  
  837. int_29    proc    near
  838.     sti
  839.     push    ds
  840.     push    es
  841.     push_all
  842. if fast29
  843.     cmp    al, 20h            ; control char?
  844.     jb    slow_way
  845.     cmp    cs:escvector, 0        ; middle of an escape sequence?
  846.     jnz    slow_way
  847.     mov    dx, ABS40
  848.     mov    ds, dx            ; set addressability
  849.     assume    ds:ABS40
  850.     mov    cx, word ptr crt_mode    ; mode in cl, columns in ch
  851.     push    ax
  852.     mov    al, cl
  853.     call    set_gmode        ; check for graphics mode
  854.     pop    ax
  855.     cmp    cs:gmode_flag, 0    ; slow if graphics
  856.     jg    slow_way
  857. ;    cmp    cl, 3            ; graphics mode?
  858. ;    ja    slow_way
  859.     xor    bx, bx            ; get cursor position
  860.     mov    bl, active_page
  861.     add    bx, bx
  862.     mov    dx, cursor_posn[bx]        ; dh has y, dl has x
  863.     inc    dl            ; point to next location
  864.     cmp    dl, ch            ; at edge?
  865.     jnb    slow_way
  866.                     ; we can go with it!
  867.     mov    cursor_posn[bx], dx    ; update pointer
  868.     xchg    ax, bx
  869.     mov    al, dh
  870.     mul    ch            ; ax has line offset
  871.     add    al, dl
  872.     adc    ah, 0            ; total offset
  873.     mov    cx, bx
  874.     mov    bx, ax            ; cl has character, bx offset
  875.  
  876.     mov    ax, crt_start
  877.     shr    ax, 1
  878.     add    bx, ax            ; corrected cursor offset, either
  879.                     ; because of fast scroll or
  880.                     ; page<>0
  881.  
  882. if MONO
  883.     mov    dx, addr_6845        ; update cursor location
  884. else
  885.     mov    dx, port_6845        ; update cursor location
  886. endif
  887.     mov    al,0eh        ; more effective to write two bytes at a time
  888.     mov    ah,bh
  889.     out    dx,ax
  890.     inc    al
  891.     mov    ah,bl
  892.     out    dx,ax
  893.  
  894.     mov    al, cl            ; get back character
  895.  
  896.     dec    bx
  897.     add    bx, bx            ; byte offset
  898.     mov    dx, 0b800h        ; address screen
  899. if MONO
  900.     cmp    crt_mode, 7
  901.     jne    int29_not_mono
  902.     mov    dh, 0b0h
  903. int29_not_mono:
  904. endif
  905.     mov    ds, dx
  906.     assume    ds:nothing
  907.     mov    ah, cs:cur_attrib
  908.     mov    ds:[bx], ax        ; write character
  909.     jmp    short int_fin
  910. endif
  911. slow_way:
  912.     mov    cx, 1
  913.     mov    bx, cs
  914.     mov    es, bx
  915.     mov    si, offset int_29_buf
  916.     mov    byte ptr es:[si], al
  917.     call    dosfn8
  918. int_fin:
  919.     pop_all
  920.     pop    es
  921.     pop    ds
  922.     iret
  923. int_29    endp
  924.  
  925.     page
  926. ;------ dosfn8 -------------------------------------------------------
  927. ; Handles writes to the device (with or without verify).
  928. ; Called with
  929. ;  CX     = number of bytes to write
  930. ;  ES:SI = transfer buffer
  931. ;  DS     = CS, so we can access local variables.  NOT ANY MORE
  932.  
  933. dosfn8    proc    near
  934.  
  935.     mov    cs:f_cptr_seg, es    ; save segment of char ptr
  936.  
  937.     ; Read the BIOS buffer address/cursor position variables.
  938.     mov    ax, ABS40
  939.     mov    ds, ax
  940.     assume    ds:ABS40
  941.  
  942.     ; Find current video mode and screen size.
  943.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  944.     mov    cs:video_mode, al
  945.     dec    ah            ; ah = max column
  946.     mov    cs:max_x, ah
  947.  
  948.     ; Save graphics mode flag
  949.     call    set_gmode
  950.  
  951.     mov    al, ega_rows        ; number of display rows
  952.     mov    cs:max_y, al        ; set maxy value
  953.  
  954.     ; Find current cursor coordinates.
  955.  
  956.     mov    al, active_page
  957.     cbw
  958.     add    ax, ax
  959.     xchg    bx, ax
  960.     mov    ax, cursor_posn[bx]
  961.     mov    cs:cur_coords, ax
  962.  
  963. if MONO
  964.     mov    ax, addr_6845        ; get the right 6845
  965.     mov    cs:our_6845, ax
  966. endif
  967.  
  968.     ; Find video buffer segment address; adjust it
  969.     ; so the offset is zero; return in AX.
  970.  
  971.     mov    ax, 0B800H        ; this gets corrected in xy_to_regs
  972.                     ; if we are not screen 0
  973. if MONO
  974.     cmp    crt_mode, 7
  975.     jne    not_mono_mode
  976.     mov    ah, 0b0h
  977. not_mono_mode:
  978. endif
  979.     push    cs
  980.     pop    ds
  981.     assume    ds:CODE
  982.     mov    es, ax
  983.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  984.  
  985.     ; | If in graphics mode, clear old pseudocursor
  986.  
  987.     cmp    gmode_flag, 0
  988.     jle    d8_no_cp
  989.     cmp    no_c_flag, 0    ; cursor not previously drawn?
  990.     mov    no_c_flag, 0    ; (reset flag, will be drawn from now on)
  991.     jnz    d8_no_cp    ; not drawn -- don't clear
  992.     cmp    gcursor, 0    ; don't clear if cursor is off, either
  993.     jz    d8_no_cp
  994.     push    cx
  995.     push    dx
  996.     push    di
  997.     draw_gcursor
  998.     pop    di
  999.     pop    dx
  1000.     pop    cx
  1001.  
  1002. d8_no_cp:
  1003.  
  1004.     mov    ah, cur_attrib
  1005.     mov    ds, f_cptr_seg        ; get segment of char ptr
  1006.     assume    ds:nothing
  1007.     cld                ; make sure we'll increment
  1008.  
  1009.     ; The Inner Loop: 12+4+4+11+14+2+19= 66 cycles/loop
  1010.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  1011.     ; At that speed, it takes 32 milliseconds to fill a screen.
  1012.  
  1013.     ; Get a character, put it on the screen, repeat 'til end of line
  1014.     ; or no more characters.
  1015.     jcxz    f_loopdone        ; if count = 0, we're already done.
  1016.     cmp    cs:escvector, 0        ; If in middle of an escape sequence,
  1017.     jz    f_tloop
  1018.     jmp    f_in_escape        ; jump to escape sequence handler.
  1019.  
  1020. f_tloop:; | If in graphics mode, jump to alternate loop
  1021.     ; | What a massive kludge!  A better approach would have been
  1022.     ; | to collect characters for a "write n chars" routine
  1023.     ; | which would handle both text and graphics modes.
  1024.     cmp    cs:gmode_flag,0
  1025.     jle    f_t_cloop
  1026.     jmp    f_g_cloop
  1027.  
  1028.     even                ; tiny performance boost
  1029. f_t_cloop:
  1030.     LODSB                ; get char! (al = ds:[si++])
  1031.     cmp    al, 28            ; is it a control char?
  1032.     jb    f_control        ;  maybe...
  1033. f_t_nctl:
  1034.     STOSW                ; Put Char! (es:[di++] = ax)
  1035.     dec    dx            ; count down to end of line
  1036.     loopnz    f_t_cloop        ; and go back for more.
  1037.     jz    f_t_at_eol        ; at end of line, maybe do a crlf.
  1038.     jmp    short f_loopdone    ; finished execution
  1039.  
  1040. f_looploop:
  1041. f_ansi_exit:                ; in case we switched into
  1042.     loopnz    f_tloop            ; a graphics mode
  1043.     jnz    f_loopdone
  1044. f_t_at_eol:
  1045.     jmp    f_at_eol
  1046.  
  1047. f_loopdone:
  1048.  
  1049.     ;--------- All done with write request -----------
  1050.     ; DI is cursor address, cursor position in cur_y, dl
  1051.  
  1052.     assume    ds:ABS40
  1053.     mov    ax, ABS40
  1054.     mov    ds, ax
  1055.  
  1056.     ; Set cursor position in low memory.
  1057.  
  1058.     mov    al,active_page
  1059.     cbw
  1060.     add    ax,ax
  1061.     xchg    bx,ax
  1062.     mov    al, cs:max_x
  1063.     inc    al
  1064.     sub    al, dl
  1065.     mov    ah, cs:cur_y
  1066.     mov    cursor_posn[bx],ax
  1067.  
  1068.     cmp    cs:gmode_flag,0
  1069.     jg    pseudocursor        ; In graphics mode, there is
  1070.                     ; a pseudo cursor to draw.
  1071.  
  1072.     ; Write directly to 6845 cursor address register.
  1073.     mov    bx, di
  1074.     shr    bx, 1            ; convert word index to byte index
  1075.  
  1076. if MONO
  1077.     mov    dx, cs:our_6845    ; either mono or color card
  1078. else
  1079.     mov    dx, port_6845    ; color card
  1080. endif
  1081.     mov    al,0eh        ; more effective to write two bytes at a time
  1082.     mov    ah,bh
  1083.     out    dx,ax
  1084.     inc    al
  1085.     mov    ah,bl
  1086.     out    dx,ax
  1087.  
  1088.  
  1089.     ; Return to DOS.
  1090.     xor    ax, ax            ; No error, not busy.
  1091.     ret
  1092.  
  1093. pseudocursor:
  1094.     cmp    cs:gcursor, 0    ; graphics cursor off?
  1095.     jz    nopseudo
  1096.     mov    cs:no_c_flag,0    ; there is a cursor now!
  1097.     draw_gcursor
  1098. nopseudo:
  1099.     xor    ax, ax
  1100.     ret
  1101.  
  1102.     ;---- handle control characters ----
  1103.     ; Note: cur_x is not kept updated in memory, but can be
  1104.     ; computed from max_x and dx.
  1105.     ; Cur_y is kept updated in memory.
  1106. f_escapex:                ; far jump
  1107.     jmp    f_escape
  1108.  
  1109. f_control:
  1110.     cmp    al, 27            ; Is it an escape?
  1111.     jz    f_escapex
  1112.     cmp    al, 13            ; carriage return?
  1113.     jz    f_cr
  1114.     cmp    al, 10            ; line feed?
  1115.     jz    f_lf
  1116.     cmp    al, 8            ; backspace?
  1117.     jz    f_bs
  1118.     cmp    al, 9            ; tab?
  1119.     jz    f_tab
  1120.     cmp    al, 7            ; bell
  1121.     jz    f_bell
  1122.     jmp    f_nctl            ; then it is not a control char.
  1123.  
  1124. f_bell: ;----- Handle bell ----------------------
  1125.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  1126.     call    beep
  1127.     or    al, al            ; clear z
  1128.     jmp    f_looploop        ; Let main loop decrement cx.
  1129.  
  1130. f_bs:    ;----- Handle backspace -----------------
  1131.     ; Moves cursor back one space without erasing.    No wraparound.
  1132.     cmp    dl, cs:max_x        ; wrap around to previous line?
  1133.     ja    fbs_wrap        ; yep; disallow it.
  1134.     dec    di            ; back up one char & attrib,
  1135.     dec    di
  1136.     inc    dx            ; and note one more char left on line.
  1137. fbs_wrap:
  1138.     jmp    f_looploop
  1139.  
  1140. f_cr:    ;----- Handle carriage return -----------
  1141.     ; di -= cur_x<<1;        set di= address of start of line
  1142.     ; dx=max_x+1;            set bx= chars left in line
  1143.     mov    al, cs:max_x
  1144.     inc    al
  1145.     sub    al, dl            ; Get cur_x into ax.
  1146.     mov    ah, 0
  1147.     sub    di, ax
  1148.     sub    di, ax
  1149.     mov    dl, cs:max_x        ; Full line ahead of us.
  1150.     inc    dx
  1151.     mov    ah, cs:cur_attrib    ; restore current attribute
  1152.     or    al, 1            ; clear z
  1153.     jmp    f_looploop        ; and let main loop decrement cx
  1154.  
  1155. f_at_eol:
  1156.     ;----- Handle overrunning right end of screen -------
  1157.     ; cx++;                compensate for double loop
  1158.     ; if (!wrap_flag) { dx++; di-=2; }
  1159.     ; else do_crlf;
  1160.     inc    cx
  1161.     test    cs:wrap_flag, 1
  1162.     jnz    feol_wrap
  1163.         dec    di
  1164.         dec    di
  1165.         inc    dx
  1166.         jmp    f_looploop
  1167. feol_wrap:
  1168.     ; dx=max_x+1;            set bx= chars left in line
  1169.     ; di -= 2*(max_x+1);
  1170.     ; do_lf
  1171.     mov    dl, cs:max_x
  1172.     inc    dx
  1173.     sub    di, dx
  1174.     sub    di, dx
  1175.     ; fall thru to line feed routine
  1176.  
  1177. f_lf:    ;----- Handle line feed -----------------
  1178.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  1179.     ; else { cur_y++; di += max_x<<1;    else increment Y
  1180.  
  1181.     mov    al, cs:max_y
  1182.     cmp    cs:cur_y, al
  1183.     jb    flf_noscroll
  1184.         call    scroll_up        ; preserves bx,cx,dx,si,di
  1185.         jmp    short flf_done
  1186. flf_noscroll:
  1187.     inc    cs:cur_y
  1188.     mov    al, cs:max_x
  1189.     mov    ah, 0
  1190.     inc    ax
  1191.     add    ax, ax
  1192.     add    di, ax
  1193. flf_done:
  1194.     mov    ah, cs:cur_attrib        ; restore current attribute
  1195.     or    al, 1            ; clear z
  1196.     jmp    f_looploop        ; and let main loop decrement cx
  1197.  
  1198. f_tab:    ;----- Handle tab expansion -------------
  1199.     ; Get cur_x into al.
  1200.     mov    al, cs:max_x
  1201.     inc    al
  1202.     sub    al, dl
  1203.     ; Calculate number of spaces to output.
  1204.     push    cx            ; save cx
  1205.     mov    ch, 0
  1206.     mov    cl, al            ; get zero based x coordinate
  1207.     and    cl, 7
  1208.     neg    cl
  1209.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  1210.     sub    dx, cx            ; update chars-to-eol, maybe set z
  1211.     pushf                ; || save Z for main loop
  1212.     ; ah is still current attribute.  Move CX spaces to the screen.
  1213.     mov    al, ' '
  1214.     cmp    cs:gmode_flag,0
  1215.     jg    f_tab_putc
  1216.  
  1217.     REP    STOSW
  1218.     popf                ; || restore Z flag for main loop test
  1219.     pop    cx            ; restore cx
  1220.     jmp    f_looploop        ; Let main loop decrement cx.
  1221.  
  1222. ;--------------- graphics mode support -----------------------
  1223.  
  1224. ;---- Alternate main loop for graphics mode ----
  1225. f_g_cloop:
  1226.     LODSB                ; get char! (al = ds:[si++])
  1227.     cmp    al, 28            ; is it a control char?
  1228.     jb    f_g_control        ;  maybe...
  1229. f_g_nctl:
  1230.     call    putchar
  1231.     dec    dx            ; count down to end of line
  1232.     loopnz    f_g_cloop        ; and go back for more.
  1233.     jz    f_at_eol        ; at end of line; maybe do a crlf.
  1234.     jmp    f_loopdone
  1235.  
  1236. f_g_control:    jmp    f_control
  1237.  
  1238. ; Tabs in graphic mode
  1239. f_tab_putc:    ; graphics mode- call putc to put the char
  1240.     add    dx, cx            ; move back to start of tab
  1241. f_tp_lp:
  1242.     call    putchar
  1243.     dec    dx            ; go to next cursor position
  1244.     loop    f_tp_lp
  1245.     popf                ; Z set if wrapped around EOL
  1246.     pop    cx
  1247.     jmp    f_looploop
  1248.  
  1249. ;---- Where to go when a character turns out not to be special
  1250. f_nctl:
  1251. f_not_ansi:
  1252.     cmp    cs:gmode_flag,0
  1253.     jg    f_g_nctl
  1254.     jmp    f_t_nctl        ; text mode
  1255.  
  1256. page
  1257. ;---- putchar ------------------------------------------------
  1258. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  1259. ; On entry, registers set up as per xy_to_regs.
  1260. ; Preserves all registers.
  1261. putchar proc    near
  1262.     push    dx
  1263.     push    cx
  1264.     push    bx
  1265.     push    ax
  1266.     ; 1. Set cursor position.
  1267.     mov    al, cs:max_x
  1268.     inc    al
  1269.     sub    al, dl
  1270.     mov    cs:cur_x, al
  1271.     mov    dx, cs:cur_coords    ; get X & Y into DX
  1272.     push    ds
  1273.     mov    ax, 40h
  1274.     mov    ds, ax
  1275.     assume    ds:ABS40
  1276.     mov    cursor_posn,dx
  1277.     pop    ds
  1278.     assume    ds:nothing
  1279.     xor    bx, bx            ; choose dpy page 0
  1280.     mov    ah, 2            ; chose "Set Cursor Position"
  1281.     call_video
  1282.     ; 2. Write char & attribute.
  1283. IF quick_char
  1284.     pop    ax
  1285.     push    ax            ; character and attribute
  1286.     call    quick_graph
  1287. ELSE
  1288.     mov    cx, 1
  1289.     pop    ax            ; get char in AL
  1290.     push    ax
  1291.     mov    bl, ah            ; attribute in BL
  1292.     mov    bh, 0
  1293.     mov    ah, 9
  1294.     call_video
  1295. ENDIF
  1296.     pop    ax
  1297.     pop    bx
  1298.     pop    cx
  1299.     pop    dx
  1300.     ret
  1301. putchar endp
  1302. page
  1303. IF quick_char
  1304. quick_graph    proc    near
  1305. ; this code has been reworked for much greater speed.
  1306.  
  1307. ; ah= mode, al= char, ax,bx,cx,dx destroyed
  1308.     gmode_test yesQuick
  1309.  
  1310.     mov    bl,ah
  1311.     xor    bh,bh
  1312.     mov    cx, 1
  1313.     mov    ah, 9
  1314.     call_video        ; do it the old way
  1315.     ret
  1316.  
  1317. yesQuick:
  1318.     push    ds
  1319.     mov    bx, 40h
  1320.     mov    ds, bx
  1321.     assume    ds:ABS40        ; address abs segment
  1322.     push    es
  1323.     push    bp
  1324.     push    si
  1325.     push    di            ; save some registers
  1326.     push    ax            ; save char and mode
  1327.     
  1328.     mov    ax, crt_cols
  1329.     mov    cx, ega_points        ; pixel rows in character
  1330.     mov    bp, ax            ; save number of columns=#bytes
  1331.     mul    byte ptr (cursor_posn+1)
  1332.     mul    cx            ; (ignore upper product in DX)
  1333.     add    al, byte ptr (cursor_posn)    ; y*#cols*#rows + x
  1334.     adc    ah, 0            ; take care of carry
  1335.     mov    si, ax            ; save address in si
  1336.     xor    ax, ax
  1337.     mov    es, ax            ; absolute zero
  1338.     les    di, es: dword ptr (43h * 4)    ; contents of vector 43h
  1339.     pop    ax
  1340.     push    ax            ; get char and mode
  1341.     mul    cl            ; offset to character in table
  1342.     add    di,ax            ; di has character bit pattern start
  1343.     mov    ax, 0a000h        ; address of display segment
  1344.     mov    ds, ax
  1345.     assume    ds:nothing
  1346.  
  1347. ; to recap: cx=#rows, bp=#columns, ds:si=display address, es:di=character addr
  1348.     mov    dx, 3ceh
  1349.     mov    ax, 0a05h
  1350.     out    dx,ax            ; set write mode 2, read mode 1
  1351.  
  1352.     mov    ax, 7            ; set color dontcare register to zero
  1353.     out    dx,ax
  1354.  
  1355.     pop    bx            ; character mode in bh
  1356. IF gbackground
  1357.     mov    bl,bh            ; extract background color
  1358. IF cheap_pc
  1359.     shr    bl,1
  1360.     shr    bl,1
  1361.     shr    bl,1
  1362.     shr    bl,1
  1363. ELSE
  1364.     shr    bl,4
  1365. ENDIF
  1366.     or    bh, bh
  1367.     jns    overMode
  1368.     mov    ax, 1803h        ; exor mode
  1369.     out    dx,ax
  1370.  
  1371.     and    bx, 0f07h        ; xor=blink bit
  1372. ELSE
  1373.     or    bh, bh
  1374.     jns    overMode
  1375.     mov    ax, 1803h        ; exor mode
  1376.     out    dx,ax
  1377.  
  1378.     and    bx, 7f00h        ; mask off xor bit
  1379. ENDIF
  1380.     mov    al, 8            ; bit mask register
  1381.     out    dx, al
  1382.     inc    dx
  1383. chLoop:
  1384.     mov    al, es:[di]        ; get pixel pattern
  1385.     out    dx, al
  1386.     and    [si],bh            ; update foreground
  1387.     not    al
  1388.     out    dx, al                 ; and background
  1389.     and    [si],bl
  1390.     inc    di
  1391.     add    si, bp            ; go to next character byte and line
  1392.     loop    chLoop
  1393.     
  1394. joinret:
  1395.     dec    dx
  1396.     mov    ax, 0ff08h        ; bit mask
  1397.     out    dx, ax
  1398.     mov    ax, 5            ; mode register
  1399.     out    dx, ax
  1400.     mov    al, 3            ; (ah is zero)
  1401.     out    dx, ax
  1402.     mov    ax, 0f07h
  1403.     out    dx, ax
  1404.  
  1405.     pop    di
  1406.     pop    si
  1407.     pop    bp
  1408.     pop    es
  1409.     pop    ds
  1410.     ret
  1411.  
  1412. overMode:
  1413. IF gbackground
  1414.     and    bx, 0f07h        ; xor=blink bit
  1415. ELSE
  1416.     and    bx, 7f00h        ; mask off xor bit
  1417. ENDIF
  1418.     mov    al, 8            ; bit mask register
  1419.     out    dx, al
  1420.     inc    dx
  1421.     ; we need to load the internal buffer with a solid
  1422.     ; background. By writing a solid background and then
  1423.     ; reading it back, we can do the job.
  1424.     mov    al, 0ffh        ; force set background
  1425.     out    dx, al
  1426.     mov    [si], bl
  1427.     mov    al, [si]        ; read reset pattern
  1428. chLoop2:
  1429.     mov    al, es:[di]        ; get pixel pattern
  1430.     out    dx, al
  1431.     mov    [si],bh            ; update foreground
  1432.     inc    di
  1433.     add    si, bp            ; go to next character byte and line
  1434.     loop    chLoop2
  1435.     jmp    joinret
  1436.  
  1437. quick_graph endp
  1438. ENDIF
  1439.  
  1440.  
  1441. ;--------------- end of graphics mode support --------------------
  1442.  
  1443. dosfn8    endp
  1444. page
  1445. ;--- get_blank_attrib ------------------------------------------------
  1446. ; Determine new attribute and character for a new blank region.
  1447. ; Use current attribute, just disallow blink and underline.
  1448. ; Returns result in AH, preserves all other registers.
  1449. get_blank_attrib    proc    near
  1450. IF gbackground
  1451.     cmp    cs:gmode_flag,0
  1452.     jle    get_attrib        ; if alpha mode
  1453.     gmode_test get_attribg        ; or good graphic mode, get attrib
  1454.     xor    ah,ah
  1455.     ret
  1456. get_attribg:
  1457.     mov    ah, cs:cur_attrib    ; must do different technique
  1458. IF cheap_pc
  1459.     shr    ah,1            ; color must be shifted into position
  1460.     shr    ah,1
  1461.     shr    ah,1
  1462.     shr    ah,1
  1463. ELSE
  1464.     shr    ah,4
  1465. ENDIF
  1466.     and    ah,07
  1467.     ret
  1468. get_attrib:
  1469.     mov    ah, cs:cur_attrib    ; the attribute
  1470.     and    ah, 7fh            ;  but disallowing blink
  1471. IF MONO
  1472.     cmp    cs:video_mode, 7    ; monochrome?
  1473.     jne    gb_aok
  1474.     cmp    ah, 7            ; underline?
  1475.     jnz    gb_aok
  1476.     mov    ah, 7
  1477. gb_aok:
  1478. ENDIF
  1479.     ret
  1480. ELSE
  1481.     mov    ah, 0            ; 0 is background if graphics mode
  1482.     cmp    cs:gmode_flag,0
  1483.     jg    gb_aok
  1484.  
  1485.     mov    ah, cs:cur_attrib
  1486.     and    ah, 7fh            ; disallow blink
  1487. IF MONO
  1488.     cmp    cs:video_mode, 7    ; monochrome?
  1489.     jne    gb_aok
  1490.     cmp    ah, 7            ; underline?
  1491.     jnz    gb_aok
  1492.     mov    ah, 7
  1493. ENDIF
  1494. gb_aok: ret
  1495. ENDIF
  1496. get_blank_attrib    endp
  1497.  
  1498. page
  1499. ;---- scroll_up ---------------------------------------------------
  1500. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  1501. ; Moves screen up 1 line, fills the last line with blanks.
  1502. ; Attribute of blanks is the current attribute sans blink and underline.
  1503.  
  1504. scroll_up    proc    near
  1505.     push_all
  1506.  
  1507.     cmp    cs:gmode_flag,0
  1508.     jz    scroll_char
  1509. if MONO
  1510.     jl    mono_scroll
  1511. endif
  1512.     jmp    scroll_graphic
  1513. scroll_char:
  1514.     push    es
  1515.     push    ds            ; save all!
  1516.     mov    ax, ABS40        ; address low mem via ds
  1517.     mov    ds, ax
  1518.     mov    ax, 0b800h        ; address display via es
  1519.     mov    es, ax
  1520.     assume    ds:ABS40
  1521.     cmp    cs:fmode,0        ; see if in fast mode
  1522.     jz    slow_scroll_up
  1523.     xor    ax,ax            ; calc addresses
  1524.     mov    al, cs:max_x
  1525.     inc    ax
  1526.     mov    cx, ax            ; save (word) count for fill
  1527.     mov    bx, ax            ; and save byte count
  1528.     shl    bx, 1            ; byte count
  1529.     mov    cs:temp_val, bx
  1530.     mul    cs:max_y        ; address offset of last line (words)
  1531.     shl    ax, 1            ; address offset in bytes
  1532.     mov    di, ax
  1533.     
  1534.     mov    ax, crt_start        ; start of display
  1535.     add    ax, bx            ; add line size in bytes
  1536.     add    di, ax            ; di is now address of new last line
  1537.     cmp    di, 7fffh - 264        ; is there room here?
  1538.     ja    no_room_here
  1539.  
  1540.     mov    crt_start, ax
  1541.     shr    ax, 1            ; make into word offset
  1542.     mov    bx, ax            ; and put into 6845
  1543.     mov    dx, port_6845
  1544.     mov    al, 0ch
  1545.     out    dx, ax
  1546.     inc    al
  1547.     mov    ah, bl
  1548.     out    dx, ax
  1549.     
  1550.     mov    ah, cs:cur_attrib
  1551.     and    ah, 7fh            ; disallow blink
  1552.     mov    al, 20h            ; blank
  1553.     rep stosw            ; clear line
  1554.  
  1555.     assume    ds:nothing
  1556.     pop    ds
  1557.     pop    es
  1558.     pop_all
  1559.     
  1560.     add    di, cs:temp_val
  1561.     ret
  1562.  
  1563. no_room_here:
  1564.     pop    ds            ; restore registers
  1565.     pop    es
  1566.     pop_all
  1567.     call    move_back        ; go to buffer start
  1568.     sub    di, cs:temp_val
  1569.     jmp    scroll_up        ; try again
  1570.  
  1571. if MONO
  1572. mono_scroll:
  1573.     push    es
  1574.     push    ds            ; save all!
  1575.     mov    ax, ABS40        ; address low mem via ds
  1576.     mov    ds, ax
  1577.     mov    ax, 0b000h        ; address display via es
  1578.     mov    es, ax
  1579. endif
  1580. slow_scroll_up:
  1581.     assume    ds:ABS40
  1582.     mov    di, crt_start        ; offset of display (because of
  1583.                     ;   different page)
  1584.     mov    ds, ax            ; ds is now display
  1585.     assume    ds:nothing
  1586.     xor    ax,ax            ; calc addresses
  1587.     mov    al, cs:max_x
  1588.     inc    ax
  1589.     mov    bx, ax            ; save (word) count
  1590.     shl    ax, 1            ; byte count
  1591.     mov    si, ax            ; start address is second line
  1592.     add    si, di            ; adjust start address by any offset
  1593.     mov    ax, bx
  1594.     mul    cs:max_y        ; number of words to move
  1595.     mov    cx, ax
  1596.     rep movsw            ; move them!
  1597.     mov    cx, bx            ; words to clear
  1598.           mov    ah, cs:cur_attrib
  1599.     and    ah, 7fh            ; disallow blink
  1600.     mov    al, 20h            ; blank
  1601.     rep stosw            ; clear line
  1602.     pop    ds
  1603.     pop    es
  1604.     pop_all
  1605.     ret
  1606.  
  1607. scroll_graphic:
  1608.     
  1609.     gmode_test scrOurself
  1610.     mov    bh, 0
  1611.     mov    al, 1            ; AL is number of lines to scroll.
  1612.     mov    ah, 6            ; BIOS: scroll up
  1613.     xor    cx, cx
  1614.     mov    dl, cs:max_x        ; lower-rite-x
  1615.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  1616.     call_video            ; call BIOS to scroll a rectangle.
  1617.  
  1618. scrret:
  1619.     pop_all
  1620.     ret
  1621.  
  1622. scrOurself:    ; try scrolling screen ourself!
  1623.     push    es
  1624.     push    ds
  1625.  
  1626.     mov    dx, 3ceh        ; set write mode 1
  1627.     mov    ax, 105h
  1628.     out    dx, ax
  1629.  
  1630.     mov    ax, 40h            ; address abs40 segment
  1631.     mov    ds, ax
  1632.     assume    ds:ABS40
  1633.     mov    ax, crt_cols        ; calculate length of line in bytes
  1634.     mul    byte ptr ega_points
  1635.     mov    si, ax            ; source of move
  1636.     xor    dx,dx
  1637.     mov    dl, ega_rows
  1638.     mul    dx            ; number of bytes to move
  1639.     mov    cx, ax
  1640.     mov    ax, si            ; save bytes in line for later
  1641.  
  1642.     mov    bx, 0a000h        ; address display
  1643.     mov    ds, bx
  1644.     mov    es, bx
  1645.  
  1646.     xor    di, di            ; destination of move
  1647.     rep movsb            ; scroll
  1648.     
  1649.     mov    cx, ax            ; bytes in line = bytes to clear
  1650.  
  1651.     mov    dx, 3ceh
  1652.     mov    ax, 05h            ; return to write mode 0
  1653.     out    dx, ax
  1654.  
  1655. IF gbackground
  1656.     mov    ah, cs:cur_attrib
  1657. IF cheap_pc
  1658.     shr    ah,1
  1659.     shr    ah,1
  1660.     shr    ah,1
  1661.     shr    ah,1
  1662. ELSE
  1663.     shr    ah,4
  1664. ENDIF
  1665.     and    ah,07            ; background color
  1666.     mov    al,0
  1667.     out    dx,ax            ; set color to write
  1668.  
  1669.     mov    ax,0f01h        ; set mask
  1670.     out    dx,ax
  1671.  
  1672.     rep    stosb            ; clear the line
  1673.  
  1674.     mov    ax,0001            ; reset mask
  1675.     out    dx,ax
  1676. ELSE
  1677.     xor    ax, ax
  1678.     rep stosb            ; clear the line
  1679. ENDIF
  1680.  
  1681.     pop    ds            ; restore registers and return
  1682.     pop    es
  1683.     jmp    scrret
  1684.  
  1685. scroll_up    endp
  1686. page
  1687. ;-----move_back --------------------------------------------
  1688. ; This routine moves the display to offset zero.
  1689. ; alters:
  1690. ; cs:temp_val = original crt_start value
  1691. ; crt_start = 0
  1692. ; controller reset properly
  1693. move_back proc near
  1694.     push    ds
  1695.     push    es
  1696.     push_all
  1697.     mov    ax, ABS40
  1698.     mov    ds, ax
  1699.  
  1700.     assume    ds:ABS40
  1701.     mov    al, ega_rows
  1702.     inc    al
  1703.     mul    byte ptr crt_cols    ; words to move
  1704.     mov    cx, ax
  1705.     mov    si, crt_start
  1706.     mov    cs:temp_val, si        ; save this value
  1707.     xor    di, di
  1708.     mov    crt_start, di
  1709.     mov    bx, cursor_posn        ; y in bh, x in bl
  1710.     mov    al, byte ptr crt_cols
  1711.     mul    bh
  1712.     add    al, bl
  1713.     adc    ah, 0
  1714.     xchg    bx, ax            ; save cursor position in bx
  1715.     
  1716.     mov    ax, 0B800h
  1717.     mov    es, ax
  1718.     mov    ds, ax
  1719.  
  1720.     mov    dx, cx
  1721.     add    dx, cx            ; see if overlapping
  1722.     cmp    dx, si
  1723.     ja    slow_move
  1724. join_move:
  1725.     cld
  1726.     rep movsw            ; move data
  1727.     
  1728.     mov    dx, port_6845
  1729.     mov    al, 0ch            ; reset offset
  1730.     xor    ah,ah
  1731.     out    dx, ax
  1732.     inc    al
  1733.     out    dx, ax
  1734.     inc    al
  1735.     mov    ah, bh
  1736.     out    dx, ax
  1737.     inc    al
  1738.     mov    ah, bl
  1739.     out    dx, ax
  1740. ;    sti
  1741.     assume    ds:nothing
  1742.     pop_all
  1743.     pop    es
  1744.     pop    ds
  1745.     ret
  1746.  
  1747. slow_move:    ; we gotta move to another spot first
  1748.     push    cx        ; save length
  1749.     dec    dx        ; length-2
  1750.     dec    dx
  1751.     add    si, dx        ; point to end
  1752.     mov    di, 7FFEh    ; safe location -- as safe as we can get
  1753.     std
  1754.     rep movsw        ; move from far end in case of overlap
  1755.                 ; (may happen on large displays)
  1756.     mov    dx, port_6845
  1757.     mov    si, di        ; source becomes destination
  1758.     inc    si        ; take care of last decrement
  1759.     inc    si
  1760.     mov    cx, si
  1761.     shr    cx, 1        ; word offset to start of new area
  1762.     mov    al, 0Ch        ; display at this new location
  1763.     mov    ah, ch
  1764.     out    dx, ax
  1765.     inc    al
  1766.     mov    ah, cl
  1767.     out    dx, ax
  1768.     pop    cx        ; reset all registers
  1769.     xor    di, di        ; destination is zero
  1770.     jmp    join_move    ; NOW move to destination
  1771.     
  1772. move_back    endp
  1773. page
  1774. if key_redef
  1775. ;---- lookup -----------------------------------------------
  1776. ; Called by getchar, peekchar, and key to see if a given key has
  1777. ; been redefined.
  1778. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  1779. ; Returns with Z cleared if no redefinition; otherwise,
  1780. ; Z is set, SI points to redefinition string, CX is its length.
  1781. ; Preseves AL, all but CX and SI.
  1782. ; Redefinition table organization:
  1783. ;  Strings are stored in reversed order, first char last.
  1784. ;  The word following the string is the character to be replaced;
  1785. ;  the next word is the length of the string sans header.
  1786. ; param_end points to the last byte used by the parameter buffer;
  1787. ; redef_end points to the last word used by the redef table.
  1788.  
  1789. lookup    proc    near
  1790.     mov    si, redef_end        ; Start at end of table, move down.
  1791.     or    al, al
  1792.     jz    lu_lp
  1793.     mov    ah, 0            ; clear extraneous scan code
  1794. lu_lp:    cmp    si, param_end
  1795.     jbe    lu_notfound        ; If below redef table, exit.
  1796.     mov    cx, [si]
  1797.     cmp    ax, [si-2]        ; are you my mommy?
  1798.     jz    lu_gotit
  1799.     sub    si, 4
  1800.     sub    si, cx            ; point to next header
  1801.     jmp    lu_lp
  1802. lu_notfound:
  1803.     or    si, si            ; clear Z
  1804.     jmp    short lu_exit
  1805. lu_gotit:
  1806.     sub    si, 2
  1807.     sub    si, cx            ; point to lowest char in memory
  1808.     cmp    al, al            ; set Z
  1809. lu_exit:
  1810.     ret
  1811. lookup    endp
  1812. endif
  1813. page
  1814. ;---- searchbuf --------------------------------------------
  1815. ; Called by getchar and peekchar to see if any characters are
  1816. ; waiting to be gotten from sources other than BIOS.
  1817. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  1818. searchbuf    proc    near
  1819.     ; Search the stuffahead buffers.
  1820. if key_redef
  1821.     mov    cx, 4            ; number of buffers to check for chars
  1822. else
  1823.     mov    cx, 3
  1824. endif
  1825.     mov    bx, offset fnkey - 4
  1826. sbloop: add    bx, 4            ; point to next buffer record
  1827.     mov    si, [bx].len
  1828.     or    si, si            ; empty?
  1829.     loopz    sbloop            ; if so, loop.
  1830.     ret
  1831. searchbuf    endp
  1832. page
  1833. ;---- getchar -----------------------------------------------
  1834. ; Returns AL = next char.
  1835. ; Trashes AX, BX, CX, BP, SI.
  1836. getchar proc    near
  1837. gc_searchbuf:
  1838.     ; See if any chars are waiting in stuffahead buffers.
  1839.     call    searchbuf
  1840.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  1841.     ; A nonempty buffer was found.
  1842.     dec    [bx].len
  1843.     dec    si
  1844.     mov    bp, [bx].adr        ; get pointer to string
  1845.     mov    al, byte ptr ds:[bp][si]; get the char
  1846.     ; Recognize function key sequences, move them to highest priority
  1847.     ; queue.
  1848.     sub    si, 1            ; set carry if si=0
  1849.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  1850.     cmp    bx, offset fnkey
  1851.     jz    gc_nofnkey        ; already highest priority -> done.
  1852.     or    al, al
  1853.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  1854.     ; Found a function key; move it to highest priority queue.
  1855.     dec    [bx].len
  1856.     mov    ah, byte ptr ds:[bp][si]; gec      [bx].len
  1857.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  1858. gc_fnkey:
  1859.     mov    fnkey.len, 1
  1860.     mov    fnkeybuf, ah        ; save it.
  1861. gc_nofnkey:
  1862.     ; Valid char in AL.  Return with it.
  1863.     jmp    short gcdone
  1864.  
  1865. gc_trykbd:
  1866.     ; Actually get a character from the keyboard.
  1867.     mov    ah, 0
  1868.     int    16h            ; BIOS returns with char in AX
  1869.     ; If it's Ctrl-break, it has already been taken care of.
  1870.     or    ax, ax
  1871.     jz    gc_trykbd
  1872.  
  1873. if key_redef
  1874.     ; Look in the reassignment table to see if it needs translation.
  1875.     call    lookup            ; Z=found; CX=length; SI=ptr
  1876.     jnz    gc_noredef
  1877.     ; Okay; set up the reassignment, and run thru the translation code.
  1878.     mov    xlatseq.len, cx
  1879.     mov    xlatseq.adr, si
  1880.     jmp    gc_searchbuf
  1881. endif
  1882. gc_noredef:
  1883.     ; Is it a function key?
  1884.     cmp    al, 0
  1885.     jz    gc_fnkey        ; yep- special treatment.
  1886. gcdone: ret    ; with character in AL.
  1887.  
  1888. getchar endp
  1889. page
  1890. ;---- peekchar -----------------------------------------------
  1891. ; Returns Z if no character ready, AL=char otherwise.
  1892. ; Trashes AX, BX, CX, BP, SI.
  1893. peekchar    proc    near
  1894.     call    searchbuf
  1895.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1896.     ; A nonempty buffer was found.
  1897.     dec    si
  1898.     mov    bp, [bx].adr        ; get pointer to string
  1899.     mov    al, byte ptr ds:[bp][si]; get the char
  1900.     ; Valid char from buffer in AL.     Return with it.
  1901.     jmp    short pcdone
  1902. pc_trykbd:
  1903.     ; Actually peek at the keyboard.
  1904.     mov    ah, 1
  1905.     int    16h            ; BIOS returns with char in AX
  1906.     jz    pcexit
  1907.     ; If it's control-break, it's already been taken care of.
  1908.     or    ax, ax
  1909.     jnz    pc_notbrk
  1910.     mov    ah, 0
  1911.     int    16h            ; so get rid of it!
  1912.     jmp    short pc_trykbd
  1913. pc_notbrk:
  1914. if key_redef
  1915.     ; Look in the reassignment table to see if it needs translation.
  1916.     call    lookup            ; Z=found; CX=length; SI=ptr
  1917.     jnz    pcdone            ; Nope; just return the char.
  1918.     ; Okay; get the first code to be returned.
  1919.     add    si, cx
  1920.     mov    al, [si-1]
  1921. endif
  1922. pcdone: or    ah, 1            ; NZ; char ready!
  1923. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1924. peekchar    endp
  1925. page
  1926. ;----- set_gmode ------------------------------------------------
  1927. ; Set gmode_flag based on mode byte in register al
  1928. set_gmode proc  near
  1929. if MONO
  1930.     cmp    al, 7            ; mono?
  1931.     je    gmode_mono
  1932.     gmode_code
  1933.     ret
  1934. gmode_mono:
  1935.     neg    al
  1936.     mov    cs:gmode_flag, al    ; set gmode_flag to neg value
  1937.     ret
  1938. else
  1939.     gmode_code            ; a macro in nnansi_d.asm
  1940.     ret
  1941. endif
  1942. set_gmode    endp
  1943.  
  1944.  
  1945. ;---- beep ------------------------------------------------------
  1946. ; Beep speaker; period given by beep_div, duration by beep_len.
  1947. ; Preserves all registers.
  1948.  
  1949. beep_div    equ    1300        ; fairly close to IBM beep
  1950. beep_len    equ    3        ; 3/18 sec- shorter than IBM
  1951.  
  1952. beep    proc    near
  1953.     push_all
  1954.  
  1955.     mov    al, 10110110b        ; select 8253
  1956.     mov    dx, 43h            ; control port address
  1957.     out    dx, al
  1958.     dec    dx            ; timer 2 address
  1959.     mov    ax, beep_div
  1960.     jmp    $+2
  1961.     out    dx, al            ; low byte of divisor
  1962.     xchg    ah, al
  1963.     jmp    $+2
  1964.     out    dx, al            ; high byte of divisor
  1965.     mov    dx, 61h
  1966.     jmp    $+2
  1967.     in    al, dx            ; get current value of control bits
  1968.     push    ax
  1969.     or    al, 3
  1970.     jmp    $+2
  1971.     out    dx, al            ; turn speaker on
  1972.  
  1973.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1974.     push    es
  1975.     mov    ax, ABS40
  1976.     mov    es, ax
  1977.     assume    es:ABS40
  1978.     mov    bx, timer_low
  1979.     mov    cx, -1
  1980. beeplp: mov    ax, timer_low
  1981.     sub    ax, bx
  1982.     cmp    ax,     beep_len
  1983.     jg    beepover
  1984.     loop    beeplp
  1985. beepover:
  1986.     pop    es
  1987.     assume    es:CODE
  1988.  
  1989.     ; Turn off speaker
  1990.     pop    ax
  1991.     and    al, not 3        ; turn speaker off
  1992.     out    dx, al
  1993.     pop_all
  1994.     ret
  1995. beep    endp
  1996.  
  1997.     page
  1998. ; A state machine implementation of the mechanics of ANSI terminal control
  1999. ; string parsing.
  2000. ;
  2001. ; Entered with a jump to f_escape when driver finds an escape, or
  2002. ; to f_in_escape when the last string written to this device ended in the
  2003. ; middle of an escape sequence.
  2004. ;
  2005. ; Exits by jumping to f_ANSI_exit when an escape sequence ends, or
  2006. ; to f_not_ANSI when a bad escape sequence is found, or (after saving state)
  2007. ; to f_loopdone when the write ends in the middle of an escape sequence.
  2008. ;
  2009. ; Parameters are stored as bytes in param_buffer.  If a parameter is
  2010. ; omitted, it is stored as zero.  Each character in a keyboard reassignment
  2011. ; command counts as one parameter.
  2012. ;
  2013. ; When a complete escape sequence has been parsed, the address of the
  2014. ; ANSI routine to call is found in ansi_fn_table.
  2015. ;
  2016. ; Register usage during parsing:
  2017. ;  DS:SI points to the incoming string.
  2018. ;  CX holds the length remaining in the incoming string.
  2019. ;  ES:DI points to the current location on the memory-mapped screen.
  2020. ;  DX is number of characters remaining on the current screen line.
  2021. ;  BX points to the current paramter byte being assembled from the incoming
  2022. ;  string.  (Stored in cur_parm_ptr between device driver calls, if needed.)
  2023. ;
  2024. ; The registers are set as follows before calling the ANSI subroutine:
  2025. ;  AX = max(1, value of first parameter)
  2026. ;  CX = number of paramters
  2027. ;  SI = offset of second parameter from CS
  2028. ;  DS = CS
  2029. ;  ES:DI points to the current location on the memory-mapped screen.
  2030. ;  DX is number of characters remaining on the current screen line.
  2031. ; The subroutine is free to trash AX, BX, CX, SI, and DS.
  2032. ; It must preserve ES, and can alter DX and DI if it wants to move the
  2033. ; cursor.
  2034. ;
  2035. ;------------------------------------------------------------------------
  2036.  
  2037.     assume    cs:code, ds:code
  2038.  
  2039. ;----- next_is -------------------------------------------------------
  2040. ; Next_is is used to advance to the next state.     If there are characters
  2041. ; left in the input string, we jump immediately to the new state;
  2042. ; otherwise, we shut down the recognizer, and wait for the next call
  2043. ; to the device driver.
  2044. next_is macro    statename
  2045.     loop    statename
  2046.     mov    ax, offset statename
  2047.     jmp    sleep
  2048.     endm
  2049.  
  2050. ;----- sleep --------------------------------------------------------
  2051. ; Remember bx and next state, then jump to device driver exit routine.
  2052. ; Device driver will re-enter at f_in_escape upon next invocation
  2053. ; because escvector is nonzero; parsing will then be resumed.
  2054. sleep:    mov    cs:cur_parm_ptr, bx
  2055.     mov    cs:escvector, ax
  2056.     jmp    f_loopdone
  2057.  
  2058. ;----- f_in_escape ---------------------------------------------------
  2059. ; Main loop noticed that escvector was not zero.
  2060. ; Recall value of BX saved when sleep was jumped to, and jump into parser.
  2061. f_in_escape:
  2062.     mov    bx, cs:cur_parm_ptr
  2063.     jmp    word ptr cs:escvector
  2064.  
  2065. fbr_syntax_error_gate:        ; jumped to from inside f_bracket
  2066.     jmp    syntax_error
  2067.  
  2068. ;----- f_escape ------------------------------------------------------
  2069. ; We found an escape.  Next character should be a left bracket.
  2070. f_escape:
  2071.     next_is f_bracket
  2072.  
  2073. ;----- f_bracket -----------------------------------------------------
  2074. ; Last char was an escape.  This one should be a [; if not, print it.
  2075. ; Next char should begin a parameter string.
  2076. f_bracket:
  2077.     lodsb
  2078.     cmp    al, '['
  2079.     jnz    fbr_syntax_error_gate
  2080.     ; Set up for getting a parameter string.
  2081.     mov    bx, cs:param_buffer
  2082.     mov    byte ptr cs:[bx], 0
  2083.     mov    cs:in_num, 0
  2084.     mov    cs:eat_key, 0        ; no eaten key
  2085.     next_is f_get_args
  2086.  
  2087. ;----- f_get_args ---------------------------------------------------
  2088. ; Last char was a [.  If the current char is a '=' or a '?', eat it.
  2089. ; In any case, proceed to f_get_param.
  2090. ; This is only here to strip off the strange chars that follow [ in
  2091. ; the SET/RESET MODE escape sequence.
  2092. f_get_args:
  2093.     lodsb
  2094.     cmp    al, '='
  2095.     jz    fga_ignore
  2096.     cmp    al, '?'
  2097.     jz    fga_ignore
  2098.         dec    si        ; let f_get_param fetch al again
  2099.         jmp    short f_get_param
  2100. fga_ignore:
  2101.     mov    cs:eat_key, al        ; save eaten key (BUG FIX!)
  2102.     next_is f_get_param
  2103.  
  2104. ;----- f_get_param ---------------------------------------------------
  2105. ; Last char was one of the four characters "]?=;".
  2106. ; We are getting the first digit of a parameter, a quoted string,
  2107. ; a ;, or a command.
  2108. f_get_param:
  2109.     lodsb
  2110.     cmp    al, '0'
  2111.     jb    fgp_may_quote
  2112.     cmp    al, '9'
  2113.     ja    fgp_may_quote
  2114.         ; It's the first digit.     Initialize current parameter with it.
  2115.         sub    al, '0'
  2116.         mov    byte ptr cs:[bx], al
  2117.         mov    cs:in_num, 1    ; set flag for sensing at cmd exec
  2118.         next_is f_in_param
  2119. fgp_may_quote:
  2120.     cmp    al, '"'
  2121.     jz    fgp_isquote
  2122.     cmp    al, "'"
  2123.     jnz    fgp_semi_or_cmd        ; jump to code shared with f_in_param
  2124. fgp_isquote:
  2125.     mov    cs:string_term, al    ; save it for end of string
  2126.     next_is f_get_string        ; and read string into param_buffer
  2127.  
  2128. ;----- f_get_string -------------------------------------
  2129. ; Last character was a quote or a string element.
  2130. ; Get characters until ending quote found.
  2131. f_get_string:
  2132.     lodsb
  2133.     cmp    al, cs:string_term
  2134.     jz    fgs_init_next_param
  2135.     mov    byte ptr cs:[bx], al
  2136.     cmp    bx, cs:param_end
  2137.     adc    bx, 0            ; if bx<param_end bx++;
  2138.     next_is f_get_string
  2139. ; Ending quote was found.
  2140. fgs_init_next_param:
  2141.     mov    byte ptr cs:[bx], 0    ; initialize new parameter
  2142.     ; | Eat following semicolon, if any.
  2143.     next_is f_eat_semi
  2144.  
  2145. ;----- f_eat_semi -------------------------------------
  2146. ; Last character was an ending quote.
  2147. ; If this char is a semi, eat it; else unget it.
  2148. ; Next state is always f_get_param.
  2149. f_eat_semi:
  2150.     lodsb
  2151.     cmp    al, ';'
  2152.     jz    fes_eaten
  2153.         inc    cx
  2154.         dec    si
  2155. fes_eaten:
  2156.     next_is f_get_param
  2157.  
  2158. ;----- syntax_error ---------------------------------------
  2159. ; A character was rejected by the state machine.  Exit to
  2160. ; main loop, and print offending character.  Let main loop
  2161. ; decrement CX (length of input string).
  2162. syntax_error:
  2163.     mov    cs:escvector, 0
  2164.     mov    ah, cs:cur_attrib
  2165.     jmp    f_not_ANSI    ; exit, print offending char
  2166.  
  2167. ;------ f_in_param -------------------------------------
  2168. ; Last character was a digit.
  2169. ; Looking for more digits, a semicolon, or a command character.
  2170. f_in_param:
  2171.     lodsb
  2172.     cmp    al, '0'
  2173.     jb    fgp_semi_or_cmd
  2174.     cmp    al, '9'
  2175.     ja    fgp_semi_or_cmd
  2176.         ; It's another digit.  Add into current parameter.
  2177.         sub    al, '0'
  2178.         xchg    byte ptr cs:[bx], al
  2179.         push    dx
  2180.         mov    dl, 10
  2181.         mul    dl
  2182.         pop    dx
  2183.         add    byte ptr cs:[bx], al
  2184.         next_is f_in_param
  2185.     ; Code common to states get_param and in_param.
  2186.     ; Accepts a semicolon or a command letter.
  2187. fgp_semi_or_cmd:
  2188.     cmp    al, ';'
  2189.     jnz    fgp_not_semi
  2190.         cmp    bx, cs:param_end    ; prepare for next param-
  2191.         adc    bx, 0            ; if bp<param_end bp++;
  2192.         ; Set new param to zero, enter state f_get_param.
  2193.         mov    cs:in_num, 0        ; no longer inside number
  2194.         jmp    fgs_init_next_param    ; spaghetti code attack!
  2195. fgp_not_semi:
  2196.     ; It must be a command letter.
  2197.     cmp    al, '@'
  2198.     jb    syntax_error
  2199.     cmp    al, 'z'
  2200.     ja    syntax_error
  2201.     cmp    al, 'Z'
  2202.     jbe    fgp_is_cmd
  2203.     cmp    al, 'a'
  2204.     jb    syntax_error
  2205.         ; It's a lower-case command letter.
  2206.         ; Remove hole between Z and a to save space in table.
  2207.         sub    al, 'a'-'['
  2208. fgp_is_cmd:
  2209.     ; It's a command letter.  Save registers, convert letter
  2210.     ; into address of routine, set up new register usage, call routine.
  2211.     push    si            ; These three registers hold info
  2212.     push    cx            ; having to do with the input string,
  2213.     push    ds            ; which has no interest at all to the
  2214.                     ; control routine.
  2215.  
  2216.     push    cs
  2217.     pop    ds            ; ds is now cs
  2218.  
  2219.     sub    al, '@'            ; first command is @: insert chars
  2220.     cbw
  2221.     add    ax, ax
  2222.     add    ax, offset ansi_fn_table
  2223.     ; ax is now pointer to command routine address in table
  2224.  
  2225.     mov    cx, bx
  2226.     mov    si, param_buffer    ; si is now pointer to parameters
  2227.     sub    cx, si            ;
  2228.     test    in_num, 1
  2229.     jz    fip_out_num
  2230.         inc    cx
  2231. fip_out_num:                ; cx is now # of parameters
  2232.  
  2233.     xchg    ax, bx            ; save pointer to routine in bx
  2234.  
  2235.     ; Calculate cur_x from DX.
  2236.     mov    al, max_x
  2237.     inc    ax
  2238.     sub    al, dl
  2239.     mov    cur_x, al
  2240.  
  2241.     ; Get first parameter into AX; if defaulted, set it to 1.
  2242.     mov    ah, 0
  2243.     lodsb
  2244.     or    al, al
  2245.     jnz    fgp_callem
  2246.         pushf        ; save zero flag (TAA fix)
  2247.         inc    ax
  2248.         popf
  2249. fgp_callem:
  2250.     ; Finally, call the command subroutine.
  2251.     call    word ptr [bx]
  2252.  
  2253.     pop    ds
  2254.     pop    cx
  2255.     pop    si
  2256.  
  2257.     mov    ah, cs:cur_attrib    ; Prepare for STOSW.
  2258.     mov    cs:escvector, 0        ; No longer parsing escape sequence.
  2259.     ; Set flags for reentry at loopnz
  2260.     or    dx, dx            ; "Any columns left on line?"
  2261.     ; Re-enter at bottom of main loop.
  2262.     jmp    f_ANSI_exit
  2263.  
  2264.     page
  2265. ; The ANSI control subroutines.
  2266.  
  2267. ; Each routine is called with the following register usage:
  2268. ;  AX = max(1, value of first parameter)
  2269. ;  Z flag is set if first parameter is zero.
  2270. ;  CX = number of paramters
  2271. ;  SI = offset of second parameter from CS
  2272. ;  DS = CS
  2273. ;  ES:DI points to the current location on the memory-mapped screen.
  2274. ;  DX is number of characters remaining on the current screen line.
  2275. ; The control routine is free to trash AX, BX, CX, SI, and DS.
  2276. ; It must preserve ES, and can alter DX and DI if it wants to move the
  2277. ; cursor.
  2278.  
  2279. ;----------------------------------------------------------------
  2280.  
  2281.     assume    cs:code, ds:code
  2282.  
  2283. ;----- byteout ---------------------------------------------------
  2284. ; Converts al to a decimal ASCII string (in 0..99),
  2285. ; stores it at ES:DI++.     Returns DI pointing at byte after last digit.
  2286. ; Destroys DL.
  2287.  
  2288. byteout proc    near
  2289.     cmp    al, 100        ; check for >99 case -- TAA mod
  2290.     jb    goodbyteout
  2291.     push    ax
  2292.     mov    al, '1'        ; assume value <200!
  2293.     stosb
  2294.     pop    ax
  2295.     sub    ax, 100
  2296. goodbyteout:
  2297.     aam
  2298.     add    ax, 3030h
  2299.     xchg    ah, al
  2300.     stosb
  2301.     xchg    ah, al
  2302.     stosb
  2303.     ret
  2304. byteout endp
  2305.  
  2306. ;----- ansi_fn_table -----------------------------------
  2307. ; Table of offsets of terminal control subroutines in order of
  2308. ; the character that invokes them, @..Z, a..z.    Exactly 53 entries.
  2309. ; All the subroutines are defined below in this module.
  2310. ansi_fn_table    label    word
  2311.     dw    ic,  cup, cdn, cfw, cbk        ; @, A, B, C, D
  2312.     dw    nul, nul, nul, hvp, nul        ; E, F, G, H, I
  2313.     dw    eid, eil, il,  d_l, nul        ; J, K, L, M, N
  2314.     dw    nul, dc,  nul, nul, nul        ; O, P, Q, R, S
  2315.     dw    nul, nul, nul, nul, nul        ; T, U, V, W, X
  2316.     dw    nul, nul            ; Y, Z
  2317.     dw    nul, nul, nul, nul, nul        ; a, b, c, d, e
  2318.     dw    hvp, nul, sm,  nul, nul        ; f, g, h, i, j
  2319.     dw    nul, rm,  sgr, dsr, nul        ; k, l, m, n, o
  2320. if key_redef
  2321.     dw    key, nul, nul, scp, nul        ; p, q, r, s, t
  2322. else
  2323.     dw    nul, nul, nul, scp, nul        ; p, q, r, s, t
  2324. endif
  2325.     dw    rcp, nul, nul, nul, nul        ; u, v, w, x, y
  2326.     dw    nul                ; z
  2327.  
  2328. ansi_functions    proc    near        ; set return type to NEAR
  2329.  
  2330.  
  2331. ;----- Cursor Motion -----------------------------------------------
  2332.  
  2333. ;-- cursor to y,x
  2334. hvp:    or    al, al        ; First parameter is desired Y coordinate.
  2335.     jz    hvp_yok
  2336.     dec    ax        ; Convert to zero-based coordinates.
  2337. hvp_yok:mov    cur_y, al
  2338.     ; Get second parameter, if it is there, and set X with it.
  2339.     xor    ax, ax
  2340.     cmp    cx, 2        ; was there a second parameter?
  2341.     jb    hvp_xok
  2342.     lodsb            ; yes.
  2343.     or    al, al
  2344.     jz    hvp_xok
  2345.     dec    ax        ; convert to zero-based coordinates.
  2346. hvp_xok:mov    cur_x, al
  2347.  
  2348.     ; Clip to maximum coordinates.
  2349. hvp_set:
  2350.     mov    ax, cur_coords        ; al = x, ah = y
  2351.     cmp    al, max_x
  2352.     jbe    hvp_sxok
  2353.     mov    al, max_x
  2354.     mov    cur_x, al
  2355. hvp_sxok:
  2356.     cmp    ah, max_y
  2357.     jbe    hvp_syok
  2358.     mov    al, max_y
  2359.     mov    cur_y, al
  2360. hvp_syok:
  2361.     ; Set values of DX and DI accordingly.
  2362.     call    xy_to_regs
  2363.     ret
  2364.  
  2365. ;-- cursor forward --
  2366. cfw:    add    cur_x, al
  2367.     jmp    hvp_set
  2368.  
  2369. ;-- cursor back -----
  2370. cbk:    sub    cur_x, al
  2371.     jae    hvp_set
  2372.     mov    cur_x, 0
  2373.     jmp    hvp_set
  2374.  
  2375. ;-- cursor down -----
  2376. cdn:    add    cur_y, al
  2377.     jmp    hvp_set
  2378.  
  2379. ;-- cursor up -------
  2380. cup:    sub    cur_y, al
  2381.     jae    hvp_set
  2382.     mov    cur_y, 0
  2383.     jmp    hvp_set
  2384.  
  2385. ;-- save cursor position --------------------------------------
  2386. scp:    mov    ax, cur_coords
  2387.     mov    saved_coords, ax
  2388.     ret
  2389.  
  2390. ;-- restore cursor position -----------------------------------
  2391. rcp:    mov    ax, saved_coords
  2392.     mov    cur_coords, ax
  2393.     jmp    hvp_set        ; Clip in case we have switched video modes.
  2394.  
  2395. ;-- set graphics rendition ------------------------------------
  2396. ; Modifies the color in which new characters are written.
  2397.  
  2398.  
  2399. sgr:    dec    si        ; get back pointer to first parameter
  2400.     or    cx, cx        ; Did he give any parameters?
  2401.     jnz    sgr_loop
  2402.     mov    byte ptr [si], 0    ; no parameters, so fake
  2403.     inc    cx            ; one with the default value.
  2404.     ; For each parameter
  2405. sgr_loop:
  2406.     lodsb                ; al = next parameter
  2407.     ; Search color table
  2408.     push    cx
  2409.  
  2410.     cmp    al, 0            ; not reset?
  2411.     jnz    sgr_continue
  2412.     mov    color_flag, 7        ; finish up normally
  2413. sgr_continue:
  2414.     mov    cx, colors
  2415.     mov    bx, offset color_table-3
  2416. sgr_search:
  2417.     add    bx, 3
  2418.     cmp    al, byte ptr [bx]
  2419.     loopnz    sgr_search    ; until match found or done
  2420.     jnz    sgr_loopx
  2421.  
  2422.     ; If parameter named a known color, set the current
  2423.     ; color variable.
  2424.     cmp    al, 30
  2425.     jge    sgr_color
  2426.     mov    ax, [bx+1]
  2427.     and    atr_flag, al
  2428.     or    atr_flag, ah
  2429.     jmp    short sgr_loopx
  2430. sgr_color:
  2431.     mov    ax, [bx+1]
  2432.     and    color_flag, al
  2433.     or    color_flag, ah
  2434. sgr_loopx:
  2435.     pop    cx
  2436.     loop    sgr_loop        ; until no more parameters.
  2437.  
  2438.     mov    al, color_flag
  2439.     mov    ah, atr_flag
  2440.     test    ah, 4            ; invisible
  2441.     jz    sgr_notinv
  2442.     mov    ah, al            ; then fade to background
  2443. if cheap_pc
  2444.     ror    al,1            ; swap nibbles
  2445.     ror    al,1
  2446.     ror    al,1
  2447.     ror    al,1
  2448. else
  2449.     ror    al,4
  2450. endif
  2451.     and    ax, 7007h
  2452.     or    al, ah
  2453.     jmp    short sgr_done
  2454. sgr_notinv:
  2455.     test    ah, 2            ; reverse?
  2456.     jz    sgr_notrev
  2457. if cheap_pc
  2458.     ror    al,1            ; swap nibbles
  2459.     ror    al,1
  2460.     ror    al,1
  2461.     ror    al,1
  2462. else
  2463.     ror    al,4
  2464. endif
  2465. sgr_notrev:
  2466.     test    ah, 1            ; underline?
  2467.     jz    sgr_notund
  2468.     cmp    gmode_flag, 0        ; monochrome display?
  2469.     jl    sgr_mono_und
  2470.     and    al, 0f0h
  2471.     or    al, 1            ; blue chars on color display
  2472.     jmp    short sgr_notund
  2473.     
  2474. sgr_mono_und:
  2475.     cmp    al, 7            ; can only underline if white on black
  2476.     jne    sgr_notund
  2477.     mov    al, 1
  2478. sgr_notund:
  2479.     and    ah, 88h            ; get bold and blink attributes
  2480.     or    al, ah            ; and  merge them in
  2481. sgr_done:
  2482.     mov    cur_attrib, al        ; save the new attribute
  2483. ;    ret    
  2484.  
  2485. ;----- nul ---------------------------------------------
  2486. ; No-action ansi sequence; called when unknown command given.
  2487. nul:    ret
  2488.  
  2489.  
  2490. ;-- erase in line ----------------------------------------
  2491. ; Uses BIOS to scroll away a one-line rectangle
  2492. eil:    push    dx
  2493.     mov    cx, cur_coords
  2494.     mov    dh, ch
  2495.     jmp    short scrollem
  2496.  
  2497. ;-- erase in display -------------------------------------
  2498. ; Uses BIOS to scroll away all of display
  2499. eid:    
  2500.     jz    eid_toend    ; first parms=0 -- erase to end
  2501.     or    cx,cx        ; no args?
  2502.     jz    eid_toend    ;  then erase to end
  2503.     cmp    al, 1        ; erase to beginning?
  2504.     jz    eid_tostart
  2505.     cmp    al, 2
  2506.     jnz    eid_ignore    ; param must be two
  2507.     mov    cur_coords, 0
  2508.     call    xy_to_regs
  2509.     cmp    cs:fmode,0    ; see if running in fast mode
  2510.     jz    eid_slow
  2511.     xor    di, di    ; offset will be zero after clearing
  2512. eid_slow:
  2513.     push    dx
  2514.     xor    cx, cx
  2515.     mov    dh, max_y
  2516. scrollem:
  2517.     call    get_blank_attrib
  2518.     mov    bh, ah
  2519.     mov    dl, max_x
  2520. eid_process:
  2521.     mov    ax, 600h
  2522.     int    10h        ; use int 10 so screen repositions itself
  2523. ;    call_video          rather than "call_video"
  2524. eid_done:
  2525.     pop    dx
  2526. eid_ignore:
  2527.     ret
  2528.  
  2529. eid_toend:    ; erase following lines then go back and erase in line
  2530.     push    dx
  2531.     call    get_blank_attrib
  2532.     mov    bh,ah
  2533.     mov    ax, 600h
  2534.     mov    dh, max_y
  2535.     mov    dl, max_x
  2536.     mov    ch, cur_y
  2537.     inc    ch
  2538.     cmp    ch, dh            ; don't erase if no following
  2539.     ja    eid_nopost
  2540.     xor    cl,cl
  2541.     call_video
  2542. eid_nopost:
  2543.     mov    cx, cur_coords
  2544.     mov    dh, ch
  2545.     jmp    short scrollem
  2546.  
  2547.  
  2548. eid_tostart:    ; erase preceeding lines then go back and erase in line
  2549.     push    dx
  2550.     call    get_blank_attrib
  2551.     mov    bh,ah
  2552.     mov    dh, cur_y
  2553.     dec    dh
  2554.     js    eid_nopre        ; don't erase if no preceeding
  2555.     mov    ax,600h
  2556.     xor    cx,cx
  2557.     mov    dl, max_x
  2558.     call_video
  2559. eid_nopre:
  2560.     mov    dx, cur_coords
  2561.     dec    dl
  2562.     js    eid_done
  2563.     mov    ch, dh
  2564.     xor    cl, cl
  2565.     jmp    eid_process
  2566.     
  2567. ;-- device status report --------------------------------
  2568. ; Stuffs an escape, a left bracket, current Y, semicolon, current X,
  2569. ; a capital R, and a carriage return into input stream.
  2570. ; The coordinates are 1 to 3 decimal digits each.
  2571.  
  2572. dsr:    push    di
  2573.     push    dx
  2574.     push    es
  2575.     mov    ax, cs
  2576.     mov    es, ax
  2577.     std            ; Store string in reversed order for fun
  2578.     mov    di, offset cpr_esc - 2
  2579.     mov    al, cur_y
  2580.     inc    al        ; convert to one-based coords
  2581.     call    byteout        ; row
  2582.     mov    al, ';'        ; ;
  2583.     stosb
  2584.     mov    al, cur_x
  2585.     inc    al        ; convert to one-based coords
  2586.     call    byteout        ; column
  2587.     mov    al, 'R'        ; R ANSI function 'Cursor Position Report'
  2588.     stosb
  2589.     mov    al, 13
  2590.     mov    word ptr cprseq.adr, di ; save pointer to last char in string
  2591.     stosb                ; send a carriage return, too
  2592.     mov    ax, offset cpr_esc
  2593.     sub    ax, di            ; ax is # of characters in string
  2594.     mov    word ptr cprseq.len, ax ; pass info to the getchar routine
  2595.     cld
  2596.     pop    es
  2597.     pop    dx
  2598.     pop    di
  2599.     ret
  2600. if key_redef
  2601. ;-- keyboard reassignment -------------------------------
  2602. ; Key reassignment buffer is between param_end and redef_end+2, exclusive.
  2603. ; When it shrinks or grows, param_end is moved.
  2604. ; Format of an entry is as follows:
  2605. ;   highest address -> length:word (may be 0)
  2606. ;               key to replace:word     (either hi or low byte is zero)
  2607. ;               .
  2608. ;               .    new key value, "length" bytes long
  2609. ;               .
  2610. ;   lowest address  -> next entry, or free space.
  2611. ; If no arguments are given, keyboard is reset to default condition.
  2612. ; Otherwise, first parameter (or first two, if first is zero) defines
  2613. ; the key whose value is to be changed, and the following parameters
  2614. ; define the key's new, possibly zero-length, value.
  2615.  
  2616. key:
  2617.     ; Is this a reset?
  2618.     or    cx, cx
  2619.     jz    key_init
  2620.     ; Get the first (or first two) parameters
  2621.     cld
  2622.     dec    si    ; point to first param
  2623.     dec    cx    ; Assume it's a fn key, get two params
  2624.     dec    cx
  2625.     lodsw
  2626.     or    al, al    ; Is it a function key?
  2627.     jz    key_fnkey
  2628.         ; It's not a function key- put second param back
  2629.         inc    cx
  2630.         dec    si
  2631. key_fnkey:
  2632.     ; Key to redefine now in AX.  If it's already redefined,
  2633.     ; lookup will set Z, point SI to redef string, set CX to its length.
  2634.     push    di
  2635.     push    es
  2636.     push    cx
  2637.     push    si
  2638.  
  2639.     std            ; moving up, must move from top down
  2640.     push    ds
  2641.     pop    es        ; string move must have ES=DS
  2642.     call    lookup        ; rets Z if redefined...
  2643.     jnz    key_newkey
  2644.     ; It's already defined.     Erase its old definition- i.e., move
  2645.     ; region param_end+1..SI-1 upwards CX+4 bytes, add CX+4 to param_end.
  2646.     add    cx, 4
  2647.     mov    bp, param_end    ; save old value in bp...
  2648.     add    param_end, cx
  2649.     dec    si        ; start at (SI-1)
  2650.     mov    di, si
  2651.     add    di, cx        ; move to (start + CX+4)
  2652.     mov    cx, si
  2653.     sub    cx, bp        ; length of region old_param_end+1..start
  2654.     rep    movsb
  2655. key_newkey:
  2656.     ; Key not redefined.  See if there's enough room to redefine it.
  2657.     pop    si        ; get back pointer to redef string
  2658.     pop    cx        ; get back number of bytes in redef string
  2659.     mov    di, param_end    ; hi byte of new redef record, hi byte of len
  2660.     sub    di, 4        ; hi byte of new data field
  2661.     mov    bx, di
  2662.     sub    bx, cx        ; hi byte of remaining buffer space
  2663.     sub    bx, 16        ; better be at least 16 bytes room
  2664.     cmp    bx, param_buffer
  2665.     jb    key_popem    ; nope- forget it.
  2666.     ; Nothing in the way now!
  2667.     mov    [di+3], cx    ; save length field
  2668.     mov    [di+1], ax    ; save name field
  2669.     jcxz    key_nullstring
  2670. key_saveloop:            ; save data field
  2671.     movsb
  2672.     add    si, 2        ; input string ascending, output descending
  2673.     loop    key_saveloop
  2674. key_nullstring:
  2675.     mov    param_end, di    ; save adr of new hi byte of free area
  2676. key_popem:
  2677.     pop    es
  2678.     pop    di
  2679.  
  2680. key_exit:
  2681.     cld
  2682.     ret
  2683.  
  2684. key_init:
  2685.     ; Build the default redefinition table:
  2686.     ;    control-printscreen -> control-P
  2687.     push    es
  2688.     push    ds
  2689.     pop    es
  2690.     std
  2691.     mov    di, redef_end
  2692.     mov    ax, 1
  2693.     stosw
  2694.     mov    ax, 7200h    ; control-printscreen
  2695.     stosw
  2696.     mov    al, 16        ; control P
  2697.     stosb
  2698.     mov    param_end, di    ; save new bottom of redef table
  2699.     pop    es
  2700.     jmp    key_exit
  2701. endif
  2702.  
  2703.  
  2704. ;---- Delete/Insert Lines -------------------------------
  2705. ; AL is number of lines to delete/insert.
  2706. ; Preserves DX, DI; does not move cursor.
  2707.  
  2708. d_l:    ; Delete lines.
  2709.     mov    ah, 6            ; BIOS: scroll up
  2710.     jmp    short il_open
  2711.  
  2712. il:    ; Insert lines.
  2713.     mov    ah, 7            ; BIOS: scroll down
  2714.  
  2715. il_open:
  2716.     ; Whether inserting or deleting, limit him to (max_y - cur_y) lines;
  2717.     ; if above that, we're just clearing; set AL=0 so BIOS doesn't burp.
  2718.     mov    bh, max_y
  2719.     sub    bh, cur_y
  2720.     cmp    al, bh
  2721.     jbe    il_ok            ; DRK 9/4...
  2722.         mov    al, 0        ; he tried to move too far
  2723. il_ok:
  2724.     push    ax
  2725.     call    get_blank_attrib
  2726.     mov    bh, ah            ; color to use on new blank areas
  2727.     pop    ax            ; AL is number of lines to scroll.
  2728.  
  2729.     mov    cl, 0            ; upper-left-x of data to scroll
  2730.     mov    ch, cur_y        ; upper-left-y of data to scroll
  2731.     push    dx
  2732.     mov    dl, max_x        ; lower-rite-x
  2733.     mov    dh, max_y        ; lower-rite-y (zero based)
  2734.     call_video            ; call BIOS to scroll a rectangle.
  2735.     pop    dx
  2736.     ret                ; done.
  2737.  
  2738. ;-- Insert / Delete Characters ----------------------------
  2739. ; AL is number of characters to insert or delete.
  2740. ; Preserves DX, DI; does not move cursor.
  2741.  
  2742. ic:    mov    ch, 1            ; 1 => swap dest & source below
  2743.     jmp    short dc_ch
  2744.  
  2745. dc:    mov    ch, 0
  2746.  
  2747. dc_ch:
  2748.     cmp    cs:gmode_flag,0
  2749.     jg    dc_ret            ; | if in graphics mode, ignore.
  2750.  
  2751.  
  2752.     ; AL = number of chars to ins or del (guarenteed nonzero).
  2753.     ; Limit him to # of chars left on line.
  2754.     cmp    al, dl
  2755.     jbe    dc_cok
  2756.         mov    al, dl
  2757. dc_cok:
  2758.     push    di            ; DI is current address of cursor
  2759.     xchg    ax, cx            ; CX gets # of chars to ins/del
  2760.     mov    bp, cx            ; BP gets # of columns to clear.
  2761.  
  2762.     ; Set up source = destination + cx*2, count = dx - cx
  2763.     mov    ch, 0            ; make it a word
  2764.     mov    si, di
  2765.     add    si, cx
  2766.     add    si, cx
  2767.     neg    cl
  2768.     add    cl, dl
  2769.     mov    ch, 0            ; CX = # of words to transfer
  2770.     cld                ; REP increments si & di
  2771.  
  2772.     ; If this is an insert, then flip transfer around in both ways.
  2773.     test    ah, 1
  2774.     jz    dc_noswap
  2775.         xchg    di, si        ; source <-> dest
  2776.         std            ; up <-> down
  2777.         mov    ax, cx        ; make move over same range
  2778.         dec    ax
  2779.         add    ax, ax        ; AX=dist from 1st to last byte.
  2780.         add    di, ax        ; Start transfer at high end of block
  2781.         add    si, ax        ;  instead of low end.
  2782. dc_noswap:
  2783.     ; Move those characters.
  2784.     push    es
  2785.     pop    ds
  2786.     rep    movsw
  2787.     mov    cx, bp
  2788.     ; Figure out what color to make the new blanks.
  2789.     call    get_blank_attrib
  2790.     mov    al, ' '
  2791.     ; Blank out vacated region.
  2792.     rep    stosw
  2793.  
  2794.     ; All done.
  2795.     cld                ; restore normal REP state and
  2796.     pop    di            ;  cursor address.
  2797. dc_ret: ret
  2798.  
  2799.  
  2800. ;---- set / reset mode ---------------------------------------
  2801. ; Sets graphics/text mode; also sets/resets "no wrap at eol" mode.
  2802. ; also set/reset graphic cursor mode
  2803. rm:    mov    cl, 0        ; reset
  2804.     jmp    short sm_rs
  2805.  
  2806. sm:    mov    cl, 0ffh    ; set
  2807. sm_rs:
  2808.     ; Is it "wrap at eol" ?
  2809.     cmp    al, 7
  2810.     jnz    sm_notwrap
  2811.     mov    wrap_flag, cl    ; true = wrap at EOL
  2812.     ret
  2813. sm_notwrap:
  2814. ; We will make this smarter by requiring the correct lead character,
  2815. ; except for wrap at eol, which has been badly documented in the MSDOS
  2816. ; manuals.
  2817.     cmp    eat_key, '='    ; set mode -- display mode
  2818.     jz    sm_notbios
  2819.     cmp    eat_key, '?'    ; set mode of togglable attribute?
  2820.     jnz    sm_done
  2821.     ; Is it set/reset graphic cursor mode?
  2822.     cmp    al,99
  2823.     jnz    sm_notgcursor
  2824.     mov    gcursor, cl
  2825.     ret
  2826. sm_notgcursor:
  2827.     ; Is it set/reset fast mode?
  2828.     cmp    al,98
  2829.     jnz    sm_notspeedy
  2830.     mov    fmode, cl
  2831.     cmp    cl,0        ; mode now fast?
  2832.     jnz    sm_done
  2833.     call    move_back
  2834. sm_done:    ret
  2835. sm_notspeedy:
  2836.     ; Is it set/reset bios write?
  2837.     cmp    al,97
  2838.     jnz    sm_done
  2839. if bios_write_tty
  2840.         mov    bmode, cl
  2841. endif
  2842.         ret
  2843. sm_notbios:
  2844.     ; we can now do 43 lines on a VGA display, if desired, as well as 50.
  2845.     ; of course an EGA display will only do 43.
  2846. IF MONO
  2847.     cmp    video_mode, 7        ; mono mode?
  2848.     je    sm_done
  2849. ENDIF    
  2850. IF VGA
  2851.     cmp    al, 43
  2852.     je    is43or50
  2853.     cmp    al, 50
  2854.     jne    sm_video        ; set to whatever it happens to be
  2855. is43or50:
  2856.     dec    al
  2857.     cmp    al, max_y
  2858.     je    sm_done            ; no change in # lines
  2859.     cmp    video_mode, 3        ; display mode > 3?
  2860.     ja    sm_done
  2861.     cmp    al, 49            ; 50 line mode?
  2862.     mov    ax, 1201H        ; set 350 lines on EGA
  2863.     jne    sm_is43
  2864.     mov    ax, 1202h        ; select 400 scan lines
  2865. sm_is43:
  2866.     mov    bl,30h            ; this call ignored on EGA
  2867.     call_video
  2868.         
  2869. ELSE
  2870.     cmp    al, 43
  2871.     jne    sm_video        ; set to whatever it happens to be
  2872.  
  2873.     cmp    max_y, 42        ; no change in # lines?
  2874.     je    sm_done
  2875.     cmp    video_mode, 3        ; must be currently mode <= 3
  2876.     ja    sm_done
  2877. ENDIF
  2878.     mov    ah,0
  2879.     mov    al, video_mode        ; select the video mode.
  2880.     call_video
  2881.         
  2882.     mov    ax,1112h        ; Load 8x8 font
  2883.     mov    bl,0            ; (instead of 8x14 or 8x16)
  2884.     call_video
  2885.  
  2886.     mov    ax, 1200h        ; Load new printscreen
  2887.     mov    bl, 20h
  2888.     call_video
  2889.  
  2890. IF EGA    ; set the EGA cursor
  2891.     mov    dx, port_6845        ; '6845' command reg
  2892.     mov    ax, 070ah        ; start on line 7
  2893.     out    dx, ax
  2894.     mov    al, 0bh            ; end on line 7
  2895.     out    dx, ax
  2896. ENDIF
  2897.     jmp    short sm_home
  2898.  
  2899.  
  2900. sm_video:
  2901.     ; It must be a video mode.  Call BIOS.
  2902.     ; Save graphics mode flag
  2903. IF VGA
  2904.     cmp    al, 3        ; On VGA, modes 0-3, set 400 scan lines
  2905.     ja    no_scan_change
  2906.     push    ax
  2907.     mov    ax, 1202h        ; select 400 scan lines
  2908.     mov    bl,30h            ; this call ignored on EGA
  2909.     call_video
  2910.     pop    ax
  2911. no_scan_change:
  2912. ENDIF    
  2913. IF VESA
  2914.     cmp    al, 80h        ; not a vesa mode?
  2915.     jb    normal_mode
  2916.     xor    bx, bx
  2917.     mov    bl, al
  2918.     add    bx, 8080h    ; set msb and put mode in range
  2919.     mov    ax, 4f02h
  2920.     int    10h        ; can't use call_video, because often
  2921.                 ; VESA compatibility driver is used and
  2922.                 ; loaded after us.
  2923.     jmp    short sm_home
  2924. normal_mode:    
  2925. ENDIF    
  2926.     mov    ah, 0        ; "set video mode"
  2927.     or    al, 80h        ;  but don't erase screen, since colors wrong.
  2928.     call_video
  2929. sm_home:
  2930.     ; Read the BIOS buffer address/cursor position variables.
  2931.     mov    ax, ABS40
  2932.     push    ds
  2933.     mov    ds, ax
  2934.     assume    ds:ABS40
  2935.  
  2936.     ; Find current video mode and screen size.
  2937.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  2938.     mov    cl, ega_rows        ; cl = max row
  2939.     pop    ds
  2940.     assume    ds:code
  2941.     call    set_gmode        ; set gmode based on video mode
  2942.     mov    video_mode, al
  2943.     dec    ah            ; ah = max column
  2944.     mov    max_x, ah
  2945.     mov    max_y, cl
  2946.  
  2947.     ; Since cursor may end up in illegal position, it's best to
  2948.     ; just go home after switching video modes.
  2949.     mov    cur_coords, 0
  2950.     call    xy_to_regs
  2951.  
  2952.     jmp    eid_slow        ; then clear the screen
  2953.  
  2954. ansi_functions    endp    ; end dummy procedure block
  2955.  
  2956.  
  2957.  
  2958. ;-------- Color table -----------------------------------------
  2959. ; Used in "set graphics rendition"
  2960.  
  2961. colors    equ    28            ; number of colors in table
  2962.  
  2963. color_table:
  2964. ; The first set attributes rather than colors
  2965.     db    0, 000h,00h        ; all attribs off; normal.
  2966.     db    1, 0ffh,08h        ; bold
  2967.     db    2, 0f7h,00h        ; dim (not bold)
  2968.     db    4, 0ffh,01h        ; underline
  2969.     db    5, 0ffh,80h        ; blink
  2970.     db    7, 0ffh,02h        ; reverse
  2971.     db    8, 0ffh,04h        ; invisible
  2972.     
  2973.     db    22,0f7h,00h        ; un-bold, un-dim
  2974.     db    24,0feh,00h        ; un-underline
  2975.     db    25,07fh,00h        ; un-blink
  2976.     db    27,0fdh,00h        ; unreverse
  2977.     db    28,0fbh,00h        ; un-invisible
  2978.  
  2979. ; These set the colors
  2980.     db    30,0f8h,00h        ; black foreground
  2981.     db    31,0f8h,04h        ; red
  2982.     db    32,0f8h,02h        ; green
  2983.     db    33,0f8h,06h        ; yellow
  2984.     db    34,0f8h,01h        ; blue
  2985.     db    35,0f8h,05h        ; magenta
  2986.     db    36,0f8h,03h        ; cyan
  2987.     db    37,0f8h,07h        ; white
  2988.  
  2989.     db    40,08fh,00h        ; black background
  2990.     db    41,08fh,40h        ; red
  2991.     db    42,08fh,20h        ; green
  2992.     db    43,08fh,60h        ; yellow
  2993.     db    44,08fh,10h        ; blue
  2994.     db    45,08fh,50h        ; magenta
  2995.     db    46,08fh,30h        ; cyan
  2996.     db    47,08fh,70h        ; white
  2997.  
  2998.     page
  2999. ;-------- dos function # 0 : init driver ---------------------
  3000. ; Initializes device driver interrupts and buffers, then
  3001. ; passes ending address of the device driver to DOS.
  3002. ; Since this code is only used once, the buffer can be set up on top
  3003. ; of it to save RAM. Placed at end so that any excess can be deleted
  3004. ; after running
  3005.  
  3006. dosfn0    proc    near
  3007.     assume    cs:code, ds:code
  3008. ; The following check for MDA/CGA courtesy of Arend van den Brug
  3009.                     ; initialise ega_rows to normal value
  3010.                     ; to enable MDA/CGA support
  3011.     mov    ax, ABS40
  3012.     mov    ds, ax
  3013.     assume    ds:ABS40
  3014.     mov    al, ega_rows
  3015.     or    al, al            ; not already filled in ?
  3016.     jnz    set_kb
  3017.     mov    ega_rows, 24        ; then set it to the normal 25 lines
  3018.  
  3019. set_kb:
  3020.     ; Install BIOS keyboard break handler.
  3021.     xor    ax, ax
  3022.     mov    ds, ax
  3023.     assume    ds:NOTHING
  3024.     mov    bx, 6Ch
  3025.     mov    word ptr [BX],offset break_handler
  3026.     mov    [BX+02], cs
  3027.     ; Install INT 29 quick putchar.
  3028.     mov    bx, 0a4h
  3029.     mov    word ptr [bx], offset int_29
  3030.     mov    [bx+2], cs
  3031. IF dos4
  3032.     ; Install INT 2Fh multiplex interrupt, saving old vector.
  3033.     mov    bx, 02fh * 4
  3034.     mov    ax, [bx]
  3035.     mov    word ptr cs:old_mpx, ax
  3036.     mov    ax, [bx+2]
  3037.     mov    word ptr cs:old_mpx[2], ax
  3038.     mov    word ptr [bx], offset new_mpx
  3039.     mov    word ptr [bx+2],  cs
  3040. ENDIF
  3041.     
  3042.     ; Install INT 10h video bios replacement, saving old vector.
  3043.     mov    bx, 40h
  3044.     mov    ax, [bx]
  3045.     mov    word ptr cs:old_vid_bios, ax
  3046.     mov    ax, [bx+2]
  3047.     mov    word ptr cs:old_vid_bios[2], ax
  3048.     mov    word ptr [bx], offset new_vid_bios
  3049.     mov    word ptr [bx+2], cs
  3050.  
  3051.     push    cs
  3052.     pop    ds
  3053.     push    cs
  3054.     pop    es            ; es=cs so we can use stosb
  3055.     assume    ds:code
  3056.     cld                ; make sure stosb increments di
  3057.  
  3058.     ; Calculate addresses of start and end of parameter/redef buffer.
  3059.     ; The buffer occupies the same area of memory as this code!
  3060.     ; ANSI parameters are accumulated at the lower end, and
  3061.     ; keyboard redefinitions are stored at the upper end; the variable
  3062.     ; param_end is the last byte used by params (changes as redefs added);
  3063.     ; redef_end is the last word used by redefinitions.
  3064.     mov    di, offset dosfn0
  3065.     mov    param_buffer, di
  3066.     add    di, buf_size
  3067.     mov    param_end, di    ; addr of last byte in free area
  3068.     inc    di
  3069.  
  3070.     ; Announce our presence
  3071.     mov    ax,cs
  3072.     mov    bx,offset Ident1
  3073.     call hexcnv
  3074.  
  3075.     mov    si, offset welcome
  3076. msg_loop:
  3077.     lodsb    
  3078.     cmp    al,0
  3079.     je    msg_done
  3080.     int    29h
  3081.     jmp    msg_loop
  3082.  
  3083. msg_done:
  3084.  
  3085. IF key_redef
  3086.     ; Build the default redefinition table:
  3087.     ;    control-printscreen -> control-P
  3088.     ; (Must be careful not to write over ourselves here!)
  3089.     mov    al, 16        ; control P
  3090.     stosb
  3091.     mov    ax, 7200h    ; control-printscreen
  3092.     stosw
  3093.     mov    ax, 1        ; length field
  3094.     mov    redef_end, di    ; address of last used word in table
  3095.     stosw
  3096. endif
  3097.  
  3098.     ; Return ending address of this device driver.
  3099.     ; Status is in AX.
  3100.     lds    si, req_ptr
  3101.     mov    word ptr [si+0Eh], di
  3102.     mov    [si+10h], cs
  3103.  
  3104.     xor    ax, ax
  3105.     ; Return exit status in ax.
  3106.     ret
  3107.  
  3108. welcome:
  3109.     db    27,'[33;1m'
  3110.     db    "NNANSI.SYS for "
  3111.     card_id
  3112.     if    cheap_pc
  3113.     db    " (XT class processor)"
  3114.     else
  3115.     db    " (AT class processor)"
  3116.     endif
  3117.     db    13, 10
  3118.     db    'By Tom Almy, version 05/91'
  3119.     db    13,10,10
  3120.     db    'Based on NANSI.SYS V2.2'
  3121.     db    13,10
  3122.     db    '(C) Daniel Kegel, Pasadena, CA 1986.'
  3123.     db    13,10
  3124.     db    'License NANSI.SYS by sending $10.00(US) to Daniel Kegel,'
  3125.     db    13,10
  3126.     db    '221 Fairview Ave, South Pasadena, CA 91030, USA.'
  3127.     db    13, 10, 13, 10
  3128.     db    'NNANSI driver loaded at '
  3129. Ident1    db    'XXXX:0000'
  3130.     db    13, 10, 27, '[0m', 0
  3131.     
  3132. dosfn0    endp
  3133.  
  3134. hexcnv    proc    near
  3135. ; AX= value
  3136. ; BX= pointer to destination string
  3137. ; AX, BX destroyed, all other registers intact.
  3138.     push    cx
  3139.     push    dx
  3140.     mov    cx, 4
  3141. hexcnv_loop:
  3142. if cheap_pc
  3143.     rol    ax, 1
  3144.     rol    ax, 1
  3145.     rol    ax, 1
  3146.     rol    ax, 1
  3147. else
  3148.     rol    ax, 4
  3149. endif
  3150.     mov    dl, al
  3151.     and    dl, 0fh
  3152.     add    dl, '0'
  3153.     cmp    dl, '9'
  3154.     jbe    hexcnv_isdigit
  3155.     add    dl, 'A'-'9'-1
  3156. hexcnv_isdigit:
  3157.     mov    [bx], dl
  3158.     inc    bx
  3159.     loop    hexcnv_loop
  3160.     pop    dx
  3161.     pop    cx
  3162.     ret
  3163.  
  3164. hexcnv    endp
  3165.  
  3166. code    ends
  3167.     end
  3168.