home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / n / nnansi93.zip / NNANSI.ASM < prev    next >
Assembly Source File  |  1993-01-15  |  92KB  |  3,711 lines

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