home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / vga / vansi.arc / NANSI.ASM < prev    next >
Assembly Source File  |  1989-11-19  |  37KB  |  1,048 lines

  1.         page    66, 132
  2. ;--- nansi.asm ----------------------------------------------------------
  3. ; New ANSI terminal driver.
  4. ; Optimized for speed in the case of multi-character write requests.
  5. ; (C) 1986 Daniel Kegel, Pasadena, CA
  6. ; May be distributed for educational and personal use only
  7. ; The following files make up the driver:
  8. ;       nansi.asm   - all DOS function handlers except init
  9. ;       nansi_p.asm - parameter parser for ANSI escape sequences
  10. ;       nansi_f.asm - ANSI command handlers
  11. ;       nansi_i.asm - init DOS function handler
  12. ;
  13. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  14. ; Revision history:
  15. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  16. ; 6  july 85: split off ANSI stuff into other files, added backspace
  17. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  18. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  19. ; 9  aug 85:  added cursor position reporting
  20. ; 10 aug 85:  added output character translation
  21. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  22. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  23. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  24. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  25. ;             direct cursor positioning, takeover of BIOS write_tty,
  26. ;             noticed & squashed 2 related bugs in tab expansion
  27. ; 13 feb 86:  Squashed them again, harder
  28. ;------------------------------------------------------------------------
  29.  
  30.         include nansi_d.asm     ; definitions
  31.  
  32.         ; from nansi_f.asm
  33.         extrn   f_escape:near, f_in_escape:near
  34.  
  35.         ; from nansi_p.asm
  36.         extrn   param_end:word, redef_end:word
  37.  
  38.         ; from nansi_i.asm
  39.         extrn   dosfn0:near
  40.  
  41.         ; to nansi_p.asm
  42.         public  f_loopdone
  43.         public  f_not_ansi
  44.         public  f_ansi_exit
  45.  
  46.         ; to both nansi_p.asm and nansi_f.asm
  47.         public  cur_x, cur_y, max_x, cur_attrib
  48.  
  49.         ; to nansi_f.asm
  50.         public  xy_to_regs, get_blank_attrib
  51.         public  port_6845
  52.         public  wrap_flag
  53.         public  cur_parm_ptr
  54.         public  cur_coords, saved_coords, max_y
  55.         public  escvector, string_term
  56.         public  cpr_esc, cprseq
  57.         public  video_mode
  58.         public  lookup
  59.         public  in_g_mode
  60.  
  61.         ; to nansi_i.asm
  62.         public  req_ptr, break_handler
  63.         public  int_29
  64.         if      takeBIOS
  65.         public  new_vid_bios, old_vid_bios
  66.         endif
  67.  
  68.         ; to all modules
  69.         public  xlate_tab_ptr
  70.  
  71. ;--- seg_cs is the CS: override prefix
  72. ; (assembler forgets cs: on second "xlat dummy_cs_byte")
  73. seg_cs  macro
  74.         db      2eh
  75.         endm
  76.  
  77. ;--- push_all, pop_all ------------------------------------------------
  78. ; Save/restore all user registers.
  79. push_all        macro
  80.         if      is_8088
  81.         push    ax
  82.         push    bx
  83.         push    cx
  84.         push    dx
  85.         push    bp
  86.         push    si
  87.         push    di
  88.         else
  89.         pusha
  90.         endif
  91.         endm
  92.  
  93. pop_all macro
  94.         if      is_8088
  95.         pop     di
  96.         pop     si
  97.         pop     bp
  98.         pop     dx
  99.         pop     cx
  100.         pop     bx
  101.         pop     ax
  102.         else
  103.         popa
  104.         endif
  105.         endm
  106.  
  107. keybuf  struc                           ; Used in getchar
  108. len     dw      ?
  109. adr     dw      ?
  110. keybuf  ends
  111.  
  112.  
  113. ABS40   segment at 40h
  114.         org     1ah
  115. buffer_head     dw      ?       ; Used in 'flush input buffer' dos call.
  116. buffer_tail     dw      ?
  117.  
  118.         org     49h
  119. crt_mode        db      ?
  120. crt_cols        dw      ?
  121. crt_len         dw      ?
  122. crt_start       dw      ?
  123. cursor_posn     dw      8 dup (?)
  124. cursor_mode     dw      ?
  125. active_page     db      ?
  126. addr_6845       dw      ?
  127. crt_mode_set    db      ?       ; = 7 only if monochrome display adaptor
  128. crt_palette     db      ?
  129.         org     6ch
  130. timer_low       dw      ?       ; low word of time-of-day counter (18.2 hz)
  131.         org 84h
  132. crt_max_y       db ?            ; CJD 11/19/89 - EGA/VGA max row #
  133. ABS40   ends
  134.  
  135.         page
  136.  
  137. CODE    segment byte public 'CODE'
  138. assume  cs:code, ds:code
  139.  
  140.         ; Device Driver Header
  141.  
  142.         org     0
  143.  
  144.         dd      -1                      ; next device
  145.         dw      8013h                   ; attributes
  146.         dw      strategy                ; request header pointer entry
  147.         dw      interrupt               ; request entry point
  148.         db      'CON     '              ; device name (8 char)
  149.  
  150.         ; Identification- in case somebody TYPEs the assembled driver
  151.         db      27, '[2J'
  152.         db      "Nansi.sys v2.2"
  153.         ife     is_8088
  154.         db      "(80286)"
  155.         endif
  156.         db      ': New ANSI driver (C) Daniel Kegel, Pasadena, CA 1986'
  157.         db      13, 10, 26
  158.  
  159.  
  160. ;----- variable area --------------------
  161. req_ptr label   dword
  162. req_off dw      ?
  163. req_seg dw      ?
  164.  
  165. wrap_flag       db      1       ; 0 = no wrap past line end
  166. escvector       dw      0       ; state vector of ESCape sequencor
  167. video_mode      db      3       ; ROM BIOS video mode (2=BW, 3=color)
  168. max_y           db      24
  169. max_cur_x       label   word    ; used to get both max & cur at once
  170. max_x           db      79      ; line width (79 for 80x25 modes)
  171. cur_coords      label   word
  172. cur_x           db      0       ; cursor position (0 = left edge)
  173. cur_y           db      0       ;                 (0 = top edge)
  174. saved_coords    dw      ?       ; holds XY after a SCP escape sequence
  175. string_term     db      0       ; either escape or double quote
  176. cur_attrib      db      7       ; current char attributes
  177. cur_page        db      0       ; current display page
  178. video_seg       dw      ?       ; segment of video card
  179. f_cptr_seg      dw      ?       ; part of fastout write buffer pointer
  180. cur_parm_ptr    dw      ?       ; last byte of parm area now used
  181. port_6845       dw      ?       ; port address of 6845 card
  182. xlate_tab_ptr   dw      ?       ; pointer to output translation table
  183.                 if      takeBIOS
  184. old_vid_bios    dd      ?       ; pointer to old video bios routine
  185.                 endif
  186.  
  187. brkkeybuf       db      3       ; control C
  188. fnkeybuf        db      ?       ; holds second byte of fn key codes
  189. cpr_buf         db      8 dup (?), '['
  190. cpr_esc         db      1bh     ; descending buffer for cpr function
  191.  
  192. ; following four keybufs hold information about input
  193. ; Storage order determines priority- since the characters making up a function
  194. ; key code must never be separated (say, by a Control-Break), they have the
  195. ; highest priority, and so on.  Keyboard keys (except ctrl-break) have the
  196. ; lowest priority.
  197.  
  198. fnkey   keybuf  <0, fnkeybuf>   ; fn key string (0 followed by scan code)
  199. cprseq  keybuf  <0>             ; CPR string (ESC [ y;x R)
  200. brkkey  keybuf  <0, brkkeybuf>  ; ^C
  201. xlatseq keybuf  <0>             ; keyboard reassignment string
  202.  
  203. ;------ xy_to_regs --------------------------------------------
  204. ; on entry: x in cur_x, y in cur_y
  205. ; on exit:  dx = chars left on line, di = address
  206. ; Alters ax, bx.
  207. xy_to_regs      proc    near
  208.         ; Find number of chars 'till end of line, keep in DX
  209.         mov     ax, max_cur_x
  210.         mov     bx, ax                  ; save max_x & cur_x for next block
  211.         mov     ah, 0                   ; ax = max_x
  212.         xchg    dx, ax
  213.         mov     al, bh
  214.         mov     ah, 0                   ; ax = cur_x
  215.         sub     dx, ax
  216.         inc     dx                      ; dx is # of chars till EOL
  217.         ; Calculate DI = current address in text buffer
  218.         mov     al, bl                  ; al = max_x
  219.         inc     al
  220.         mul     cur_y
  221.         add     al, bh                  ; al += cur_x
  222.         adc     ah, 0                   ; AX is # of chars into buffer
  223.         add     ax, ax
  224.         xchg    di, ax                  ; DI is now offset of cursor.
  225.         ret
  226. xy_to_regs      endp
  227.  
  228.  
  229. ;------- dos_fn_tab -------------
  230. ; This table is used in "interrupt" to call the routine that handles
  231. ; the requested function.
  232.  
  233. max_cmd equ     12
  234. dos_fn_tab:
  235.         dw      dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  236.         dw      dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  237.  
  238. ;------- strategy ----------------------------------------------------
  239. ; DOS calls strategy with a request which is to be executed later.
  240. ; Strategy just saves the request.
  241.  
  242. strategy        proc    far
  243.         mov     cs:req_off,BX
  244.         mov     cs:req_seg,ES
  245.         ret
  246. strategy        endp
  247.  
  248. ;------ interrupt -----------------------------------------------------
  249. ; This is where the request handed us during "strategy" is
  250. ; actually carried out.
  251. ; Calls one of 12 subroutines depending on the function requested.
  252. ; Each subroutine returns with exit status in AX.
  253.  
  254. interrupt       proc    far
  255.         sti
  256.         push_all                        ; preserve caller's registers
  257.         push    ds
  258.         push    es
  259.  
  260.  
  261.         ; Read requested function information into registers
  262.         lds     bx,cs:req_ptr
  263.         mov     al,[BX+02h]             ; al = function code
  264.         les     si,[BX+0Eh]             ; ES:SI = input/output buffer addr
  265.         mov     cx,[BX+12h]             ; cx = input/output byte count
  266.  
  267.         cmp     al, max_cmd
  268.         ja      unk_command             ; too big, exit with error code
  269.  
  270.         xchg    bx, ax
  271.         shl     bx, 1                   ; form index to table of words
  272.         mov     ax, cs
  273.         mov     ds, ax
  274.         call    word ptr dos_fn_tab[bx]
  275. int_done:
  276.         lds     bx,cs:req_ptr           ; report status
  277.         or      ax, 100h                ; (always set done bit upon exit)
  278.         mov     [bx+03],ax
  279.  
  280.         pop     ES                      ; restore caller's registers
  281.         pop     DS
  282.         pop_all
  283.         ret                             ; return to DOS.
  284.  
  285. unk_command:
  286.         call    badcmd
  287.         jmp     int_done
  288.  
  289. interrupt       endp
  290.  
  291. ;----- BIOS break handler -----------------------------------------
  292. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  293. ; Simply notes that a break was hit.  Flag is checked during input calls.
  294.  
  295. break_handler   proc
  296.         mov     cs:brkkey.len, 1
  297.         iret
  298. break_handler   endp
  299.  
  300.         page
  301.  
  302. ;------ badcmd -------------------------------------------------------
  303. ; Invalid function request by DOS.
  304. badcmd  proc    near
  305.         mov     ax, 813h                ; return "Error: invalid cmd"
  306.         ret
  307. badcmd  endp
  308.  
  309.  
  310. ;------ nopcmd -------------------------------------------------------
  311. ; Unimplemented or dummy function request by DOS.
  312. nopcmd  proc    near
  313.         xor     ax, ax                  ; No error, not busy.
  314.         ret
  315. nopcmd  endp
  316.  
  317. ;------- dos function #4 ----------------------------------------
  318. ; Reads CX characters from the keyboard, places them in buffer at
  319. ; ES:SI.
  320. dosfn4  proc    near
  321.         jcxz    dos4done
  322.         mov     di, si
  323. dos4lp: push    cx
  324.         call    getchar
  325.         pop     cx
  326.         stosb
  327.         loop    dos4lp
  328. dos4done:
  329.         xor     ax, ax                  ; No error, not busy.
  330.         ret
  331. dosfn4  endp
  332.  
  333. ;-------- dos function #5: non-destructive input, no wait ------
  334. ; One-character lookahead into the keyboard buffer.
  335. ; If no characters in buffer, return BUSY; otherwise, get value of first
  336. ; character of buffer, stuff into request header, return DONE.
  337. dosfn5  proc    near
  338.         call    peekchar
  339.         jz      dos5_busy
  340.  
  341.         lds     bx,req_ptr
  342.         mov     [bx+0Dh], al
  343.         xor     ax, ax                  ; No error, not busy.
  344.         jmp     short dos5_exit
  345. dos5_busy:
  346.         MOV     ax, 200h                ; No error, busy.
  347. dos5_exit:
  348.         ret
  349.  
  350. dosfn5  endp
  351.  
  352. ;-------- dos function #6: input status --------------------------
  353. ; Returns "busy" if no characters waiting to be read.
  354. dosfn6  proc    near
  355.         call    peekchar
  356.         mov     ax, 200h                ; No error, busy.
  357.         jz      dos6_exit
  358.         xor     ax, ax                  ; No error, not busy.
  359. dos6_exit:
  360.         ret
  361. dosfn6  endp
  362.  
  363. ;-------- dos function #7: flush input buffer --------------------
  364. ; Clears the IBM keyboard input buffer.  Since it is a circular
  365. ; queue, we can do this without knowing the beginning and end
  366. ; of the buffer; all we need to do is set the tail of the queue
  367. ; equal to the head (as if we had read the entire queue contents).
  368. ; Also resets all the device driver's stuffahead buffers.
  369. dosfn7  proc    near
  370.         xor     ax, ax
  371.         mov     fnkey.len, ax           ; Reset the stuffahead buffers.
  372.         mov     cprseq.len, ax
  373.         mov     brkkey.len, ax
  374.         mov     xlatseq.len, ax
  375.  
  376.         mov     ax, abs40
  377.         mov     es, ax
  378.         mov     ax, es:buffer_head      ; clear queue by making the tail
  379.         mov     es:buffer_tail, ax      ; equal to the head
  380.  
  381.         xor     ax, ax                  ; no error, not busy.
  382.         ret
  383. dosfn7  endp
  384.  
  385.         page
  386.         if      takeBIOS
  387. ;--- new_vid_bios -------------------------------------------
  388. ; New_vid_bios simply replaces the write_tty call.
  389. ; All other calls get sent to the old video bios.
  390. ; This gives BIOS ANSI capability.
  391. ; However, it takes away the escape character.
  392. ; If this is not desired, just tell init to not take over the vector.
  393.  
  394. new_vid_bios    proc
  395.         cmp     ah, 14
  396.         jz      nvb_write_tty
  397.         jmp     dword ptr cs:old_vid_bios
  398. nvb_write_tty:
  399.         push    cx
  400.         mov     cl, cs:cur_attrib
  401.         ; If in graphics mode, BL is new color
  402.         call    in_g_mode               ; returns carry set if text mode
  403.         jc      nvb_wt_text
  404.                 mov     cs:cur_attrib, bl       ; ja?
  405. nvb_wt_text:
  406.         int     29h                     ; write AL
  407.         mov     cs:cur_attrib, cl       ; restore color
  408.         pop     cx
  409.         iret
  410.  
  411. new_vid_bios    endp
  412.         endif
  413.  
  414. ;------ int_29 ----------------------------------------------
  415. ; Int 29 handles DOS quick-access putchar.
  416. ; Last device loaded with attribute bit 4 set gets accessed for
  417. ; single-character writes via int 29h instead of via interrupt.
  418. ; Must preserve all registers.
  419. ; Installed as int 29h by dosfn0 (init).
  420. int_29_buf      db      ?
  421.  
  422. int_29  proc    near
  423.         sti
  424.         push    ds
  425.         push    es
  426.         push_all
  427.         mov     cx, 1
  428.         mov     bx, cs
  429.         mov     es, bx
  430.         mov     ds, bx
  431.         mov     si, offset int_29_buf
  432.         mov     byte ptr [si], al
  433.         call    dosfn8
  434.         pop_all
  435.         pop     es
  436.         pop     ds
  437.         iret
  438. int_29  endp
  439.  
  440.         page
  441. ;------ dosfn8 -------------------------------------------------------
  442. ; Handles writes to the device (with or without verify).
  443. ; Called with
  444. ;  CX    = number of bytes to write
  445. ;  ES:SI = transfer buffer
  446. ;  DS    = CS, so we can access local variables.
  447.  
  448. dosfn8  proc    near
  449.  
  450.         mov     f_cptr_seg, es  ; save segment of char ptr
  451.  
  452.         ; Read the BIOS buffer address/cursor position variables.
  453.         mov     ax, abs40
  454.         mov     ds, ax
  455.         assume  ds:abs40
  456.  
  457.         ; Find current video mode and screen size.
  458.         mov     ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  459.         mov     cs:video_mode, al
  460.         dec     ah                      ; ah = max column
  461.         mov     cs:max_x, ah
  462.         mov     al,crt_max_y            ; CJD 11/19/89 - get/set max row #
  463.         mov     cs:max_y,al
  464.  
  465.         ; Find current cursor coordinates.
  466.         mov     al,active_page
  467.         cbw
  468.         add     ax,ax
  469.         xchg    bx,ax
  470.         mov     ax,cursor_posn[bx]
  471.         mov     cs:cur_coords,AX
  472.  
  473.         ; Find video buffer segment address; adjust it
  474.         ; so the offset is zero; return in AX.
  475.  
  476.         ; DS is abs40.
  477.         ; Find 6845 address.
  478.         mov     ax, addr_6845
  479.         mov     cs:port_6845, ax
  480.         ; Find video buffer address.
  481.         MOV     AX,crt_start
  482.         shr     ax, 1
  483.         shr     ax, 1
  484.         shr     ax, 1
  485.         shr     ax, 1
  486.         add     ah, 0B0h                ; assume it's a monochrome card...
  487.         CMP     cs:video_mode,07
  488.         jz      d8_gots
  489.         add     ah, 8                   ; but if not mode 7, it's color.
  490. d8_gots:
  491.         push    cs
  492.         pop     ds
  493.         assume  ds:code
  494.         mov     video_seg, ax
  495.         mov     es, ax
  496.         call    xy_to_regs              ; Set DX, DI according to cur_coords.
  497.  
  498.         ; | If in graphics mode, clear old pseudocursor
  499.         call    in_g_mode
  500.         jc      d8_no_cp
  501.                 call    pseudocursor    ; write block in xor
  502. d8_no_cp:
  503.         mov     bx, xlate_tab_ptr       ; get pointer to translation table
  504.  
  505.         mov     ah, cur_attrib
  506.         mov     ds, f_cptr_seg          ; get segment of char ptr
  507.         assume  ds:nothing
  508.         cld                             ; make sure we'll increment
  509.  
  510.         ; The Inner Loop: 4+12 +4+4 +4+4 +0+15 +4+4 +4+18 = 77 cycles/loop
  511.         ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  512.         ; At that speed, it takes 32 milliseconds to fill a screen.
  513.  
  514.         ; Get a character, put it on the screen, repeat 'til end of line
  515.         ; or no more characters.
  516.         jcxz    f_loopdone              ; if count = 0, we're already done.
  517.         cmp     cs:escvector, 0         ; If in middle of an escape sequence,
  518.         jnz     f_in_escapex            ; jump to escape sequence handler.
  519.  
  520. f_tloop:; | If in graphics mode, jump to alternate loop
  521.         ; | What a massive kludge!  A better approach would have been
  522.         ; | to collect characters for a "write n chars" routine
  523.         ; | which would handle both text and graphics modes.
  524.         call    in_g_mode
  525.         jc      f_t_cloop
  526.         jmp     f_g_cloop
  527.  
  528. f_t_cloop:
  529.         LODSB                           ; get char! (al = ds:[si++])
  530.         cmp     al, 28                  ; is it a control char?
  531.         jb      f_control               ;  maybe...
  532. f_t_nctl:
  533.         seg_cs
  534.         xlat
  535.         STOSW                           ; Put Char! (es:[di++] = ax)
  536.         dec     dx                      ; count down to end of line
  537.         loopnz  f_t_cloop               ; and go back for more.
  538.         jz      f_t_at_eol              ; at end of line; maybe do a crlf.
  539.         jmp     short f_loopdone
  540.  
  541. f_looploop:
  542. f_ansi_exit:                            ; in case we switched into
  543.         loopnz  f_tloop                 ; a graphics mode
  544. f_t_at_eol:
  545.         jz      f_at_eol
  546.  
  547. f_loopdone:
  548.  
  549.         ;--------- All done with write request -----------
  550.         ; DI is cursor address; cursor position in cur_y, dl.
  551.         mov     ax, cs
  552.         mov     ds, ax                  ; get our segment back
  553.         assume  ds:code
  554.  
  555.         ; Restore cur_x = max_x - dx + 1.
  556.         mov     al, max_x
  557.         inc     al
  558.         sub     al, dl
  559.         mov     cur_x, al
  560.         ; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
  561.         call    set_pseudocursor
  562.         ; Return to DOS.
  563.         xor     ax, ax                  ; No error, not busy.
  564.         ret
  565.  
  566.         ;---- handle control characters ----
  567.         ; Note: cur_x is not kept updated in memory, but can be
  568.         ; computed from max_x and dx.
  569.         ; Cur_y is kept updated in memory.
  570. f_control:
  571.         cmp     al, 27                  ; Is it an escape?
  572.         jz      f_escapex
  573.         cmp     al, 13                  ; carriage return?
  574.         jz      f_cr
  575.         cmp     al, 10                  ; line feed?
  576.         jz      f_lf
  577.         cmp     al, 9                   ; tab?
  578.         jz      f_tabx
  579.         cmp     al, 8                   ; backspace?
  580.         jz      f_bs
  581.         cmp     al, 7                   ; bell?
  582.         jz      f_bell
  583.         jmp     f_nctl                  ; then it is not a control char.
  584.  
  585. f_tabx: jmp     f_tab
  586. f_escapex:
  587.         jmp     f_escape
  588. f_in_escapex:
  589.         jmp     f_in_escape
  590.  
  591. f_bs:   ;----- Handle backspace -----------------
  592.         ; Moves cursor back one space without erasing.  No wraparound.
  593.         cmp     dl, cs:max_x            ; wrap around to previous line?
  594.         ja      fbs_wrap                ; yep; disallow it.
  595.         dec     di                      ; back up one char & attrib,
  596.         dec     di
  597.         inc     dx                      ; and note one more char left on line.
  598. fbs_wrap:
  599.         jmp     f_looploop
  600.  
  601. f_bell: ;----- Handle bell ----------------------
  602.         ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  603.  
  604.         mov     ax, 0e07h               ; "write bell to tty simulator"
  605.         mov     bx, 0                   ; "page zero, color black"
  606.         int     10h                     ; call BIOS
  607.         mov     ah, cs:cur_attrib       ; restore current attribute
  608.         mov     bx, cs:xlate_tab_ptr    ; restore translate table address
  609.         or      al, al                  ; al still 7; this clears Z.
  610.         jmp     f_looploop              ; Let main loop decrement cx.
  611.  
  612. f_cr:   ;----- Handle carriage return -----------
  613.         ; di -= cur_x<<1;               set di= address of start of line
  614.         ; dx=max_x+1;                   set bx= chars left in line
  615.         mov     al, cs:max_x
  616.         inc     al
  617.         sub     al, dl                  ; Get cur_x into ax.
  618.         mov     ah, 0
  619.         sub     di, ax
  620.         sub     di, ax
  621.         mov     dl, cs:max_x            ; Full line ahead of us.
  622.         inc     dx
  623.         mov     ah, cs:cur_attrib       ; restore current attribute
  624.         or      al, 1                   ; clear z
  625.         jmp     f_looploop              ; and let main loop decrement cx
  626.  
  627. f_at_eol:
  628.         ;----- Handle overrunning right end of screen -------
  629.         ; cx++;                         compensate for double loop
  630.         ; if (!wrap_flag) { dx++; di-=2; }
  631.         ; else do_crlf;
  632.         inc     cx
  633.         test    cs:wrap_flag, 1
  634.         jnz     feol_wrap
  635.                 dec     di
  636.                 dec     di
  637.                 inc     dx
  638.                 jmp     f_looploop
  639. feol_wrap:
  640.         ; dx=max_x+1;                   set bx= chars left in line
  641.         ; di -= 2*(max_x+1);
  642.         ; do_lf
  643.         mov     dl, cs:max_x
  644.         inc     dx
  645.         sub     di, dx
  646.         sub     di, dx
  647.         ; fall thru to line feed routine
  648.  
  649. f_lf:   ;----- Handle line feed -----------------
  650.         ; if (cur_y >= max_y) scroll;           scroll screen up if needed
  651.         ; else { cur_y++; di += max_x<<1;       else increment Y
  652.  
  653.         mov     al, cs:max_y
  654.         cmp     cs:cur_y, al
  655.         jb      flf_noscroll
  656.                 call    scroll_up               ; preserves bx,cx,dx,si,di
  657.                 jmp     short flf_done
  658. flf_noscroll:
  659.         inc     cs:cur_y
  660.         mov     al, cs:max_x
  661.         mov     ah, 0
  662.         inc     ax
  663.         add     ax, ax
  664.         add     di, ax
  665. flf_done:
  666.         mov     ah, cs:cur_attrib               ; restore current attribute
  667.         or      al, 1                   ; clear z
  668.         jmp     f_looploop              ; and let main loop decrement cx
  669.  
  670. f_tab:  ;----- Handle tab expansion -------------
  671.         ; Get cur_x into al.
  672.         mov     al, cs:max_x
  673.         inc     al
  674.         sub     al, dl
  675.         ; Calculate number of spaces to output.
  676.         push    cx                      ; save cx
  677.         mov     ch, 0
  678.         mov     cl, al                  ; get zero based x coordinate
  679.         and     cl, 7
  680.         neg     cl
  681.         add     cl, 8                   ; 0 -> 8, 1 -> 8, ... 7 -> 1
  682.         sub     dx, cx                  ; update chars-to-eol, maybe set z
  683.         pushf                           ; || save Z for main loop
  684.         ; ah is still current attribute.  Move CX spaces to the screen.
  685.         mov     al, ' '
  686.         call    in_g_mode               ; | graphics mode
  687.         jnc     f_tab_putc              ; |
  688.         REP     STOSW
  689.         popf                            ; || restore Z flag for main loop test
  690.         pop     cx                      ; restore cx
  691.         jmp     f_looploop              ; Let main loop decrement cx.
  692.  
  693. ;--------------- graphics mode support -----------------------
  694.  
  695. f_tab_putc:     ; graphics mode- call putc to put the char
  696.         add     dx, cx                  ; move back to start of tab
  697. f_tp_lp:
  698.         call    putchar
  699.         dec     dx                      ; go to next cursor position
  700.         loop    f_tp_lp
  701.         popf                            ; Z set if wrapped around EOL
  702.         pop     cx
  703.         jmp     f_looploop
  704.  
  705. ;---- in_g_mode -------------
  706. ; Returns Carry set if not in a graphics mode.
  707. ; Preserves all registers.
  708.  
  709. in_g_mode       proc    near
  710.         cmp     cs:video_mode, 4
  711.         jb      igm_stc
  712.         cmp     cs:video_mode, 7
  713.         jz      igm_stc
  714.         clc
  715.         ret
  716. igm_stc:
  717.         stc
  718.         ret
  719. in_g_mode       endp
  720.  
  721. ;---- Where to go when a character turns out not to be special
  722. f_nctl:
  723. f_not_ansi:
  724.         call    in_g_mode
  725.         jnc     f_g_nctl                ; graphics mode
  726. f_jmptnctl:
  727.         jmp     f_t_nctl                ; text mode
  728.  
  729. ;---- Alternate main loop for graphics mode ----
  730. f_g_cloop:
  731.         LODSB                           ; get char! (al = ds:[si++])
  732.         cmp     al, 28                  ; is it a control char?
  733.         jb      f_g_control             ;  maybe...
  734. f_g_nctl:
  735.         seg_cs
  736.         xlat
  737.         call    putchar
  738.         dec     dx                      ; count down to end of line
  739.         loopnz  f_g_cloop               ; and go back for more.
  740.         jz      f_g_at_eol              ; at end of line; maybe do a crlf.
  741.         jmp     f_loopdone
  742.  
  743. f_g_control:    jmp     f_control
  744. f_g_at_eol:     jmp     f_at_eol
  745.  
  746. ;---- putchar ------------------------------------------------
  747. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  748. ; On entry, registers set up as per xy_to_regs.
  749. ; Preserves all registers.
  750. putchar proc    near
  751.         push    dx
  752.         push    cx
  753.         push    bx
  754.         push    ax
  755.         ; 1. Set cursor position.
  756.         mov     al, cs:max_x
  757.         inc     al
  758.         sub     al, dl
  759.         mov     cs:cur_x, al
  760.         mov     dx, cs:cur_coords       ; get X & Y into DX
  761.         xor     bx, bx                  ; choose dpy page 0
  762.         mov     ah, 2                   ; chose "Set Cursor Position"
  763.         int     10h                     ; call ROM BIOS
  764.         ; 2. Write char & attribute.
  765.         mov     cx, 1
  766.         pop     ax                      ; get char in AL
  767.         push    ax
  768.         mov     bl, ah                  ; attribute in BL
  769.         mov     bh, 0
  770.         mov     ah, 9
  771.         int     10h
  772.         pop     ax
  773.         pop     bx
  774.         pop     cx
  775.         pop     dx
  776.         ret
  777. putchar endp
  778.  
  779. ;---- set_pseudocursor ------------
  780. ; If in graphics mode, set pseudocursor, else set real cursor.
  781. ; Destroys DS!!!!
  782.  
  783. set_pseudocursor        proc    near
  784.         call    in_g_mode
  785.         jnc     pseudocursor
  786.  
  787. if BIOS_cur
  788.  
  789. ; old (more portable, but slower) version
  790.         mov     dx, cur_coords          ; get X & Y into DX
  791.         xor     bx, bx                  ; choose dpy page 0
  792.         mov     ah, 2                   ; chose "Set Cursor Position"
  793.         int     10h                     ; call ROM BIOS
  794.  
  795. else
  796.  
  797.         ; Write directly to 6845 cursor address register.
  798.         mov     bx, di
  799.         shr     bx, 1                   ; convert word index to byte index
  800.  
  801.         mov     dx, port_6845
  802.         mov     al, 0eh
  803.         out     dx, al
  804.  
  805.         jmp     $+2
  806.         inc     dx
  807.         mov     al, bh
  808.         out     dx, al
  809.  
  810.         jmp     $+2
  811.         dec     dx
  812.         mov     al, 0fh
  813.         out     dx, al
  814.  
  815.         jmp     $+2
  816.         inc     dx
  817.         mov     al, bl
  818.         out     dx, al
  819.  
  820.         ; Set cursor position in low memory.
  821.         assume  ds:abs40
  822.         mov     ax, abs40
  823.         mov     ds, ax
  824. ; Does anybody ever use anything but page zero?
  825. ;       mov     al,active_page
  826. ;       cbw
  827. ;       add     ax,ax
  828. ;       xchg    bx,ax
  829.         mov     ax, cs:cur_coords
  830.         mov     cursor_posn,ax
  831. endif
  832.  
  833.         ret
  834.  
  835.         assume  ds:code
  836. set_pseudocursor        endp
  837.  
  838.  
  839. ;---- pseudocursor --------------------------------------------------
  840. ; Writes a color 15 block in XOR at the current cursor location.
  841. ; Preserves DS, ES, BX, CX, DX, SI, DI.
  842. ; Should be disableable- the pseudocursor slows down single-char
  843. ; writes by a factor of three.
  844. pseudocursor    proc    near
  845.         mov     ax, 8f16h       ; xor, color 15, ^V (small block)
  846.         call    putchar
  847.         ret
  848. pseudocursor    endp
  849.  
  850. ;--------------- end of graphics mode support --------------------
  851.  
  852. dosfn8  endp
  853.  
  854. ;--- get_blank_attrib ------------------------------------------------
  855. ; Determine new attribute and character for a new blank region.
  856. ; Use current attribute, just disallow blink and underline.
  857. ; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
  858. ; Returns result in AH, preserves all other registers.
  859. get_blank_attrib        proc    near
  860.         mov     ah, 0
  861.         call    in_g_mode
  862.         jnc     gb_aok                  ; if graphics mode, 0 is bkgnd
  863.  
  864.         mov     ah, cs:cur_attrib
  865.         and     ah, 7fh                 ; disallow blink
  866.         cmp     cs:video_mode, 7        ; monochrome?
  867.         jnz     gb_aok
  868.                 cmp     ah, 1           ; underline?
  869.                 jnz     gb_aok
  870.                 mov     ah, 7           ; yep- set it to normal.
  871. gb_aok: ret
  872. get_blank_attrib        endp
  873.  
  874.  
  875. ;---- scroll_up ---------------------------------------------------
  876. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  877. ; Moves screen up 1 line, fills the last line with blanks.
  878. ; Attribute of blanks is the current attribute sans blink and underline.
  879.  
  880. scroll_up       proc    near
  881.         push    ax
  882.         push    bx
  883.         push    cx
  884.         push    dx
  885.  
  886.         call    get_blank_attrib
  887.         mov     bh, ah                  ; color to use on new blank areas
  888.         mov     al, 1                   ; AL is number of lines to scroll.
  889.         mov     ah, 6                   ; BIOS: scroll up
  890.         mov     cl, 0                   ; upper-left-x of data to scroll
  891.         mov     ch, 0                   ; upper-left-y of data to scroll
  892.         mov     dl, cs:max_x            ; lower-rite-x
  893.         mov     dh, cs:max_y            ; lower-rite-y (zero based)
  894.         int     10h                     ; call BIOS to scroll a rectangle.
  895.  
  896.         pop     dx
  897.         pop     cx
  898.         pop     bx
  899.         pop     ax
  900.         ret
  901. scroll_up       endp
  902.  
  903. ;---- lookup -----------------------------------------------
  904. ; Called by getchar, peekchar, and key to see if a given key has
  905. ; been redefined.
  906. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  907. ; Returns with Z cleared if no redefinition; otherwise,
  908. ; Z is set, SI points to redefinition string, CX is its length.
  909. ; Preseves AL, all but CX and SI.
  910. ; Redefinition table organization:
  911. ;  Strings are stored in reversed order, first char last.
  912. ;  The word following the string is the character to be replaced;
  913. ;  the next word is the length of the string sans header.
  914. ; param_end points to the last byte used by the parameter buffer;
  915. ; redef_end points to the last word used by the redef table.
  916.  
  917. lookup  proc    near
  918.         mov     si, redef_end           ; Start at end of table, move down.
  919.         or      al, al
  920.         jz      lu_lp
  921.         mov     ah, 0                   ; clear extraneous scan code
  922. lu_lp:  cmp     si, param_end
  923.         jbe     lu_notfound             ; If below redef table, exit.
  924.         mov     cx, [si]
  925.         cmp     ax, [si-2]              ; are you my mommy?
  926.         jz      lu_gotit
  927.         sub     si, 4
  928.         sub     si, cx                  ; point to next header
  929.         jmp     lu_lp
  930. lu_notfound:
  931.         or      si, si                  ; clear Z
  932.         jmp     short lu_exit
  933. lu_gotit:
  934.         sub     si, 2
  935.         sub     si, cx                  ; point to lowest char in memory
  936.         cmp     al, al                  ; set Z
  937. lu_exit:
  938.         ret
  939. lookup  endp
  940.  
  941. ;---- searchbuf --------------------------------------------
  942. ; Called by getchar and peekchar to see if any characters are
  943. ; waiting to be gotten from sources other than BIOS.
  944. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  945. searchbuf       proc    near
  946.         ; Search the stuffahead buffers.
  947.         mov     cx, 4                   ; number of buffers to check for chars
  948.         mov     bx, offset fnkey - 4
  949. sbloop: add     bx, 4                   ; point to next buffer record
  950.         mov     si, [bx].len
  951.         or      si, si                  ; empty?
  952.         loopz   sbloop                  ; if so, loop.
  953.         ret
  954. searchbuf       endp
  955.  
  956. ;---- getchar -----------------------------------------------
  957. ; Returns AL = next char.
  958. ; Trashes AX, BX, CX, BP, SI.
  959. getchar proc    near
  960. gc_searchbuf:
  961.         ; See if any chars are waiting in stuffahead buffers.
  962.         call    searchbuf
  963.         jz      gc_trykbd               ; No chars?  Try the keyboard.
  964.         ; A nonempty buffer was found.
  965.         dec     [bx].len
  966.         dec     si
  967.         mov     bp, [bx].adr            ; get pointer to string
  968.         mov     al, byte ptr ds:[bp][si]; get the char
  969.         ; Recognize function key sequences, move them to highest priority
  970.         ; queue.
  971.         sub     si, 1                   ; set carry if si=0
  972.         jc      gc_nofnkey              ; no chars left -> nothing to protect.
  973.         cmp     bx, offset fnkey
  974.         jz      gc_nofnkey              ; already highest priority -> done.
  975.         or      al, al
  976.         jnz     gc_nofnkey              ; nonzero first byte -> not fnkey.
  977.         ; Found a function key; move it to highest priority queue.
  978.         dec     [bx].len
  979.         mov     ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  980. gc_fnkey:
  981.         mov     fnkey.len, 1
  982.         mov     fnkeybuf, ah            ; save it.
  983. gc_nofnkey:
  984.         ; Valid char in AL.  Return with it.
  985.         jmp     short gcdone
  986.  
  987. gc_trykbd:
  988.         ; Actually get a character from the keyboard.
  989.         mov     ah, 0
  990.         int     16h                     ; BIOS returns with char in AX
  991.         ; If it's Ctrl-break, it has already been taken care of.
  992.         or      ax, ax
  993.         jz      gc_trykbd
  994.  
  995.         ; Look in the reassignment table to see if it needs translation.
  996.         call    lookup                  ; Z=found; CX=length; SI=ptr
  997.         jnz     gc_noredef
  998.         ; Okay; set up the reassignment, and run thru the translation code.
  999.         mov     xlatseq.len, cx
  1000.         mov     xlatseq.adr, si
  1001.         jmp     gc_searchbuf
  1002. gc_noredef:
  1003.         ; Is it a function key?
  1004.         cmp     al, 0
  1005.         jz      gc_fnkey                ; yep- special treatment.
  1006. gcdone: ret     ; with character in AL.
  1007.  
  1008. getchar endp
  1009.  
  1010. ;---- peekchar -----------------------------------------------
  1011. ; Returns Z if no character ready, AL=char otherwise.
  1012. ; Trashes AX, BX, CX, BP, SI.
  1013. peekchar        proc    near
  1014. pc_searchbuf:
  1015.         call    searchbuf
  1016.         jz      pc_trykbd               ; No chars?  Try the keyboard.
  1017.         ; A nonempty buffer was found.
  1018.         dec     si
  1019.         mov     bp, [bx].adr            ; get pointer to string
  1020.         mov     al, byte ptr ds:[bp][si]; get the char
  1021.         ; Valid char from buffer in AL.  Return with it.
  1022.         jmp     short pcdone
  1023. pc_trykbd:
  1024.         ; Actually peek at the keyboard.
  1025.         mov     ah, 1
  1026.         int     16h                     ; BIOS returns with char in AX
  1027.         jz      pcexit
  1028.         ; If it's control-break, it's already been taken care of.
  1029.         or      ax, ax
  1030.         jnz     pc_notbrk
  1031.         mov     ah, 0
  1032.         int     16h                     ; so get rid of it!
  1033.         jmp     short pc_trykbd
  1034. pc_notbrk:
  1035.         ; Look in the reassignment table to see if it needs translation.
  1036.         call    lookup                  ; Z=found; CX=length; SI=ptr
  1037.         jnz     pcdone                  ; Nope; just return the char.
  1038.         ; Okay; get the first code to be returned.
  1039.         add     si, cx
  1040.         mov     al, [si-1]
  1041. pcdone: or      ah, 1                   ; NZ; char ready!
  1042. pcexit: ret     ; with character in AL, Z true if no char waiting.
  1043. peekchar        endp
  1044.  
  1045. CODE    ends
  1046.  
  1047.         end                             ; of nansi.asm
  1048.