home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / assemblr / library / ansi / nnansi / nnansi.asm < prev    next >
Assembly Source File  |  1990-09-11  |  43KB  |  1,758 lines

  1. %title    "NNANSI Terminal Driver  Main Module"
  2. %pagesize    60, 132
  3. %noconds
  4. ;--- nnansi.asm ----------------------------------------------------------
  5. ; New, New ANSI terminal driver.
  6. ; Optimized for speed in the case of multi-character write requests.
  7. ; (C) 1986 Daniel Kegel, Pasadena, CA
  8. ; May be distributed for educational and personal use only
  9. ; The following files make up the driver:
  10. ;    nnansi.asm   - all DOS function handlers except init
  11. ;       nnansi_d.asm - Compilation options
  12. ;    nnansi_p.asm - parameter parser for ANSI escape sequences
  13. ;    nnansi_f.asm - ANSI command handlers
  14. ;    nnansi_i.asm - init DOS function handler
  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. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  26. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  27. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  28. ;          direct cursor positioning, takeover of BIOS write_tty,
  29. ;          noticed & squashed 2 related bugs in tab expansion
  30. ; 13 feb 86:  Squashed them again, harder
  31. ; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
  32. ;          If the addition of the beep period to the
  33. ;          BIOS low timer word results in an overflow, the beep will be
  34. ;          supressed. Also made code compatible eith earlier versions
  35. ;          of assembler.
  36.  
  37. ; Tom Almy, Tualatin, Oregon (toma@tekgvs.labs.tek.com) modified the NANSI 
  38. ;  version ; 2.2 code for use in EGA/VGA environments only:
  39. ; 8 Jan 89:   Additional compilation options
  40. ;             Scrolling via reprogramming display start (*MUCH* faster)
  41. ;          INT29 updates display directly if not control character.
  42. ;          Various cleanups
  43. ; Nov 89:     Some bug fixes, customization for various cards enhanced
  44. ;             display modes, better handling of graphic cursor, graphic
  45. ;             characters in 16 color modes are drawn by NNANSI rather
  46. ;             than BIOS (much faster).
  47. ; Sept 90:    Color backgrounds and XOR mode (as BLINK) in 16 color graphic
  48. ;          modes. VGA has 43 or 50 line modes (instead of producing 50
  49. ;          (when 43 selected). FAST mode can be toggled via escape 
  50. ;          sequences. Lots of code clean up. Some old options incorporated
  51. ;          (extra ANSI sequences) or deleted (output conversion).
  52. ;             The fast graphic draw routine has been speeded up, since it
  53. ;          was slower than some BIOSes (!). The BIOS TTY call has been
  54. ;             redirected to the INT29 routine which has some efficiency
  55. ;          speedups; this also saved code space.
  56.  
  57. ;------------------------------------------------------------------------
  58.  
  59.     include nnansi_d.asm    ; definitions
  60.  
  61.     ; from nnansi_f.asm
  62.     extrn    f_escape:near, f_in_escape:near
  63.  
  64.     ; from nnansi_i.asm
  65.     extrn    dosfn0:near
  66.  
  67.     ; to nnansi_p.asm
  68.     public    f_loopdone
  69.     public    f_not_ansi
  70.     public    f_ansi_exit
  71.     public    in_num
  72.  
  73.     ; to both nnansi_p.asm and nnansi_f.asm
  74.     public    cur_x, cur_y, max_x, cur_attrib
  75.  
  76.     ; to nnansi_f.asm
  77.     public    xy_to_regs, get_blank_attrib
  78.     public    wrap_flag
  79.     public    cur_parm_ptr
  80.     public    cur_coords, saved_coords, max_y
  81.     public    escvector, string_term
  82.     public    cpr_esc, cprseq
  83.     public    video_mode
  84. if key_redef
  85.     public    lookup
  86. endif
  87.     public    gmode_flag
  88.     public    gcursor
  89.     public    set_gmode
  90.     public    fmode
  91.     public    rev_flag
  92.     public    move_back
  93.  
  94.     ; to nnansi_i.asm
  95.     public    req_ptr, break_handler
  96.     public    int_29
  97.     public    new_vid_bios, old_vid_bios
  98.  
  99.     ; to all modules
  100.     public    param_buffer, param_end
  101. if key_redef
  102.     public    redef_end
  103. endif
  104.  
  105. ;--- push_all, pop_all ------------------------------------------------
  106. ; Save/restore all user registers.
  107. IF    cheap_pc
  108. push_all    macro
  109.     push    ax
  110.     push    bx
  111.     push    cx
  112.     push    dx
  113.     push    bp
  114.     push    si
  115.     push    di
  116.     endm
  117. ELSE
  118. push_all    macro
  119.     pusha
  120.     endm
  121. ENDIF
  122.  
  123. IF    cheap_pc
  124. pop_all macro
  125.     pop    di
  126.     pop    si
  127.     pop    bp
  128.     pop    dx
  129.     pop    cx
  130.     pop    bx
  131.     pop    ax
  132.     endm
  133. ELSE
  134. pop_all macro
  135.     popa
  136.     endm
  137. ENDIF
  138.  
  139. call_video macro    ; call original video interrupt
  140.     pushf        ; push flags
  141.     call    dword ptr cs:old_vid_bios
  142. endm
  143.  
  144. draw_gcursor macro        ; draw graphic cursor
  145. if quick_char
  146.     mov    ax, 8f16h
  147.     call    quick_graph
  148. else
  149.     mov    ax, 0916h        ; draw cursor at location
  150.     mov    bx, 8fh
  151.     mov    cx, 1
  152.     call_video
  153. endif
  154. endm
  155.  
  156.  
  157. keybuf    struc                ; Used in getchar
  158. len    dw    ?
  159. adr    dw    ?
  160. keybuf    ends
  161.  
  162.  
  163. ABS40    segment at 40h
  164.     org    1ah
  165. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  166. buffer_tail    dw    ?
  167.  
  168.     org    49h
  169. crt_mode    db    ?
  170. crt_cols    dw    ?
  171. crt_len        dw    ?
  172. crt_start    dw    ?
  173. cursor_posn    dw    8 dup (?)
  174. cursor_mode    dw    ?
  175. active_page    db    ?
  176. addr_6845    dw    ?
  177. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  178. crt_palette    db    ?
  179.     org    6ch
  180. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  181.     org    84h
  182. ega_rows    db    ?    ; #rows-1 on display
  183. ega_points    dw    ?    ; bytes per character
  184.  
  185. ABS40    ends
  186.  
  187.     page
  188.  
  189. CODE    segment word public 'CODE'
  190. assume    cs:CODE, ds:CODE
  191.  
  192.     ; Device Driver Header
  193.  
  194.     org    0
  195.  
  196.     dd    -1            ; next device
  197.     dw    8013h            ; attributes
  198.     dw    strategy        ; request header pointer entry
  199.     dw    interrupt        ; request entry point
  200.     db    'CON'            ; device name (8 char)
  201.     db    5 dup (20h)        ;  ... and 5 blanks)
  202.  
  203.     ; Identification- in case somebody TYPEs the assembled driver
  204.     db    27,'[2J'
  205.     db    "NNANSI.SYS EGA/VGA"
  206.     ife    cheap_pc
  207.     db    "(80286)"
  208.     else
  209.     db    "(80x86)"
  210.     endif
  211.     db    13, 10
  212.     db    'by Tom Almy based on code (C) Daniel Kegel, Pasadena, CA 1986.'
  213.     db    13, 10, 26
  214.  
  215.     even
  216. ;----- variable area --------------------
  217.     org    $-38        ; overlay id string with uninitialized data
  218. req_ptr label    dword
  219. req_off dw    ?
  220. req_seg dw    ?
  221. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  222. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  223. old_vid_bios    dd    ?    ; pointer to old video bios routine
  224. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  225. temp_val    dw    ?    ; just a temporary
  226. param_buffer    dw    ?    ; address of first byte free for new params
  227. param_end    dw    ?    ; address of end of free area
  228. if key_redef
  229. redef_end    dw    ?    ; address of end of redefinition area
  230. endif
  231. no_c_flag    db    ?    ; there is no graphic cursor on the screen.
  232. max_y        db    ?    ; lines-1
  233. max_cur_x    label    word    ; used to get both max & cur at once
  234. max_x        db    ?    ; line width (79 for 80x25 modes)
  235. cur_coords    label    word
  236. cur_x        db    ?    ; cursor position (0 = left edge)
  237. cur_y        db    ?    ;          (0 = top edge)
  238. video_mode    db    ?    ; ROM BIOS video mode (2=BW, 3=color, etc)
  239. string_term    db    ?    ; either escape or double quote
  240. in_num        db    ?    ; true if between a digit and a semi in parse
  241. int_29_buf    db    ?    ; character buffer for int 29 calls
  242. fnkeybuf    db    ?    ; holds second byte of fn key codes
  243. cpr_buf        db    8 dup (?), '['
  244. cpr_esc        db    1bh    ; descending buffer for cpr function
  245.  
  246.     even    ; this should be redundant, if I did it right
  247.  
  248. ; following four keybufs hold information about input
  249. ; Storage order determines priority- since the characters making up a function
  250. ; key code must never be separated (say, by a Control-Break), they have the
  251. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  252. ; lowest priority.
  253.  
  254. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  255. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  256. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  257. if key_redef
  258. xlatseq keybuf    <0>        ; keyboard reassignment string
  259. endif
  260.  
  261. escvector    dw    0    ; state vector of ESCape sequencor
  262. brkkeybuf    db    3    ; control C
  263. wrap_flag    db    1    ; 0 = no wrap past line end
  264. cur_attrib    db    7    ; current char attributes
  265. gmode_flag    db    0    ; true if in graphics mode
  266. gcursor        db    initgc    ; true if graphic cursor enabled
  267. fmode        db    initfast    ; in fast mode?
  268. rev_flag    db    0    ; non-zero if in reverse video
  269.  
  270. port_6845    equ    3d4h
  271. page
  272. ;------ xy_to_regs --------------------------------------------
  273. ; on entry: x in cur_x, y in cur_y
  274. ; on exit:  dx = chars left on line, di = address
  275. ; Alters ax, bx.
  276. xy_to_regs    proc    near
  277.     ; Find number of chars 'till end of line, keep in DX
  278.     mov    ax, max_cur_x
  279.     mov    bx, ax            ; save max_x & cur_x for next block
  280.     xor    ah, ah            ; ax = max_x
  281.     xchg    dx, ax
  282.     mov    al, bh
  283.     xor    ah, ah            ; ax = cur_x
  284.     sub    dx, ax
  285.     inc    dx            ; dx is # of chars till EOL
  286.     ; Calculate DI = current address in text buffer
  287.     mov    al, bl            ; al = max_x
  288.     inc    al
  289.     mul    cur_y
  290.     add    al, bh            ; al += cur_x
  291.     adc    ah, 0            ; AX is # of chars into buffer
  292.     add    ax, ax
  293.     xchg    di, ax            ; DI is now offset of cursor.
  294.  
  295.     push    ds
  296.     mov    ax, ABS40
  297.     mov    ds, ax
  298.     assume    ds:ABS40
  299.     add    di, crt_start        ; crt offset
  300.                     ; the offset could be non-zero because
  301.                     ; of video pages or fast scrolling.
  302.     pop    ds
  303.     assume    ds:nothing
  304.     ret
  305. xy_to_regs    endp
  306.  
  307. page
  308. ;------- dos_fn_tab -------------
  309. ; This table is used in "interrupt" to call the routine that handles
  310. ; the requested function.
  311.  
  312. max_cmd equ    12
  313. dos_fn_tab:
  314.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  315.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  316.  
  317. ;------- strategy ----------------------------------------------------
  318. ; DOS calls strategy with a request which is to be executed later.
  319. ; Strategy just saves the request.
  320.  
  321. strategy    proc    far
  322.     mov    cs:req_off,BX
  323.     mov    cs:req_seg,ES
  324.     ret
  325. strategy    endp
  326.  
  327. ;------ interrupt -----------------------------------------------------
  328. ; This is where the request handed us during "strategy" is
  329. ; actually carried out.
  330. ; Calls one of 12 subroutines depending on the function requested.
  331. ; Each subroutine returns with exit status in AX.
  332.  
  333. interrupt    proc    far
  334.  
  335.     sti
  336.     push_all            ; preserve caller's registers
  337.     push    ds
  338.     push    es
  339.     
  340.  
  341.     ; Read requested function information into registers
  342.     lds    bx,cs:req_ptr
  343.     xor    ah,ah            ; clear upper part of ax
  344.     mov    al,ds:[BX+02h]        ; al = function code
  345. ;
  346. ; The next instruction blows up MASM 1.0 but who cares!!
  347. ;
  348.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  349.     mov    cx,[BX+12h]        ; cx = input/output byte count
  350.  
  351.     cmp    al, max_cmd
  352.     ja    unk_command        ; too big, exit with error code
  353.  
  354.     xchg    bx, ax
  355.     shl    bx, 1            ; form index to table of words
  356.     mov    ax, cs
  357.     mov    ds, ax
  358.     call    word ptr dos_fn_tab[bx]
  359. int_done:
  360.     lds    bx,cs:req_ptr        ; report status
  361.     or    ax, 100h        ; (always set done bit upon exit)
  362.     mov    [bx+03],ax
  363.  
  364.     pop    ES            ; restore caller's registers
  365.     pop    DS
  366.     pop_all
  367.     ret                ; return to DOS.
  368.  
  369. unk_command:
  370.     call    badcmd
  371.     jmp    int_done
  372.  
  373. interrupt    endp
  374.     page
  375. ;----- BIOS break handler -----------------------------------------
  376. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  377. ; Simply notes that a break was hit.  Flag is checked during input calls.
  378.  
  379. break_handler    proc
  380.     mov    cs:brkkey.len, 1
  381.     iret
  382. break_handler    endp
  383.  
  384.     page
  385.  
  386. ;------ badcmd -------------------------------------------------------
  387. ; Invalid function request by DOS.
  388. badcmd    proc    near
  389.     mov    ax, 813h        ; return "Error: invalid cmd"
  390.     ret
  391. badcmd    endp
  392.  
  393.  
  394. ;------ nopcmd -------------------------------------------------------
  395. ; Unimplemented or dummy function request by DOS.
  396. nopcmd    proc    near
  397.     xor    ax, ax            ; No error, not busy.
  398.     ret
  399. nopcmd    endp
  400.  
  401. ;------- dos function #4 ----------------------------------------
  402. ; Reads CX characters from the keyboard, places them in buffer at
  403. ; ES:SI.
  404. dosfn4    proc    near
  405.     jcxz    dos4done
  406.     mov    di, si
  407. dos4lp: push    cx
  408.     call    getchar
  409.     pop    cx
  410.     stosb
  411.     loop    dos4lp
  412. dos4done:
  413.     xor    ax, ax            ; No error, not busy.
  414.     ret
  415. dosfn4    endp
  416.  
  417. ;-------- dos function #5: non-destructive input, no wait ------
  418. ; One-character lookahead into the keyboard buffer.
  419. ; If no characters in buffer, return BUSY; otherwise, get value of first
  420. ; character of buffer, stuff into request header, return DONE.
  421. dosfn5    proc    near
  422.     call    peekchar
  423.     jz    dos5_busy
  424.  
  425.     lds    bx,req_ptr
  426.     mov    [bx+0Dh], al
  427.     xor    ax, ax            ; No error, not busy.
  428.     jmp    short dos5_exit
  429. dos5_busy:
  430.     MOV    ax, 200h        ; No error, busy.
  431. dos5_exit:
  432.     ret
  433.  
  434. dosfn5    endp
  435.  
  436. ;-------- dos function #6: input status --------------------------
  437. ; Returns "busy" if no characters waiting to be read.
  438. dosfn6    proc    near
  439.     call    peekchar
  440.     mov    ax, 200h        ; No error, busy.
  441.     jz    dos6_exit
  442.     xor    ax, ax            ; No error, not busy.
  443. dos6_exit:
  444.     ret
  445. dosfn6    endp
  446.  
  447. ;-------- dos function #7: flush input buffer --------------------
  448. ; Clears the IBM keyboard input buffer.     Since it is a circular
  449. ; queue, we can do this without knowing the beginning and end
  450. ; of the buffer; all we need to do is set the tail of the queue
  451. ; equal to the head (as if we had read the entire queue contents).
  452. ; Also resets all the device driver's stuffahead buffers.
  453. dosfn7    proc    near
  454.     xor    ax, ax
  455.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  456.     mov    cprseq.len, ax
  457.     mov    brkkey.len, ax
  458. if key_redef
  459.     mov    xlatseq.len, ax
  460. endif
  461.  
  462.     mov    ax, ABS40
  463.     mov    es, ax
  464.     mov    ax, es:buffer_head    ; clear queue by making the tail
  465.     mov    es:buffer_tail, ax    ; equal to the head
  466.  
  467.     xor    ax, ax            ; no error, not busy.
  468.     ret
  469. dosfn7    endp
  470.  
  471.     page
  472. ;--- new_vid_bios -------------------------------------------
  473. ; new_vid_bios takes the set cursor, get display mode, change mode, and
  474. ; get mode calls.
  475.  
  476. ; If bios_write_tty defined, new_vid_bios replaces the write_tty call.
  477. ; This gives BIOS ANSI capability.
  478. ; However, it takes away the escape character.
  479. ; If this is not desired, just tell init to not take over the vector.
  480. ; All other calls get sent to the old video bios.
  481.  
  482. JUMPS    ; clarify branches -- TASM V2.0 recommended
  483.  
  484. new_vid_bios    proc
  485.     STI
  486. IF bios_write_tty
  487.     cmp    ah, 14
  488.     jz    nvb_write_tty
  489. ENDIF
  490.     cmp    Ah, 02h     ; set cursor position command?
  491.     jz    nvb_setcursor
  492.     cmp    Ah,0        ; change mode command?
  493.     jz    nvb_smode
  494.     cmp    cs:fmode,0    ; slow mode?
  495.     jz    new_vid_pass    ; then pass it on
  496.     cmp    Ah, 0Fh     ; get display mode command?
  497.     jz    nvb_display
  498.     cmp    Ah, 06h        ; clear screen command?
  499.     jz    nvb_scroll
  500.     cmp    Ah, 07h        ; alternative cls command?
  501.     jz    nvb_scroll
  502. new_vid_pass:
  503.     jmp    dword ptr cs:old_vid_bios
  504. IF bios_write_tty
  505. IF 1    ; Does INT 29 now!
  506. ; WRITE TTY SUBCOMMAND
  507. nvb_write_tty:
  508.     push    cx        ; save register
  509.     mov    cl, cs:cur_attrib
  510.     ; If in graphics mode, BL is new color
  511.     cmp    cs:gmode_flag, 0
  512.     jz    nvb_wt_text
  513.  
  514.     mov    cs:cur_attrib, bl    ; ja?
  515. nvb_wt_text:
  516.     int    29h
  517.     mov    cs:cur_attrib, cl
  518.     pop    cx
  519.     iret
  520. ELSE
  521. ; WRITE TTY SUBCOMMAND
  522. nvb_write_tty:
  523. ;    sti
  524.     push    ds
  525.     push    cs
  526.     pop    ds    ; establish adressability
  527.     assume    ds:CODE
  528.     push    es
  529.     push_all
  530.     mov    cl, cur_attrib
  531.     ; If in graphics mode, BL is new color
  532.     cmp    gmode_flag, 0
  533.     jz    nvb_wt_text
  534.  
  535.     mov    cur_attrib, bl    ; ja?
  536. nvb_wt_text:
  537.     push    cx
  538.  
  539.     mov    cx, 1
  540.     mov    bx, cs
  541.     mov    es, bx
  542.     mov    si, offset int_29_buf
  543.     mov    byte ptr es:[si], al
  544.     call    dosfn8
  545.  
  546.     pop    cx
  547.     mov    cs:cur_attrib, cl    ; restore color
  548.     pop_all
  549.     pop    es
  550.     pop    ds
  551.     assume    ds:nothing
  552.     iret
  553. ENDIF
  554. ENDIF
  555.  
  556. ; GET DISPLAY MODE SUBCOMMAND
  557. nvb_display:
  558.     cmp    cs:gmode_flag,0    ; Graphic mode?
  559.     jnz    new_vid_pass
  560.     push    ds
  561.     push    dx
  562.     mov    dx, ABS40
  563.     mov    ds, dx
  564.     assume    ds:ABS40
  565.     cmp    crt_start,0    ; At start of mem?
  566.     jz    nvb_pass
  567.     call    move_back
  568.     jmp    nvb_pass
  569.  
  570. ; SCROLL DISPLAY SUBCOMMAND
  571. nvb_scroll:
  572.     push    ds
  573.     push    dx
  574.     mov    dx, ABS40
  575.     mov    ds, dx
  576.     assume    ds:ABS40
  577.     mov    cs:no_c_flag, 1    ; if graphic, don't draw cursor afterwards
  578.     cmp    cs:gmode_flag,0    ; graphic mode?
  579.     jnz    nvb_pass
  580.     cmp    crt_start,0    ; not at start of mem
  581.     jz    nvb_pass
  582.     cmp    al, 0        ; scroll, not erase
  583.     jne    nvb_pass
  584.     or    cx, cx        ; not entire screen?
  585.     jne    nvb_pass
  586.     pop    dx
  587.     push    dx
  588.     inc    dl
  589.     cmp    dl, byte ptr crt_cols    ; same question, max columns
  590.     ja    nvb_pass        ; >size is full screen, though
  591.     cmp    dh, ega_rows        ; same question, max rows
  592.     ja    nvb_pass        ; >size is full screen, though
  593.     push    ax        ; erase is easier since we dont move screen
  594.     xor    ax,ax
  595.     mov    crt_start,0    ; reset offsets
  596.     mov    dx,port_6845
  597.     mov    al,0ch
  598.     out    dx,ax
  599.     inc    al
  600.     out    dx,ax
  601.     pop    ax
  602. ;    jmp    nvb_pass    ; jump is redundant
  603.  
  604. nvb_pass:
  605.     pop    dx
  606.     assume    ds:nothing
  607.     pop    ds
  608.     jmp    dword ptr cs:old_vid_bios    ; now doit
  609.  
  610. ; SET CURSOR SUBCOMMAND
  611. nvb_setcursor:
  612.     push    ds
  613.     push    dx
  614.     mov    dx, ABS40
  615.     mov    ds,dx
  616.     assume    ds:ABS40
  617.     cmp    cs:gmode_flag,0    ; Alpha mode?
  618.     je    nvb_pass
  619.     cmp    cs:no_c_flag, 0    ; inhibited cursor?
  620.     jnz    nvb_pass    ; then keep inhibited
  621.     cmp    cs:gcursor, 0    ; no cursor?
  622.     jz    nvb_pass    ; then don't want one now!
  623.     push_all
  624.     draw_gcursor
  625.     pop_all
  626.     assume    ds:nothing
  627.     pop    dx            ; restore registers
  628.     pop    ds
  629.     call_video            ; original int 10h
  630.     push_all
  631.     draw_gcursor            ; redraw the cursor
  632.     pop_all
  633.     iret                ; return from interrupt
  634.  
  635. ; SET DISPLAY MODE SUBCOMMAND
  636. nvb_smode:
  637.     call_video
  638.     push_all
  639.     push    ds
  640.     mov    dx, ABS40
  641.     mov    ds, dx
  642.     assume    ds:ABS40
  643.     mov    al, crt_mode    ; get mode and check for being graphic
  644.     call    set_gmode
  645.     mov    cs:no_c_flag, al ; if graphic, then no cursor is on screen.
  646.     pop    ds
  647.     assume    ds:nothing
  648.     pop_all
  649.     iret
  650. NOJUMPS
  651. new_vid_bios    endp
  652. page
  653.  
  654. ;------ int_29 ----------------------------------------------
  655. ; Int 29 handles DOS quick-access putchar.
  656. ; Last device loaded with attribute bit 4 set gets accessed for
  657. ; single-character writes via int 29h instead of via interrupt.
  658. ; Must preserve all registers.
  659. ; Installed as int 29h by dosfn0 (init).
  660.  
  661. int_29    proc    near
  662.     sti
  663.     push    ds
  664.     push    es
  665.     push_all
  666. if fast29
  667.     cmp    al, 20h            ; control char?
  668.     jb    slow_way
  669.     cmp    cs:escvector, 0        ; middle of an escape sequence?
  670.     jnz    slow_way
  671.     mov    dx, ABS40
  672.     mov    ds, dx            ; set addressability
  673.     assume    ds:ABS40
  674.     mov    cx, word ptr crt_mode    ; mode in cl, columns in ch
  675.     cmp    cl, 3            ; graphics mode?
  676.     ja    slow_way
  677.     xor    bx, bx            ; get cursor position
  678.     mov    bl, active_page
  679.     add    bx, bx
  680.     mov    dx, cursor_posn[bx]        ; dh has y, dl has x
  681.     inc    dl            ; point to next location
  682.     cmp    dl, ch            ; at edge?
  683.     jnb    slow_way
  684.                     ; we can go with it!
  685.     mov    cursor_posn[bx], dx    ; update pointer
  686.     xchg    ax, bx
  687.     mov    al, dh
  688.     mul    ch            ; ax has line offset
  689.     add    al, dl
  690.     adc    ah, 0            ; total offset
  691.     mov    cx, bx
  692.     mov    bx, ax            ; cl has character, bx offset
  693.  
  694.     mov    ax, crt_start
  695.     shr    ax, 1
  696.     add    bx, ax            ; corrected cursor offset, either
  697.                     ; because of fast scroll or
  698.                     ; page<>0
  699.  
  700.     mov    dx, port_6845        ; update cursor location
  701.     mov    al,0eh        ; more effective to write two bytes at a time
  702.     mov    ah,bh
  703.     out    dx,ax
  704.     inc    al
  705.     mov    ah,bl
  706.     out    dx,ax
  707.  
  708.     mov    al, cl            ; get back character
  709.  
  710.     dec    bx
  711.     add    bx, bx            ; byte offset
  712.     mov    dx, 0b800h        ; address screen
  713.     mov    ds, dx
  714.     assume    ds:nothing
  715.     mov    ah, cs:cur_attrib
  716.     mov    ds:[bx], ax        ; write character
  717.     jmp    short int_fin
  718. endif
  719. slow_way:
  720.     mov    cx, 1
  721.     mov    bx, cs
  722.     mov    es, bx
  723.     mov    si, offset int_29_buf
  724.     mov    byte ptr es:[si], al
  725.     call    dosfn8
  726. int_fin:
  727.     pop_all
  728.     pop    es
  729.     pop    ds
  730.     iret
  731. int_29    endp
  732.  
  733.  
  734.     page
  735. ;------ dosfn8 -------------------------------------------------------
  736. ; Handles writes to the device (with or without verify).
  737. ; Called with
  738. ;  CX     = number of bytes to write
  739. ;  ES:SI = transfer buffer
  740. ;  DS     = CS, so we can access local variables.  NOT ANY MORE
  741.  
  742. dosfn8    proc    near
  743.  
  744.     mov    cs:f_cptr_seg, es    ; save segment of char ptr
  745.  
  746.     ; Read the BIOS buffer address/cursor position variables.
  747.     mov    ax, ABS40
  748.     mov    ds, ax
  749.     assume    ds:ABS40
  750.  
  751.     ; Find current video mode and screen size.
  752.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  753.     mov    cs:video_mode, al
  754.     dec    ah            ; ah = max column
  755.     mov    cs:max_x, ah
  756.  
  757.     ; Save graphics mode flag
  758.     call    set_gmode
  759.  
  760.     mov    al, ega_rows        ; number of display rows
  761.     mov    cs:max_y, al        ; set maxy value
  762.  
  763.     ; Find current cursor coordinates.
  764.  
  765.     mov    al, active_page
  766.     cbw
  767.     add    ax, ax
  768.     xchg    bx, ax
  769.     mov    ax, cursor_posn[bx]
  770.     mov    cs:cur_coords, ax
  771.  
  772.     ; Find video buffer segment address; adjust it
  773.     ; so the offset is zero; return in AX.
  774.  
  775.     mov    ax, 0B800H        ; this gets corrected in xy_to_regs
  776.                     ; if we are not screen 0
  777.     push    cs
  778.     pop    ds
  779.     assume    ds:CODE
  780.     mov    es, ax
  781.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  782.  
  783.     ; | If in graphics mode, clear old pseudocursor
  784.  
  785.     cmp    gmode_flag, 0
  786.     jz    d8_no_cp
  787.     cmp    no_c_flag, 0    ; cursor not previously drawn?
  788.     mov    no_c_flag, 0    ; (reset flag, will be drawn from now on)
  789.     jnz    d8_no_cp    ; not drawn -- don't clear
  790.     cmp    gcursor, 0    ; don't clear if cursor is off, either
  791.     jz    d8_no_cp
  792.     push    cx
  793.     push    dx
  794.     push    di
  795.     draw_gcursor
  796.     pop    di
  797.     pop    dx
  798.     pop    cx
  799.  
  800. d8_no_cp:
  801.  
  802.     mov    ah, cur_attrib
  803.     mov    ds, f_cptr_seg        ; get segment of char ptr
  804.     assume    ds:nothing
  805.     cld                ; make sure we'll increment
  806.  
  807.     ; The Inner Loop: 12+4+4+11+14+2+19= 66 cycles/loop
  808.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  809.     ; At that speed, it takes 32 milliseconds to fill a screen.
  810.  
  811.     ; Get a character, put it on the screen, repeat 'til end of line
  812.     ; or no more characters.
  813.     jcxz    f_loopdone        ; if count = 0, we're already done.
  814.     cmp    cs:escvector, 0        ; If in middle of an escape sequence,
  815.     jz    f_tloop
  816.     jmp    f_in_escape        ; jump to escape sequence handler.
  817.  
  818. f_tloop:; | If in graphics mode, jump to alternate loop
  819.     ; | What a massive kludge!  A better approach would have been
  820.     ; | to collect characters for a "write n chars" routine
  821.     ; | which would handle both text and graphics modes.
  822.     cmp    cs:gmode_flag,0
  823.     jz    f_t_cloop
  824.     jmp    f_g_cloop
  825.  
  826. f_t_cloop:
  827.     LODSB                ; get char! (al = ds:[si++])
  828.     cmp    al, 28            ; is it a control char?
  829.     jb    f_control        ;  maybe...
  830. f_t_nctl:
  831.     STOSW                ; Put Char! (es:[di++] = ax)
  832.     dec    dx            ; count down to end of line
  833.     loopnz    f_t_cloop        ; and go back for more.
  834.     jz    f_t_at_eol        ; at end of line, maybe do a crlf.
  835.     jmp    short f_loopdone    ; finished execution
  836.  
  837. f_looploop:
  838. f_ansi_exit:                ; in case we switched into
  839.     loopnz    f_tloop            ; a graphics mode
  840.     jnz    f_loopdone
  841. f_t_at_eol:
  842.     jmp    f_at_eol
  843.  
  844. f_loopdone:
  845.  
  846.     ;--------- All done with write request -----------
  847.     ; DI is cursor address, cursor position in cur_y, dl
  848.  
  849.     assume    ds:ABS40
  850.     mov    ax, ABS40
  851.     mov    ds, ax
  852.  
  853.     ; Set cursor position in low memory.
  854.  
  855.     mov    al,active_page
  856.     cbw
  857.     add    ax,ax
  858.     xchg    bx,ax
  859.     mov    al, cs:max_x
  860.     inc    al
  861.     sub    al, dl
  862.     mov    ah, cs:cur_y
  863.     mov    cursor_posn[bx],ax
  864.  
  865.     cmp    cs:gmode_flag,0
  866.     jnz    pseudocursor        ; In graphics mode, there is
  867.                     ; a pseudo cursor to draw.
  868.  
  869.     ; Write directly to 6845 cursor address register.
  870.     mov    bx, di
  871.     shr    bx, 1            ; convert word index to byte index
  872.  
  873.     mov    dx, port_6845    ; works with or without no-mono
  874.  
  875.     mov    al,0eh        ; more effective to write two bytes at a time
  876.     mov    ah,bh
  877.     out    dx,ax
  878.     inc    al
  879.     mov    ah,bl
  880.     out    dx,ax
  881.  
  882.  
  883.     ; Return to DOS.
  884.     xor    ax, ax            ; No error, not busy.
  885.     ret
  886.  
  887. pseudocursor:
  888.     cmp    cs:gcursor, 0    ; graphics cursor off?
  889.     jz    nopseudo
  890.     mov    cs:no_c_flag,0    ; there is a cursor now!
  891.     draw_gcursor
  892. nopseudo:
  893.     xor    ax, ax
  894.     ret
  895.  
  896.     ;---- handle control characters ----
  897.     ; Note: cur_x is not kept updated in memory, but can be
  898.     ; computed from max_x and dx.
  899.     ; Cur_y is kept updated in memory.
  900. f_escapex:                ; far jump
  901.     jmp    f_escape
  902.  
  903. f_control:
  904.     cmp    al, 27            ; Is it an escape?
  905.     jz    f_escapex
  906.     cmp    al, 13            ; carriage return?
  907.     jz    f_cr
  908.     cmp    al, 10            ; line feed?
  909.     jz    f_lf
  910.     cmp    al, 8            ; backspace?
  911.     jz    f_bs
  912.     cmp    al, 9            ; tab?
  913.     jz    f_tab
  914.     cmp    al, 7            ; bell
  915.     jz    f_bell
  916.     jmp    f_nctl            ; then it is not a control char.
  917.  
  918. f_bell: ;----- Handle bell ----------------------
  919.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  920.     call    beep
  921.     or    al, al            ; clear z
  922.     jmp    f_looploop        ; Let main loop decrement cx.
  923.  
  924. f_bs:    ;----- Handle backspace -----------------
  925.     ; Moves cursor back one space without erasing.    No wraparound.
  926.     cmp    dl, cs:max_x        ; wrap around to previous line?
  927.     ja    fbs_wrap        ; yep; disallow it.
  928.     dec    di            ; back up one char & attrib,
  929.     dec    di
  930.     inc    dx            ; and note one more char left on line.
  931. fbs_wrap:
  932.     jmp    f_looploop
  933.  
  934. f_cr:    ;----- Handle carriage return -----------
  935.     ; di -= cur_x<<1;        set di= address of start of line
  936.     ; dx=max_x+1;            set bx= chars left in line
  937.     mov    al, cs:max_x
  938.     inc    al
  939.     sub    al, dl            ; Get cur_x into ax.
  940.     mov    ah, 0
  941.     sub    di, ax
  942.     sub    di, ax
  943.     mov    dl, cs:max_x        ; Full line ahead of us.
  944.     inc    dx
  945.     mov    ah, cs:cur_attrib    ; restore current attribute
  946.     or    al, 1            ; clear z
  947.     jmp    f_looploop        ; and let main loop decrement cx
  948.  
  949. f_at_eol:
  950.     ;----- Handle overrunning right end of screen -------
  951.     ; cx++;                compensate for double loop
  952.     ; if (!wrap_flag) { dx++; di-=2; }
  953.     ; else do_crlf;
  954.     inc    cx
  955.     test    cs:wrap_flag, 1
  956.     jnz    feol_wrap
  957.         dec    di
  958.         dec    di
  959.         inc    dx
  960.         jmp    f_looploop
  961. feol_wrap:
  962.     ; dx=max_x+1;            set bx= chars left in line
  963.     ; di -= 2*(max_x+1);
  964.     ; do_lf
  965.     mov    dl, cs:max_x
  966.     inc    dx
  967.     sub    di, dx
  968.     sub    di, dx
  969.     ; fall thru to line feed routine
  970.  
  971. f_lf:    ;----- Handle line feed -----------------
  972.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  973.     ; else { cur_y++; di += max_x<<1;    else increment Y
  974.  
  975.     mov    al, cs:max_y
  976.     cmp    cs:cur_y, al
  977.     jb    flf_noscroll
  978.         call    scroll_up        ; preserves bx,cx,dx,si,di
  979.         jmp    short flf_done
  980. flf_noscroll:
  981.     inc    cs:cur_y
  982.     mov    al, cs:max_x
  983.     mov    ah, 0
  984.     inc    ax
  985.     add    ax, ax
  986.     add    di, ax
  987. flf_done:
  988.     mov    ah, cs:cur_attrib        ; restore current attribute
  989.     or    al, 1            ; clear z
  990.     jmp    f_looploop        ; and let main loop decrement cx
  991.  
  992. f_tab:    ;----- Handle tab expansion -------------
  993.     ; Get cur_x into al.
  994.     mov    al, cs:max_x
  995.     inc    al
  996.     sub    al, dl
  997.     ; Calculate number of spaces to output.
  998.     push    cx            ; save cx
  999.     mov    ch, 0
  1000.     mov    cl, al            ; get zero based x coordinate
  1001.     and    cl, 7
  1002.     neg    cl
  1003.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  1004.     sub    dx, cx            ; update chars-to-eol, maybe set z
  1005.     pushf                ; || save Z for main loop
  1006.     ; ah is still current attribute.  Move CX spaces to the screen.
  1007.     mov    al, ' '
  1008.     cmp    cs:gmode_flag,0
  1009.     jnz    f_tab_putc
  1010.  
  1011.     REP    STOSW
  1012.     popf                ; || restore Z flag for main loop test
  1013.     pop    cx            ; restore cx
  1014.     jmp    f_looploop        ; Let main loop decrement cx.
  1015.  
  1016. ;--------------- graphics mode support -----------------------
  1017.  
  1018. ;---- Alternate main loop for graphics mode ----
  1019. f_g_cloop:
  1020.     LODSB                ; get char! (al = ds:[si++])
  1021.     cmp    al, 28            ; is it a control char?
  1022.     jb    f_g_control        ;  maybe...
  1023. f_g_nctl:
  1024.     call    putchar
  1025.     dec    dx            ; count down to end of line
  1026.     loopnz    f_g_cloop        ; and go back for more.
  1027.     jz    f_at_eol        ; at end of line; maybe do a crlf.
  1028.     jmp    f_loopdone
  1029.  
  1030. f_g_control:    jmp    f_control
  1031.  
  1032. ; Tabs in graphic mode
  1033. f_tab_putc:    ; graphics mode- call putc to put the char
  1034.     add    dx, cx            ; move back to start of tab
  1035. f_tp_lp:
  1036.     call    putchar
  1037.     dec    dx            ; go to next cursor position
  1038.     loop    f_tp_lp
  1039.     popf                ; Z set if wrapped around EOL
  1040.     pop    cx
  1041.     jmp    f_looploop
  1042.  
  1043. ;---- Where to go when a character turns out not to be special
  1044. f_nctl:
  1045. f_not_ansi:
  1046.     cmp    cs:gmode_flag,0
  1047.     jnz    f_g_nctl
  1048.     jmp    f_t_nctl        ; text mode
  1049.  
  1050. page
  1051. ;---- putchar ------------------------------------------------
  1052. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  1053. ; On entry, registers set up as per xy_to_regs.
  1054. ; Preserves all registers.
  1055. putchar proc    near
  1056.     push    dx
  1057.     push    cx
  1058.     push    bx
  1059.     push    ax
  1060.     ; 1. Set cursor position.
  1061.     mov    al, cs:max_x
  1062.     inc    al
  1063.     sub    al, dl
  1064.     mov    cs:cur_x, al
  1065.     mov    dx, cs:cur_coords    ; get X & Y into DX
  1066.     push    ds
  1067.     mov    ax, 40h
  1068.     mov    ds, ax
  1069.     assume    ds:ABS40
  1070.     mov    cursor_posn,dx
  1071.     pop    ds
  1072.     assume    ds:nothing
  1073.     xor    bx, bx            ; choose dpy page 0
  1074.     mov    ah, 2            ; chose "Set Cursor Position"
  1075.     call_video
  1076.     ; 2. Write char & attribute.
  1077. IF quick_char
  1078.     pop    ax
  1079.     push    ax            ; character and attribute
  1080.     call    quick_graph
  1081. ELSE
  1082.     mov    cx, 1
  1083.     pop    ax            ; get char in AL
  1084.     push    ax
  1085.     mov    bl, ah            ; attribute in BL
  1086.     mov    bh, 0
  1087.     mov    ah, 9
  1088.     call_video
  1089. ENDIF
  1090.     pop    ax
  1091.     pop    bx
  1092.     pop    cx
  1093.     pop    dx
  1094.     ret
  1095. putchar endp
  1096. page
  1097. IF quick_char
  1098. quick_graph    proc    near
  1099. ; this code has been reworked for much greater speed.
  1100.  
  1101. ; ah= mode, al= char, ax,bx,cx,dx destroyed
  1102.     gmode_test yesQuick
  1103.  
  1104.     mov    bl,ah
  1105.     xor    bh,bh
  1106.     mov    cx, 1
  1107.     mov    ah, 9
  1108.     call_video        ; do it the old way
  1109.     ret
  1110.  
  1111. yesQuick:
  1112.     push    ds
  1113.     mov    bx, 40h
  1114.     mov    ds, bx
  1115.     assume    ds:ABS40        ; address abs segment
  1116.     push    es
  1117.     push    bp
  1118.     push    si
  1119.     push    di            ; save some registers
  1120.     push    ax            ; save char and mode
  1121.     
  1122.     mov    ax, crt_cols
  1123.     mov    cx, ega_points        ; pixel rows in character
  1124.     mov    bp, ax            ; save number of columns=#bytes
  1125.     mul    byte ptr (cursor_posn+1)
  1126.     mul    cx            ; (ignore upper product in DX)
  1127.     add    al, byte ptr (cursor_posn)    ; y*#cols*#rows + x
  1128.     adc    ah, 0            ; take care of carry
  1129.     mov    si, ax            ; save address in si
  1130.     xor    ax, ax
  1131.     mov    es, ax            ; absolute zero
  1132.     les    di, es: dword ptr (43h * 4)    ; contents of vector 43h
  1133.     pop    ax
  1134.     push    ax            ; get char and mode
  1135.     mul    cl            ; offset to character in table
  1136.     add    di,ax            ; di has character bit pattern start
  1137.     mov    ax, 0a000h        ; address of display segment
  1138.     mov    ds, ax
  1139.     assume    ds:nothing
  1140.  
  1141. ; to recap: cx=#rows, bp=#columns, ds:si=display address, es:di=character addr
  1142.     mov    dx, 3ceh
  1143.     mov    ax, 0a05h
  1144.     out    dx,ax            ; set write mode 2, read mode 1
  1145.  
  1146.     mov    ax, 7            ; set color dontcare register to zero
  1147.     out    dx,ax
  1148.  
  1149.     pop    bx            ; character mode in bh
  1150. IF gbackground
  1151.     mov    bl,bh            ; extract background color
  1152. IF cheap_pc
  1153.     shr    bl,1
  1154.     shr    bl,1
  1155.     shr    bl,1
  1156.     shr    bl,1
  1157. ELSE
  1158.     shr    bl,4
  1159. ENDIF
  1160.     or    bh, bh
  1161.     jns    overMode
  1162.     mov    ax, 1803h        ; exor mode
  1163.     out    dx,ax
  1164.  
  1165.     and    bx, 0f07h        ; xor=blink bit
  1166. ELSE
  1167.     or    bh, bh
  1168.     jns    overMode
  1169.     mov    ax, 1803h        ; exor mode
  1170.     out    dx,ax
  1171.  
  1172.     and    bx, 7f00h        ; mask off xor bit
  1173. ENDIF
  1174.     mov    al, 8            ; bit mask register
  1175.     out    dx, al
  1176.     inc    dx
  1177. chLoop:
  1178.     mov    al, es:[di]        ; get pixel pattern
  1179.     out    dx, al
  1180.     and    [si],bh            ; update foreground
  1181.     not    al
  1182.     out    dx, al                 ; and background
  1183.     and    [si],bl
  1184.     inc    di
  1185.     add    si, bp            ; go to next character byte and line
  1186.     loop    chLoop
  1187.     
  1188. joinret:
  1189.     dec    dx
  1190.     mov    ax, 0ff08h        ; bit mask
  1191.     out    dx, ax
  1192.     mov    ax, 5            ; mode register
  1193.     out    dx, ax
  1194.     mov    al, 3            ; (ah is zero)
  1195.     out    dx, ax
  1196.     mov    ax, 0f07h
  1197.     out    dx, ax
  1198.  
  1199.     pop    di
  1200.     pop    si
  1201.     pop    bp
  1202.     pop    es
  1203.     pop    ds
  1204.     ret
  1205.  
  1206. overMode:
  1207. IF gbackground
  1208.     and    bx, 0f07h        ; xor=blink bit
  1209. ELSE
  1210.     and    bx, 7f00h        ; mask off xor bit
  1211. ENDIF
  1212.     mov    al, 8            ; bit mask register
  1213.     out    dx, al
  1214.     inc    dx
  1215.     ; we need to load the internal buffer with a solid
  1216.     ; background. By writing a solid background and then
  1217.     ; reading it back, we can do the job.
  1218.     mov    al, 0ffh        ; force set background
  1219.     out    dx, al
  1220.     mov    [si], bl
  1221.     mov    al, [si]        ; read reset pattern
  1222. chLoop2:
  1223.     mov    al, es:[di]        ; get pixel pattern
  1224.     out    dx, al
  1225.     mov    [si],bh            ; update foreground
  1226.     inc    di
  1227.     add    si, bp            ; go to next character byte and line
  1228.     loop    chLoop2
  1229.     jmp    joinret
  1230.  
  1231. quick_graph endp
  1232. ENDIF
  1233.  
  1234.  
  1235. ;--------------- end of graphics mode support --------------------
  1236.  
  1237. dosfn8    endp
  1238. page
  1239. ;--- get_blank_attrib ------------------------------------------------
  1240. ; Determine new attribute and character for a new blank region.
  1241. ; Use current attribute, just disallow blink and underline.
  1242. ; (Pretty strange way to do it.     Might want to disallow rev vid, too.)
  1243. ; Returns result in AH, preserves all other registers.
  1244. get_blank_attrib    proc    near
  1245. IF gbackground
  1246.     cmp    cs:gmode_flag,0
  1247.     jz    get_attrib        ; if alpha mode
  1248.     gmode_test get_attribg        ; or good graphic mode, get attrib
  1249.     xor    ah,ah
  1250.     ret
  1251. get_attribg:
  1252.     mov    ah, cs:cur_attrib    ; must do different technique
  1253. IF cheap_pc
  1254.     shr    ah,1            ; color must be shifted into position
  1255.     shr    ah,1
  1256.     shr    ah,1
  1257.     shr    ah,1
  1258. ELSE
  1259.     shr    ah,4
  1260. ENDIF
  1261.     and    ah,07
  1262.     ret
  1263. get_attrib:
  1264.     mov    ah, cs:cur_attrib    ; the attribute
  1265.     and    ah, 7fh            ;  but disallowing blink
  1266.     ret
  1267. ELSE
  1268.     mov    ah, 0            ; 0 is background if graphics mode
  1269.     cmp    cs:gmode_flag,0
  1270.     jnz    gb_aok
  1271.  
  1272.     mov    ah, cs:cur_attrib
  1273.     and    ah, 7fh            ; disallow blink
  1274. gb_aok: ret
  1275. ENDIF
  1276. get_blank_attrib    endp
  1277.  
  1278. page
  1279. ;---- scroll_up ---------------------------------------------------
  1280. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  1281. ; Moves screen up 1 line, fills the last line with blanks.
  1282. ; Attribute of blanks is the current attribute sans blink and underline.
  1283.  
  1284. scroll_up    proc    near
  1285.     push_all
  1286.  
  1287.     cmp    cs:gmode_flag,0
  1288.     jz    scroll_char
  1289.     jmp    scroll_graphic
  1290. scroll_char:
  1291.     push    es
  1292.     push    ds            ; save all!
  1293.     mov    ax, ABS40        ; address low mem via ds
  1294.     mov    ds, ax
  1295.     mov    ax, 0b800h        ; address display via es
  1296.     mov    es, ax
  1297.     assume    ds:ABS40
  1298.     cmp    cs:fmode,0        ; see if in fast mode
  1299.     jz    slow_scroll_up
  1300.     xor    ax,ax            ; calc addresses
  1301.     mov    al, cs:max_x
  1302.     inc    ax
  1303.     mov    cx, ax            ; save (word) count for fill
  1304.     mov    bx, ax            ; and save byte count
  1305.     shl    bx, 1            ; byte count
  1306.     mov    cs:temp_val, bx
  1307.     mul    cs:max_y        ; address offset of last line (words)
  1308.     shl    ax, 1            ; address offset in bytes
  1309.     mov    di, ax
  1310.     
  1311.     mov    ax, crt_start        ; start of display
  1312.     add    ax, bx            ; add line size in bytes
  1313.     add    di, ax            ; di is now address of new last line
  1314.     cmp    di, 7fffh - 264        ; is there room here?
  1315.     ja    no_room_here
  1316.  
  1317.     mov    crt_start, ax
  1318.     shr    ax, 1            ; make into word offset
  1319.     mov    bx, ax            ; and put into 6845
  1320.     mov    dx, port_6845
  1321.     mov    al, 0ch
  1322.     out    dx, ax
  1323.     inc    al
  1324.     mov    ah, bl
  1325.     out    dx, ax
  1326.     
  1327.     mov    ah, cs:cur_attrib
  1328.     and    ah, 7fh            ; disallow blink
  1329.     mov    al, 20h            ; blank
  1330.     rep stosw            ; clear line
  1331.  
  1332.     assume    ds:nothing
  1333.     pop    ds
  1334.     pop    es
  1335.     pop_all
  1336.     
  1337.     add    di, cs:temp_val
  1338.     ret
  1339.  
  1340. no_room_here:
  1341.     pop    ds            ; restore registers
  1342.     pop    es
  1343.     pop_all
  1344.     call    move_back        ; go to buffer start
  1345.     sub    di, cs:temp_val
  1346.     jmp    scroll_up        ; try again
  1347.  
  1348. slow_scroll_up:
  1349.     assume    ds:ABS40
  1350.     mov    di, crt_start        ; offset of display (because of
  1351.                     ;   different page)
  1352.     mov    ds, ax            ; ds is now display
  1353.     assume    ds:nothing
  1354.     xor    ax,ax            ; calc addresses
  1355.     mov    al, cs:max_x
  1356.     inc    ax
  1357.     mov    bx, ax            ; save (word) count
  1358.     shl    ax, 1            ; byte count
  1359.     mov    si, ax            ; start address is second line
  1360.     add    si, di            ; adjust start address by any offset
  1361.     mov    ax, bx
  1362.     mul    cs:max_y        ; number of words to move
  1363.     mov    cx, ax
  1364.     rep movsw            ; move them!
  1365.     mov    cx, bx            ; words to clear
  1366.           mov    ah, cs:cur_attrib
  1367.     and    ah, 7fh            ; disallow blink
  1368.     mov    al, 20h            ; blank
  1369.     rep stosw            ; clear line
  1370.     pop    ds
  1371.     pop    es
  1372.     pop_all
  1373.     ret
  1374.  
  1375. scroll_graphic:
  1376.     
  1377.     gmode_test scrOurself
  1378.     mov    bh, 0
  1379.     mov    al, 1            ; AL is number of lines to scroll.
  1380.     mov    ah, 6            ; BIOS: scroll up
  1381.     xor    cx, cx
  1382.     mov    dl, cs:max_x        ; lower-rite-x
  1383.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  1384.     call_video            ; call BIOS to scroll a rectangle.
  1385.  
  1386. scrret:
  1387.     pop_all
  1388.     ret
  1389.  
  1390. scrOurself:    ; try scrolling screen ourself!
  1391.     push    es
  1392.     push    ds
  1393.  
  1394.     mov    dx, 3ceh        ; set write mode 1
  1395.     mov    ax, 105h
  1396.     out    dx, ax
  1397.  
  1398.     mov    ax, 40h            ; address abs40 segment
  1399.     mov    ds, ax
  1400.     assume    ds:ABS40
  1401.     mov    ax, crt_cols        ; calculate length of line in bytes
  1402.     mul    byte ptr ega_points
  1403.     mov    si, ax            ; source of move
  1404.     xor    dx,dx
  1405.     mov    dl, ega_rows
  1406.     mul    dx            ; number of bytes to move
  1407.     mov    cx, ax
  1408.     mov    ax, si            ; save bytes in line for later
  1409.  
  1410.     mov    bx, 0a000h        ; address display
  1411.     mov    ds, bx
  1412.     mov    es, bx
  1413.  
  1414.     xor    di, di            ; destination of move
  1415.     rep movsb            ; scroll
  1416.     
  1417.     mov    cx, ax            ; bytes in line = bytes to clear
  1418.  
  1419.     mov    dx, 3ceh
  1420.     mov    ax, 05h            ; return to write mode 0
  1421.     out    dx, ax
  1422.  
  1423. IF gbackground
  1424.     mov    ah, cs:cur_attrib
  1425. IF cheap_pc
  1426.     shr    ah,1
  1427.     shr    ah,1
  1428.     shr    ah,1
  1429.     shr    ah,1
  1430. ELSE
  1431.     shr    ah,4
  1432. ENDIF
  1433.     and    ah,07            ; background color
  1434.     mov    al,0
  1435.     out    dx,ax            ; set color to write
  1436.  
  1437.     mov    ax,0f01h        ; set mask
  1438.     out    dx,ax
  1439.  
  1440.     rep    stosb            ; clear the line
  1441.  
  1442.     mov    ax,0001            ; reset mask
  1443.     out    dx,ax
  1444. ELSE
  1445.     xor    ax, ax
  1446.     rep stosb            ; clear the line
  1447. ENDIF
  1448.  
  1449.     pop    ds            ; restore registers and return
  1450.     pop    es
  1451.     jmp    scrret
  1452.  
  1453. scroll_up    endp
  1454. page
  1455. ;-----move_back --------------------------------------------
  1456. ; This routine moves the display to offset zero.
  1457. ; alters:
  1458. ; cs:temp_val = original crt_start value
  1459. ; crt_start = 0
  1460. ; controller reset properly
  1461. move_back proc near
  1462.     push    ds
  1463.     push    es
  1464.     push_all
  1465.     mov    ax, ABS40
  1466.     mov    ds, ax
  1467.  
  1468.     assume    ds:ABS40
  1469.     mov    al, ega_rows
  1470.     inc    al
  1471.     mul    byte ptr crt_cols    ; words to move
  1472.     mov    cx, ax
  1473.     mov    si, crt_start
  1474.     mov    cs:temp_val, si        ; save this value
  1475.     xor    di, di
  1476.     mov    crt_start, di
  1477.     mov    bx, cursor_posn        ; y in bh, x in bl
  1478.     mov    al, byte ptr crt_cols
  1479.     mul    bh
  1480.     add    al, bl
  1481.     adc    ah, 0
  1482.     xchg    bx, ax            ; save cursor position in bx
  1483.     
  1484.     mov    ax, 0B800h
  1485.     mov    es, ax
  1486.     mov    ds, ax
  1487.  
  1488.     mov    dx, cx
  1489.     add    dx, cx            ; see if overlapping
  1490.     cmp    dx, si
  1491.     ja    slow_move
  1492. join_move:
  1493.     cld
  1494.     rep movsw            ; move data
  1495.     
  1496.     mov    dx, port_6845
  1497.     mov    al, 0ch            ; reset offset
  1498.     xor    ah,ah
  1499.     out    dx, ax
  1500.     inc    al
  1501.     out    dx, ax
  1502.     inc    al
  1503.     mov    ah, bh
  1504.     out    dx, ax
  1505.     inc    al
  1506.     mov    ah, bl
  1507.     out    dx, ax
  1508. ;    sti
  1509.     assume    ds:nothing
  1510.     pop_all
  1511.     pop    es
  1512.     pop    ds
  1513.     ret
  1514.  
  1515. slow_move:    ; we gotta move to another spot first
  1516.     push    cx        ; save length
  1517.     dec    dx        ; length-2
  1518.     dec    dx
  1519.     add    si, dx        ; point to end
  1520.     mov    di, 7FFEh    ; safe location -- as safe as we can get
  1521.     std
  1522.     rep movsw        ; move from far end in case of overlap
  1523.                 ; (may happen on large displays)
  1524.     mov    dx, port_6845
  1525.     mov    si, di        ; source becomes destination
  1526.     inc    si        ; take care of last decrement
  1527.     inc    si
  1528.     mov    cx, si
  1529.     shr    cx, 1        ; word offset to start of new area
  1530.     mov    al, 0Ch        ; display at this new location
  1531.     mov    ah, ch
  1532.     out    dx, ax
  1533.     inc    al
  1534.     mov    ah, cl
  1535.     out    dx, ax
  1536.     pop    cx        ; reset all registers
  1537.     xor    di, di        ; destination is zero
  1538.     jmp    join_move    ; NOW move to destination
  1539.     
  1540. move_back    endp
  1541. page
  1542. if key_redef
  1543. ;---- lookup -----------------------------------------------
  1544. ; Called by getchar, peekchar, and key to see if a given key has
  1545. ; been redefined.
  1546. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  1547. ; Returns with Z cleared if no redefinition; otherwise,
  1548. ; Z is set, SI points to redefinition string, CX is its length.
  1549. ; Preseves AL, all but CX and SI.
  1550. ; Redefinition table organization:
  1551. ;  Strings are stored in reversed order, first char last.
  1552. ;  The word following the string is the character to be replaced;
  1553. ;  the next word is the length of the string sans header.
  1554. ; param_end points to the last byte used by the parameter buffer;
  1555. ; redef_end points to the last word used by the redef table.
  1556.  
  1557. lookup    proc    near
  1558.     mov    si, redef_end        ; Start at end of table, move down.
  1559.     or    al, al
  1560.     jz    lu_lp
  1561.     mov    ah, 0            ; clear extraneous scan code
  1562. lu_lp:    cmp    si, param_end
  1563.     jbe    lu_notfound        ; If below redef table, exit.
  1564.     mov    cx, [si]
  1565.     cmp    ax, [si-2]        ; are you my mommy?
  1566.     jz    lu_gotit
  1567.     sub    si, 4
  1568.     sub    si, cx            ; point to next header
  1569.     jmp    lu_lp
  1570. lu_notfound:
  1571.     or    si, si            ; clear Z
  1572.     jmp    short lu_exit
  1573. lu_gotit:
  1574.     sub    si, 2
  1575.     sub    si, cx            ; point to lowest char in memory
  1576.     cmp    al, al            ; set Z
  1577. lu_exit:
  1578.     ret
  1579. lookup    endp
  1580. endif
  1581. page
  1582. ;---- searchbuf --------------------------------------------
  1583. ; Called by getchar and peekchar to see if any characters are
  1584. ; waiting to be gotten from sources other than BIOS.
  1585. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  1586. searchbuf    proc    near
  1587.     ; Search the stuffahead buffers.
  1588. if key_redef
  1589.     mov    cx, 4            ; number of buffers to check for chars
  1590. else
  1591.     mov    cx, 3
  1592. endif
  1593.     mov    bx, offset fnkey - 4
  1594. sbloop: add    bx, 4            ; point to next buffer record
  1595.     mov    si, [bx].len
  1596.     or    si, si            ; empty?
  1597.     loopz    sbloop            ; if so, loop.
  1598.     ret
  1599. searchbuf    endp
  1600. page
  1601. ;---- getchar -----------------------------------------------
  1602. ; Returns AL = next char.
  1603. ; Trashes AX, BX, CX, BP, SI.
  1604. getchar proc    near
  1605. gc_searchbuf:
  1606.     ; See if any chars are waiting in stuffahead buffers.
  1607.     call    searchbuf
  1608.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  1609.     ; A nonempty buffer was found.
  1610.     dec    [bx].len
  1611.     dec    si
  1612.     mov    bp, [bx].adr        ; get pointer to string
  1613.     mov    al, byte ptr ds:[bp][si]; get the char
  1614.     ; Recognize function key sequences, move them to highest priority
  1615.     ; queue.
  1616.     sub    si, 1            ; set carry if si=0
  1617.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  1618.     cmp    bx, offset fnkey
  1619.     jz    gc_nofnkey        ; already highest priority -> done.
  1620.     or    al, al
  1621.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  1622.     ; Found a function key; move it to highest priority queue.
  1623.     dec    [bx].len
  1624.     mov    ah, byte ptr ds:[bp][si]; gec      [bx].len
  1625.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  1626. gc_fnkey:
  1627.     mov    fnkey.len, 1
  1628.     mov    fnkeybuf, ah        ; save it.
  1629. gc_nofnkey:
  1630.     ; Valid char in AL.  Return with it.
  1631.     jmp    short gcdone
  1632.  
  1633. gc_trykbd:
  1634.     ; Actually get a character from the keyboard.
  1635.     mov    ah, 0
  1636.     int    16h            ; BIOS returns with char in AX
  1637.     ; If it's Ctrl-break, it has already been taken care of.
  1638.     or    ax, ax
  1639.     jz    gc_trykbd
  1640.  
  1641. if key_redef
  1642.     ; Look in the reassignment table to see if it needs translation.
  1643.     call    lookup            ; Z=found; CX=length; SI=ptr
  1644.     jnz    gc_noredef
  1645.     ; Okay; set up the reassignment, and run thru the translation code.
  1646.     mov    xlatseq.len, cx
  1647.     mov    xlatseq.adr, si
  1648.     jmp    gc_searchbuf
  1649. endif
  1650. gc_noredef:
  1651.     ; Is it a function key?
  1652.     cmp    al, 0
  1653.     jz    gc_fnkey        ; yep- special treatment.
  1654. gcdone: ret    ; with character in AL.
  1655.  
  1656. getchar endp
  1657. page
  1658. ;---- peekchar -----------------------------------------------
  1659. ; Returns Z if no character ready, AL=char otherwise.
  1660. ; Trashes AX, BX, CX, BP, SI.
  1661. peekchar    proc    near
  1662.     call    searchbuf
  1663.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1664.     ; A nonempty buffer was found.
  1665.     dec    si
  1666.     mov    bp, [bx].adr        ; get pointer to string
  1667.     mov    al, byte ptr ds:[bp][si]; get the char
  1668.     ; Valid char from buffer in AL.     Return with it.
  1669.     jmp    short pcdone
  1670. pc_trykbd:
  1671.     ; Actually peek at the keyboard.
  1672.     mov    ah, 1
  1673.     int    16h            ; BIOS returns with char in AX
  1674.     jz    pcexit
  1675.     ; If it's control-break, it's already been taken care of.
  1676.     or    ax, ax
  1677.     jnz    pc_notbrk
  1678.     mov    ah, 0
  1679.     int    16h            ; so get rid of it!
  1680.     jmp    short pc_trykbd
  1681. pc_notbrk:
  1682. if key_redef
  1683.     ; Look in the reassignment table to see if it needs translation.
  1684.     call    lookup            ; Z=found; CX=length; SI=ptr
  1685.     jnz    pcdone            ; Nope; just return the char.
  1686.     ; Okay; get the first code to be returned.
  1687.     add    si, cx
  1688.     mov    al, [si-1]
  1689. endif
  1690. pcdone: or    ah, 1            ; NZ; char ready!
  1691. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1692. peekchar    endp
  1693. page
  1694. ;----- set_gmode ------------------------------------------------
  1695. ; Set gmode_flag based on mode byte in register al
  1696. set_gmode proc  near
  1697.     gmode_code            ; a macro in nnansi_d.asm
  1698.     ret
  1699. set_gmode    endp
  1700.  
  1701.  
  1702. ;---- beep ------------------------------------------------------
  1703. ; Beep speaker; period given by beep_div, duration by beep_len.
  1704. ; Preserves all registers.
  1705.  
  1706. beep_div    equ    1300        ; fairly close to IBM beep
  1707. beep_len    equ    3        ; 3/18 sec- shorter than IBM
  1708.  
  1709. beep    proc    near
  1710.     push_all
  1711.  
  1712.     mov    al, 10110110b        ; select 8253
  1713.     mov    dx, 43h            ; control port address
  1714.     out    dx, al
  1715.     dec    dx            ; timer 2 address
  1716.     mov    ax, beep_div
  1717.     jmp    $+2
  1718.     out    dx, al            ; low byte of divisor
  1719.     xchg    ah, al
  1720.     jmp    $+2
  1721.     out    dx, al            ; high byte of divisor
  1722.     mov    dx, 61h
  1723.     jmp    $+2
  1724.     in    al, dx            ; get current value of control bits
  1725.     push    ax
  1726.     or    al, 3
  1727.     jmp    $+2
  1728.     out    dx, al            ; turn speaker on
  1729.  
  1730.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1731.     push    es
  1732.     mov    ax, ABS40
  1733.     mov    es, ax
  1734.     assume    es:ABS40
  1735.     mov    bx, timer_low
  1736.     mov    cx, -1
  1737. beeplp: mov    ax, timer_low
  1738.     sub    ax, bx
  1739.     cmp    ax,     beep_len
  1740.     jg    beepover
  1741.     loop    beeplp
  1742. beepover:
  1743.     pop    es
  1744.     assume    es:CODE
  1745.  
  1746.     ; Turn off speaker
  1747.     pop    ax
  1748.     and    al, not 3        ; turn speaker off
  1749.     out    dx, al
  1750.     pop_all
  1751.     ret
  1752. beep    endp
  1753. CODE    ends
  1754.  
  1755.     end                ; of nansi.asm
  1756.  
  1757.