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