home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 8 / CDASC08.ISO / VRAC / NNANS593.ZIP / NNANSI.ASM < prev    next >
Assembly Source File  |  1993-05-07  |  94KB  |  3,836 lines

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