home *** CD-ROM | disk | FTP | other *** search
/ PDA Software Library / pdasoftwarelib.iso / HP95_100 / UTILITY / REMKEY / REMKEY.ASM next >
Encoding:
Assembly Source File  |  1994-01-10  |  58.5 KB  |  2,571 lines

  1. ; RemKey 1.00
  2. ; Assemble with ML.EXE (from MASM 6.0), not MASM.EXE
  3.     .MODEL tiny
  4.  
  5. ; Some of the logic in this program was lifted from KEY100.ASM by
  6. ; Andy Gryc and PUSHKEYS.COM by Raan Young and Dave Suvak, all of
  7. ; HP Corvallis.
  8. ;
  9. ; Many thanks to the beta-test team for testing and
  10. ; suggesting improvements:
  11. ;   Siroos Afshar
  12. ;   Conrad D. Cox
  13. ;   Ron Crain
  14. ;   James Dean
  15. ;   Stanley Dobrowski
  16. ;   Bruce Holmen
  17. ;   Ed Keefe
  18. ;   Gilles Kohl
  19. ;   David J. Marsh
  20. ;   Thomas Rundel
  21. ;   Mark Scardina
  22. ;   David N. Smith
  23. ;   Jorge M. Trevino
  24. ;   Steve Zweibel
  25.  
  26. debug equ 0        ; non-zero enables debug output
  27.  
  28. ; Leave this off. The send-file code didn't work out well.
  29. ; About 3 out of 500 characters were dropped.
  30. fileOpt equ 0
  31.  
  32. localStack equ 1    ; non-zero enables use of local stack
  33.  
  34. fixedKeys equ 1
  35.  
  36. bcdVersion equ 0100h
  37.  
  38. HotKey equ 7f00h    ; Alt 8
  39.  
  40. ; Timer chip equates
  41. timer0ModeCmd equ 36h
  42. timer2ModeCmd equ 0B6h
  43. timer2LatchCmd equ 80h
  44. timer2ReadBackStatusCmd equ 0E8h
  45.  
  46. timerModeReg equ 43h
  47. timer0CountReg equ 40h
  48. timer2CountReg equ 42h
  49.  
  50. timerOutputFlag equ 80h
  51.  
  52. portB equ 61h
  53.  
  54. ; bits defined for reading and writing of port B
  55. timer2gate equ 1
  56. speaker2gate equ 2
  57. gate2AndSpeaker2 equ timer2gate or speaker2gate
  58.  
  59. VHiToneFreq equ 128
  60. hiToneFreq equ  64
  61. midToneFreq equ 32
  62. loToneFreq equ 16
  63.  
  64. VHiToneDivisor equ (115200 / VHiToneFreq)
  65. hiToneDivisor equ (115200 / hiToneFreq)
  66. midToneDivisor equ (115200 / midToneFreq)
  67. loToneDivisor equ (115200 / loToneFreq)
  68.  
  69. VHiToneClicks equ VHiToneFreq / 2        ; 1/2 second
  70. hiToneClicks equ hiToneFreq / 2        ; 1/2 second
  71. midToneClicks equ midToneFreq / 2    ; 1/2 second
  72. loToneClicks equ loToneFreq / 2        ; 1/2 second
  73.  
  74. MenuKeyCode equ 0c800h
  75.  
  76. ; These keys are sent with a high byte of 0f5h and the int 9 scan code in
  77. ; the low byte. 80h is added for a released key.
  78. RShiftScan equ 36h    ; int 9 scan code of the right shift key
  79. LShiftScan equ 2ah    ; int 9 scan code of the left shift key
  80. LCtrlScan  equ 1dh    ; int 9 scan code of the left Ctrl key
  81. LAltScan   equ 38h    ; int 9 scan code of the left Alt key
  82.  
  83. ; These are handled as special cases and can be given any code but
  84. ; the above codes or the above plus 80h:
  85.  
  86. LockCode    equ 0f400h    ; code sent over serial line when Caps Lock goes on
  87. UnlockCode  equ 0f401h    ; code sent over serial line when Caps Lock goes off
  88.  
  89. CapsScan   equ 3ah    ; int 9 scan code of the Caps Lock key
  90.  
  91. baudRate equ 1200
  92. baudRateDivisor equ (115200 / baudRate)
  93.  
  94. ; Factor by which we increase the SysTick rate over the
  95. ; standard 18.2 ticks/sec
  96. TickFactor equ baudRate/182+1
  97.  
  98. sioConfig equ 3        ; 8 data, 1 stop, no parity
  99.  
  100. ; Base I/O port of 4 serial ports
  101. com1    equ 3f8h
  102. com2    equ 2f8h
  103. com3    equ 3e8h
  104. com4    equ 2e8h
  105.  
  106. ; Offsets from base of various UART registers
  107. rx    equ 0
  108. tx    equ 0
  109. int_en    equ 1
  110. int_id    equ 2
  111. lcont    equ 3
  112. mcont    equ 4
  113. lstat    equ 5
  114. mstat    equ 6
  115. dlab_l    equ 0
  116. dlab_h    equ 1
  117.  
  118. pcRecvDataAvailable    equ 1
  119. pcOverrunError        equ 2
  120. pcParityError        equ 4
  121. pcFramingError        equ 8
  122. pcBreakInterrupt    equ 10h
  123. pcXmitBufferEmpty    equ 20h
  124. pcXmitShiftRegEmpty    equ 40h
  125. pcXmitAllEmpty        equ (pcXmitBufferEmpty OR pcXmitShiftRegEmpty)
  126.  
  127. video_int    equ    10h        ;int for video output calls to bios
  128. tty_out        equ    14        ; put-char function # for video_int
  129.  
  130. MpxInt    equ    2fh            ; the multiplex interupt
  131. RemKeyMpxFn equ 93h            ; Multiplex func code used by RemKey
  132.  
  133. ; The BIOS variables at segment 40h
  134. biosdata    segment at 40h        ; rom bios data area
  135.         org    17h
  136. ShiftState    dw    ?
  137.         org    1ah
  138. bufferHead    dw    ?        ; Head of keyboard buffer
  139. bufferTail    dw    ?        ; Tail of keyboard buffer
  140.  
  141.         org    49h        ; start of video data
  142. crt_mode    db    ?
  143. crt_cols    dw    ?
  144. crt_len        dw    ?
  145. crt_start    dw    ?
  146. cursor_posn    dw    8 dup(?)
  147. cursor_mode    dw    ?
  148. active_page    db    ?
  149. addr_6845    dw    ?
  150. crt_mode_set    db    ?
  151. crt_palette    db    ?
  152.  
  153.         org    6bh        ; Phoenix-specific
  154. LastInterrupt    db    ?        ; bit-map, last interrupt
  155.  
  156.               org    80h
  157. ; These words contain offsets from an assumed segment of 40h
  158. bufferStart    dw    ?        ; Start of keyboard buffer
  159. bufferEnd    dw    ?        ; End+1 of keyboard buffer
  160.  
  161.         org    0a1h
  162. sleepCountdown    dw    ?        ; a countdown to zero triggers sleep
  163. sleepTimeout    dw    ?        ; used to reload sleepCountdown
  164.  
  165.         org    0f1h        ; 100LX-specific keyboard data
  166. KbdFlgs        db    ?
  167. FnFlags        dw    ?
  168. SysFlags2    db    ?
  169. Debounce    db    ?
  170. LastIrq2    dw    ?
  171. LastKey        db    ?
  172. RptCnt        db    ?
  173. MiscFlags    db    ?
  174.  
  175. biosdata    ends
  176.  
  177.        .CODE
  178.        ORG 100h
  179. Begin:
  180.        jmp Main
  181.  
  182. if localStack
  183.            db    '<..RemKey 00 ..>'
  184.            db    '<..RemKey 10 ..>'
  185.            db    '<..RemKey 20 ..>'
  186.            db    '<..RemKey 30 ..>'
  187.            db    '<..RemKey 40 ..>'
  188.            db    '<..RemKey 50 ..>'
  189.            db    '<..RemKey 60 ..>'
  190.            db    '<..RemKey 70 ..>'
  191. stack_top:
  192.  
  193. if debug
  194.     db    '<< Stack Top <<<'
  195. endif
  196.  
  197. InCheckUart db 0            ; used to detect recursion
  198.  
  199. ss_save    dw    ?
  200. sp_save    dw    ?
  201. endif
  202.  
  203. version dw bcdVersion
  204.  
  205. EnableDef db 1        ; non-zero to enable send or receive by default
  206. Receive    db 1        ; non-zero for receive mode, zero for send mode
  207.  
  208. UseEnDef db 1        ; non-zero to copy EnableDef to Enabled
  209.  
  210. DoConfig db 0        ; non-zero to trigger reconfig write
  211.  
  212. OldKey label dword    ; Int 16h -- keyboard hook
  213. OldKeyOff dw ?
  214. OldKeySeg dw ?
  215.  
  216. OldTick label dword    ; Int 8 -- timer hardware tick
  217. OldTickOff dw ?
  218. OldTickSeg dw ?
  219.  
  220. OldMpx label dword    ; Int 2f -- multiplex interrupt
  221. OldMpxOff dw ?
  222. OldMpxSeg dw ?
  223.  
  224. Enabled db 0        ; MUST BE 1 to enable send or receive
  225. SendTSR    db 0        ; Sender installs as a TSR
  226. Is100LX db 0        ; non-zero if running on a 100LX
  227.  
  228. UartBase dw com1    ; default is com1
  229.  
  230. TickCount dw TickFactor    ; counts real ticks
  231.  
  232. ; Offset of our service routine for int 16h
  233. NewKey    dw offset NewKeySend ; assume send-mode
  234.  
  235. ResidentMpxNum    db    0c0h    ; multiplex number of resident code (01b3)
  236. OurMpxNum    db    0    ; our multiplex number (01b4)
  237.  
  238. if fileOpt
  239. SendFileName    dw    0    ; offset of path to file name to be sent
  240. FileNameEnd    dw    0    ; saved pointer to end of path
  241. endif
  242.  
  243. ; blinking, black on white (inverted)
  244. ; <<< RemKey Active, Alt-8 to exit >>>
  245. banner    label word
  246.     db ' ',  70h
  247.     db '<', 0f0h
  248.     db '<', 0f0h
  249.     db '<', 0f0h
  250.     db ' ', 0f0h
  251.     db 'R', 0f0h
  252.     db 'e', 0f0h
  253.     db 'm', 0f0h
  254.     db 'k', 0f0h
  255.     db 'e', 0f0h
  256.     db 'y', 0f0h
  257.     db ' ', 0f0h
  258.     db 'A', 0f0h
  259.     db 'c', 0f0h
  260.     db 't', 0f0h
  261.     db 'i', 0f0h
  262.     db 'v', 0f0h
  263.     db 'e', 0f0h
  264.     db ',', 0f0h
  265.     db ' ', 0f0h
  266.     db 'A', 0f0h
  267.     db 'l', 0f0h
  268.     db 't', 0f0h
  269.     db '+', 0f0h
  270.     db '8', 0f0h
  271.     db ' ', 0f0h
  272.     db 't', 0f0h
  273.     db 'o', 0f0h
  274.     db ' ', 0f0h
  275.     db 'e', 0f0h
  276.     db 'x', 0f0h
  277.     db 'i', 0f0h
  278.     db 't', 0f0h
  279.     db ' ', 0f0h
  280.     db '>', 0f0h
  281.     db '>', 0f0h
  282.     db '>', 0f0h
  283.     db ' ',  70h
  284. bannerLength equ ($-banner)/2    ; length in words
  285.  
  286. ; If we receive a byte such that:
  287. ;
  288. ;   LO(pending) XOR ROL(HI(pending)) = RecvdByte XOR 5ah
  289. ;
  290. ; then pending holds a valid ASCII/scan-code pair.
  291. ; In other words the vertical parity of a valid packet
  292. ; is 5ah
  293. pending dw 0
  294. pendingCount db 0
  295.  
  296. ;=======================================================================
  297.  
  298. SpeedUp proc near
  299.     assume ds:nothing,ss:nothing,es:nothing
  300.     mov    al,timer0ModeCmd    ; Prepare to set clock speed
  301.     pushf                ; save current interrupt enable state
  302.     cli
  303.     out    timerModeReg,al
  304.     mov    ax,(65536/TickFactor)
  305.     out    timer0CountReg,al
  306.     mov    al,ah
  307.     out    timer0CountReg,al
  308.     popf                ; restore interrupt enable state
  309.     mov    TickCount,TickFactor
  310.     ret
  311. SpeedUp endp
  312.  
  313. SlowDown proc near
  314.     assume ds:nothing,ss:nothing,es:nothing
  315. ; restore normal systick rate, div = 65536 (0)
  316.     mov    al,timer0ModeCmd    ; Prepare to set clock speed
  317.     pushf                ; save current interrupt enable state
  318.     cli
  319.     out    timerModeReg,al
  320.     mov    al,0
  321.     out    timer0CountReg,al
  322.     out    timer0CountReg,al
  323.     popf                ; restore interrupt enable state
  324.     ret
  325. SlowDown endp
  326.  
  327. StartTone proc near
  328.     assume ds:nothing,ss:nothing,es:nothing
  329.     push    bx
  330.     mov    bx,ax            ; save divide in bx
  331.  
  332.     pushf                ; save current interrupt enable state
  333.     cli                ; disable interrupts
  334.  
  335.     mov    al,timer2ModeCmd
  336.     out    timerModeReg,al
  337.     mov    al,bl
  338.     out    timer2CountReg,al
  339.     mov    al,bh
  340.     out    timer2CountReg,al
  341.  
  342. ; enable speaker
  343.     in    al,portB
  344.     or    al,gate2AndSpeaker2
  345.     out    portB,al
  346.  
  347.     popf                ; restore interrupt enable state
  348.     pop    bx
  349.     ret
  350. StartTone endp
  351.  
  352. EndTone proc near
  353.     assume ds:nothing,ss:nothing,es:nothing
  354.     push    ax
  355.  
  356.     pushf                ; save current interrupt enable state
  357.     cli                ; disable interrupts
  358.  
  359. ; disable speaker
  360.     in    al,portB
  361.     and    al, not gate2AndSpeaker2
  362.     out    portB,al
  363.  
  364.     popf                ; restore interrupt enable state
  365.     pop    ax
  366.     ret
  367. EndTone endp
  368.  
  369. ; delays for "ax" hi-lo transitions on the speaker
  370. ToneDelay proc near
  371.     assume ds:nothing,ss:nothing,es:nothing
  372.     push    ax
  373.     push    bx
  374.     push    cx
  375.  
  376.     mov    bx,ax            ; save count in bx
  377. ToneLoop:
  378. ; wait for hi
  379.     sub    cx,cx            ; reset timeout counter
  380. HiLoop:
  381.     pushf                ; save current interrupt enable state
  382.     cli                ; disable interrupts
  383.     mov    al,timer2ReadBackStatusCmd
  384.     out    timerModeReg,al
  385.     in    al,timer2CountReg
  386.     popf                ; restore interrupt enable state
  387.     and    al,timerOutputFlag
  388.     jnz    LoWait
  389.     loop    HiLoop
  390.     jmp    short @@Exit        ; exit early if we timeout
  391.  
  392. ; wait for lo
  393. LoWait:
  394.     sub    cx,cx            ; reset timeout counter
  395. LoLoop:
  396.     pushf                ; save current interrupt enable state
  397.     cli                ; disable interrupts
  398.     mov    al,timer2ReadBackStatusCmd
  399.     out    timerModeReg,al
  400.     in    al,timer2CountReg
  401.     popf                ; restore interrupt enable state
  402.     and    al,timerOutputFlag
  403.     jz    DecClicks
  404.     loop    LoLoop
  405.     jmp    short @@Exit        ; exit early if we timeout
  406.  
  407. DecClicks:
  408.     dec    bx            ; drop click count
  409.     jnz    ToneLoop
  410. @@Exit:
  411.  
  412.     pop    cx
  413.     pop    bx
  414.     pop    ax
  415.     ret
  416. ToneDelay endp
  417.  
  418. ; ax = divisor, bx = duration in clicks
  419. Tone proc near
  420.     assume ds:nothing,ss:nothing,es:nothing
  421.     call    StartTone
  422.     mov    ax,bx
  423.     call    ToneDelay
  424.     jmp    EndTone
  425. Tone endp
  426.  
  427. VHiTone proc near
  428.     assume ds:nothing,ss:nothing,es:nothing
  429.     mov    ax,VHiToneDivisor
  430.     mov    bx,VHiToneClicks
  431.     jmp    Tone
  432. VHiTone endp
  433.  
  434. HiTone proc near
  435.     assume ds:nothing,ss:nothing,es:nothing
  436.     mov    ax,hiToneDivisor
  437.     mov    bx,hiToneClicks
  438.     jmp    Tone
  439. HiTone endp
  440.  
  441. MidTone proc near
  442.     assume ds:nothing,ss:nothing,es:nothing
  443.     mov    ax,midToneDivisor
  444.     mov    bx,midToneClicks
  445.     jmp    Tone
  446. MidTone endp
  447.  
  448. LoTone proc near
  449.     assume ds:nothing,ss:nothing,es:nothing
  450.     mov    ax,loToneDivisor
  451.     mov    bx,loToneClicks
  452.     jmp    Tone
  453. LoTone endp
  454.  
  455. Tweedle proc near
  456.     assume ds:nothing,ss:nothing,es:nothing
  457.     push    ax
  458.     push    bx
  459.     call    VHiTone
  460.     call    HiTone
  461.     call    VHiTone
  462.     call    HiTone
  463.     pop    bx
  464.     pop    ax
  465.     ret
  466. Tweedle endp
  467.  
  468. Buzz proc near
  469.     assume ds:nothing,ss:nothing,es:nothing
  470.     push    ax
  471.     push    bx
  472.     call    MidTone
  473.     call    LoTone
  474.     call    MidTone
  475.     call    LoTone
  476.     pop    bx
  477.     pop    ax
  478.     ret
  479. Buzz endp
  480.  
  481. if debug
  482. ; Display the character in al via a BIOS call
  483. PutChar    proc near
  484.     assume ds:nothing,ss:nothing,es:nothing
  485.     push    ax
  486.     push    bx
  487.     push    si
  488.     push    di
  489.     push    bp
  490.     mov    ah,tty_out
  491.     mov    bl,0
  492.     int    video_int
  493.     pop    bp
  494.     pop    di
  495.     pop    si
  496.     pop    bx
  497.     pop    ax
  498.     ret
  499. PutChar    endp
  500.  
  501. ; Display the byte in al in hex via a BIOS call
  502. PutByte    proc near
  503.     assume ds:nothing,ss:nothing,es:nothing
  504.     push    ax
  505.     shr    al,1
  506.     shr    al,1
  507.     shr    al,1
  508.     shr    al,1
  509.     call    put_nybble
  510.     pop    ax
  511. put_nybble:
  512.     push    ax
  513.     and    al,0fh
  514.     add    al,'0'
  515.     cmp    al,'9'
  516.     jbe    put_digit
  517.     add    al,'A'-'0'-10
  518. put_digit:
  519.     call    PutChar
  520.     pop    ax
  521.     ret
  522. PutByte        endp
  523.  
  524. PutWord proc near
  525.     assume ds:nothing,ss:nothing,es:nothing
  526.     xchg    ah,al
  527.     call    PutByte
  528.     xchg    ah,al
  529.     jmp    PutByte
  530. PutWord endp
  531.  
  532. endif
  533.  
  534. ; Save int 16h vector in OldKey and set to offset in NewKey.
  535. HookKey    proc near
  536.     assume ds:nothing,ss:nothing,es:nothing
  537.     push    ds
  538.     push    es
  539.  
  540.     mov    ax,3516h  ; Get keyboard interrupt
  541.     int    21h
  542.  
  543.     mov    OldKeyOff,bx
  544.     mov    OldKeySeg,es
  545.  
  546.     mov    dx,cs
  547.     mov    ds,dx
  548.  
  549.     mov    dx,NewKey
  550.     mov    ax,2516h
  551.     int    21h
  552.  
  553.     pop    es
  554.     pop    ds
  555.     ret
  556. HookKey endp
  557.  
  558. ; "Press" Fn and stuff scan code in ch
  559. WithFn proc near
  560.     assume ds:nothing,ss:nothing,es:nothing
  561.     push    es
  562.     cmp    Is100LX,0
  563.     je    @@Exit
  564.     mov    ax,seg biosdata        ; set es to the BIOS data seg at 40h
  565.     mov    es,ax
  566.     assume    es:biosdata
  567.     or    KbdFlgs,10h        ; Set flag to indicate next key is Fn
  568.     or    MiscFlags,10h        ; Set Keyboard flag for FN active
  569.     and    MiscFlags,NOT 20h    ; And clear the FN Clear flag
  570.     mov    LastKey,79h        ; Put the last key pressed to "Fn"
  571.     mov    al,ch
  572.     out    60h,al            ; stuff the key into the KB i/o port
  573.     int    9            ; simulate a keyboard interrupt
  574.     and    MiscFlags,NOT 10h    ; Clear Keyboard flag for FN active
  575.     or    MiscFlags,20h        ; And set the FN Clear flag
  576. @@Exit:
  577.     pop    es
  578.     assume es:nothing
  579.     ret
  580. WithFn endp
  581.  
  582. ForceUART proc near
  583.     assume ds:nothing,ss:nothing,es:nothing
  584.  
  585.     cmp    Is100LX,0
  586.     je    PowerOn        ; assume the power is on for non-100LX
  587.  
  588.     pushf            ; save interrupt state
  589.     cli            ; disable interrupts
  590.  
  591.     in    al,22h        ; read current index register
  592.     mov    ah,al        ;   and save it in ah
  593.  
  594.     mov    al,51h        ; select hidden reg 51h
  595.     out    22h,al
  596.     in    al,23h        ; read hidden reg 51h
  597.     and    al,21h        ; 20h bit is rs232 pwr, 1 bit is IR on
  598.     cmp    al,20h        ; we want rs232 on and IR off
  599.     je    RestoreIx
  600.     mov    ax,4900h    ; route serial port to wire
  601.     int    15h
  602.     mov    ax,4a01h    ; turn serial port on
  603.     int    15h
  604. RestoreIx:
  605.     mov    al,ah        ; bring original index back to al
  606.     out    22h,al        ; restore original index
  607.     popf            ; restore interrupt state
  608. ; Fall into PowerOn...
  609.  
  610. PowerOn:
  611. ; check that UART has not been reconfigured
  612.     mov    dx,UartBase        ; get base address of UART
  613.     add    dx,lcont        ; move to the line control register
  614.     in    al,dx            ; read current line configuration
  615.     cmp    al,sioConfig        ; does it match ours?
  616.     jne    ResetUART        ; if not, reset UART
  617.     or    al,80h            ; raise Divisor Latch Access Bit (DLAB)
  618.     out    dx,al
  619.  
  620.     add    dx,(dlab_l - lcont)    ; move to low byte of rate divisor
  621.     in    al,dx            ; read it
  622.     cmp    al,low baudRateDivisor    ; right value?
  623.     jne    ResetUart        ; if not, reset UART
  624.  
  625.     inc    dx            ; move to high byte of rate divisor
  626.     in    al,dx            ; read it
  627.     cmp    al,high baudRateDivisor    ; right value?
  628.     jne    ResetUART        ; if not, reset UART
  629.  
  630.     add    dx,(lcont - dlab_h)    ; move back to line control
  631.     mov    al,sioConfig        ; drop DLAB and return to our settings
  632.     out    dx,al
  633.     jmp    short UartOk
  634.  
  635. ResetUart:
  636.     call    InitUart        ; force UART to our config
  637. UartOk:
  638.     ret
  639. ForceUART endp
  640.  
  641. ; This table maps a video index (as returned in bh by int 15h, ah=0dfh)
  642. ; to the next zoom-number (as used in al by int 15h, ah=0d0h). This only
  643. ; works for text modes which neatly prevents us from trying to zoom a
  644. ; SysMgr app - they all run in graphics mode 6. A value of zero says
  645. ; "no change". In terms of the index the three zoom "cycles" are:
  646. ;
  647. ;   2 -> 10 -> 14 -> 2         3 -> 11 -> 15 -> 3         7 -> 9 -> 7
  648. ;        12 -> 14                   13 -> 15
  649. ;
  650. ; The unsupported 40*25 modes zoom to the 40*16 modes
  651. ;
  652. ; The undocumented "Z" option changes this to include the 40*25 modes:
  653. ;
  654. ; 2 -> 10 -> 12 -> 14 -> 2     3 -> 11 -> 13 -> 15 -> 3   7 -> 9 -> 7
  655. ;
  656.  
  657. NextZoom label byte    ; ix  mode zoom
  658.     db    0    ;  0    0    *   40*25  B&W   CGA Low Res Text
  659.     db    0    ;  1    1    *   40*25  Color CGA Low Res Text
  660.     db    80h    ;  2    2    2   80*25  B&W   CGA Hi Res Text
  661.     db    81h    ;  3    3    3   80*25  Color CGA Hi Res Text
  662.     db    0    ;  4    4    *  320*200 Color CGA Low Res Graphics
  663.     db    0    ;  5    5    *  320*200 B&W   CGA Low Res Graphics
  664.     db    0    ;  6    6    *  640*200 Color CGA Hi Res Graphics
  665.     db    21h    ;  7    7    7   40*16  B&W   MDA Zoom Text
  666.     db    0    ;  8   20h   *  240*128 B&W   95LX MDA graphics
  667.     db    7    ;  9    7   21h  80*25  B&W   MDA Text
  668. OptZa    db    84h    ; 10    2   80h  64*18  B&W   CGA Zoom Text
  669. OptZb    db    85h    ; 11    3   81h  64*18  Color CGA Zoom Text
  670.     db    84h    ; 12    2   82h  40*25  B&W   CGA Zoom Text
  671.     db    85h    ; 13    3   83h  40*25  Color CGA Zoom Text
  672.     db    2    ; 14    2   84h  40*16  B&W   CGA Zoom Text
  673.     db    3    ; 15    3   85h  40*16  Color CGA Zoom Text
  674.  
  675. ; First check that UART is powered and in the configuration we need.
  676. ; Then if we are in receive mode check for a received character.
  677. CheckUart proc near
  678.     assume ds:nothing,ss:nothing,es:nothing
  679.  
  680.     push    ax
  681.  
  682. if localStack
  683.     mov    al,1
  684.     xchg    InCheckUart,al            ; test and set
  685.     or    al,al
  686.     jne    EarlyExit
  687.  
  688.     mov    ss_save,ss            ; save caller's ss
  689.     mov    sp_save,sp            ;  and sp
  690.     mov    ax,cs                ; use local stack
  691.     mov    ss,ax
  692.     mov    sp,offset stack_top
  693. endif
  694.  
  695.     push    bx
  696.     push    cx
  697.     push    dx
  698.     push    si
  699.     push    di
  700.     push    bp
  701.     push    ds
  702.     push    es
  703.  
  704.     mov    ax,seg biosdata        ; set ds to the BIOS data seg at 40h
  705.     mov    ds,ax
  706.     assume    ds:biosdata
  707.  
  708.     call    ForceUART
  709.  
  710.     cmp    Receive,0        ; if we are in send mode don't
  711.     je    @@Exit            ;   check the UART
  712.     mov    dx,UartBase        ; get base address of UART
  713.     add    dx,lstat        ; move to the line status register
  714.     in    al,dx            ;  and read it
  715. if debug
  716. ; any errors?
  717.     test    al,(pcOverrunError OR pcParityError OR pcFramingError)
  718.     jz    NoLineError
  719.     push    ax
  720.     mov    al,"E"
  721.     call    PutChar
  722.     pop    ax
  723.     push    ax
  724.     call    PutByte
  725.     mov    al," "
  726.     call    PutChar
  727.     pop    ax
  728. NoLineError:
  729. endif
  730.     test    al,pcRecvDataAvailable    ; received data ready?
  731.     jz    @@Exit
  732.     add    dx,(rx - lstat)        ; move to the receive data register
  733.     in    al,dx            ; read the sent byte
  734. if debug
  735.     push    ax
  736.     mov    al,"R"
  737.     call    PutChar
  738.     pop    ax
  739.     push    ax
  740.     call    PutByte
  741.     mov    al," "
  742.     call    PutChar
  743.     pop    ax
  744. endif
  745.  
  746. ; reset auto-sleep countdown
  747.     mov    cx,sleepTimeout        ; reload sleepCountdown
  748.     mov    sleepCountdown,cx
  749.  
  750.     mov    ah,al            ; save new char in ah
  751.     mov    cx,pending        ; get previously received bytes
  752.     cmp    pendingCount,2        ; two valid bytes in pending?
  753.     jb    partialPacket
  754.     mov    al,ch            ; calc ROL(hi) XOR lo XOR new
  755.     rol    al,1
  756.     xor    al,cl
  757.     xor    al,ah
  758.     cmp    al,5ah
  759.     je    ValidPacket
  760. partialPacket:
  761.     inc    pendingCount        ; count the new byte
  762.     mov    cl,ch            ; push the new byte into pending
  763.     mov    ch,ah
  764.     mov    pending,cx
  765.     jmp    @@Exit            ; leave and wait for next byte
  766.  
  767. ValidPacket:
  768.     mov    pendingCount,0        ; mark pending bytes as consumed
  769.     mov    pending,0        ; not really needed if the code works
  770.     mov    ax,cx            ; bring scan-code/ASCII to ax
  771.     cmp    ah,0f5h            ; special code w/scan-code?
  772.     jne    NotShift
  773. ; if the high byte is 0f5h then the low byte is the int 9 scan code
  774. DoShift:
  775.     cmp    Is100LX,0
  776.     jne    DoStuff
  777.     jmp    @@Exit            ; can't stuff the key I/O on non-HP
  778.  
  779. DoStuff:
  780.     out    60h,al            ; stuff the key into the KB i/o port
  781.     int    9            ; simulate a keyboard interrupt
  782.     jmp    @@Exit
  783.  
  784. NotShift:
  785.     cmp    ah,0f4h            ; other special codes?
  786.     jne    StuffIt
  787.     cmp    al,low LockCode        ; turn Caps Lock on?
  788.     jne    NotLock
  789.     mov    cl,40h            ; desired shift state w/caps Lock on
  790. SetLock:
  791.     mov    ah,2            ; get shift status
  792.     pushf                ; go through old vector
  793.     call    [OldKey]        ;  to prevent recursion
  794.     xor    al,cl
  795.     test    al,40h
  796.     jz    NotUnlock        ; Caps already correct
  797.     mov    al,LShiftScan          ; "Press" the left shift key
  798.     out    60h,al
  799.     int    9
  800.     mov    al,CapsScan          ; "Press" the Caps key
  801.     out    60h,al
  802.     int    9
  803.     mov    al,CapsScan or 80h    ; "Release" the Caps key
  804.     out    60h,al
  805.     int    9
  806.     mov    al,LShiftScan or 80h    ; "Release" the left shift key
  807.     jmp    short DoShift
  808.  
  809. NotLock:
  810.     cmp    al,low UnlockCode    ; turn Caps Lock off?
  811.     jne    NotUnlock        ; no known 0f5xx key, ignore it
  812.     sub    cl,cl            ; desired shift state w/caps Lock off
  813.     jmp    SetLock
  814.  
  815. StuffIt:
  816.     or    cl,cl            ; ASCII = 0?
  817.     jnz    NotFnFx
  818.     cmp    cx,0db00h        ; Fn F1 ?
  819.     jb    NotFnFx
  820.     cmp    cx,0e400h        ; Fn F10 ?
  821.     ja    NotFnFx
  822.     sub    ch,0dbh-3bh        ; 0dbh..0e4h -> 3bh..44h
  823.     call    WithFn            ; stuff scan code with Fn "pressed"
  824.     jmp    @@Exit
  825.  
  826. NotFnFx:
  827. ; The scancode for Alt-downarrow is 0A000, this is the same scancode
  828. ; as the ON key. If you stuff a 0A000 and you are running on batteries
  829. ; the 100LX sort-of turns off: the screen goes blank.
  830. ; Solution: discard 0A000's on the LX
  831.     cmp    Is100LX,0
  832.     jz    NotOnKey
  833.     cmp    cx,0a000h
  834.     je    IgnoreKey
  835. NotOnKey:
  836. ; The SysMgr applications handle the ZOOM scan code on their own. So
  837. ; stuffing the scancode works. But zooming in DOS is done below interrupts
  838. ; 9 and 16. So we have to do it ourselves.
  839.     cmp    cx,0d000h    ; scancode for ZOOM
  840.     jne    NotZoom
  841.     mov    ah,0dfh        ; Get the video mode/zoom index in bh
  842.     int    10h
  843.     mov    bl,bh        ; convert bh to a word in bx
  844.     sub    bh,bh
  845.     mov    al,NextZoom[bx]    ; get next zoom number
  846.     or    al,al        ; non-zoomable?
  847.     jz    NotZoom
  848.     mov    ah,0d0h        ; Text Zoom function
  849.     int    10h
  850. NotZoom:
  851.  
  852. ; Very, very odd and very, very frustrating. We would like to stuff our
  853. ; keys by simply calling the "write key" function. This works fine in all
  854. ; cases EXCEPT when you run DOS or a DOS app from SysMgr (<&..>D). If you
  855. ; do then each key is duplicated 50-100% of the time. Directly manipulating
  856. ; the scan code buffer works in all cases so for now that is what we do.
  857. if 0
  858.     mov    ah,5            ; ch = scan code, cl = ASCII
  859.     int    16h            ; add to typeahead buffer
  860. else
  861.     pushf                ; save current interrupt enable state
  862.     cli                ; disable interrupts
  863.     mov    bx,bufferTail
  864.     mov    dx,bx            ; advance the ptr before we store
  865.     inc    dx            ; to check for overflow
  866.     inc    dx
  867.     cmp    dx,bufferEnd
  868.     jb    NoWrap
  869.     mov    dx,bufferStart
  870. NoWrap:
  871.     cmp    dx,bufferHead
  872.     je    Overflow
  873.     mov    [bx],cx            ; save scan code
  874.     mov    bufferTail,dx        ; save new tail ptr
  875.     or    [LastInterrupt],2    ; key int was last interrupt
  876.                     ; (PUSHKEYS.COM does this)
  877. Overflow:
  878.     popf                ; restore interrupt enable state
  879. endif
  880. if debug
  881.     mov    al,"W"
  882.     call    PutChar
  883.     mov    ax,cx
  884.     call    PutWord
  885.     mov    al," "
  886.     call    PutChar
  887. endif
  888.  
  889. NotUnlock:            ; no known 0f5xx key, ignore it
  890. IgnoreKey:
  891.  
  892. @@Exit:
  893.     pop    es
  894.     pop    ds
  895.     pop    bp
  896.     pop    di
  897.     pop    si
  898.     pop    dx
  899.     pop    cx
  900.     pop    bx
  901.  
  902. if localStack
  903.     mov    ss,ss_save
  904.     mov    sp,sp_save
  905.     mov    InCheckUart,0        ; clear recursion flag
  906. endif
  907.  
  908. EarlyExit:
  909.     pop    ax
  910.  
  911.     ret
  912. CheckUart endp
  913.  
  914. ;=======================================================================
  915.  
  916. NewTick proc    far
  917.     assume ds:nothing,ss:nothing,es:nothing
  918.     push    ax
  919.     cmp    Enabled,0
  920.     jz    SlowTick
  921.     call    CheckUart    ; poll the UART every actual tick
  922.     dec    TickCount
  923.     jnz    cont6
  924.     mov    TickCount,TickFactor
  925. SlowTick:
  926.     pushf
  927.     call    [OldTick]
  928.     jmp    short cont7
  929. cont6:
  930.     mov    al,20h        ; EOI
  931.     out    20h,al        ; Send EOI to 8259 int. controller
  932. cont7:
  933.     pop    ax
  934.     iret
  935. NewTick endp
  936.  
  937. NewMpx proc near
  938.     assume ds:nothing,ss:nothing,es:nothing
  939.     cmp    ah,OurMpxNum        ; our multiplex ID?
  940.     jne    UseOldMpx
  941.     or    al,al            ; the generic ID function code?
  942.     jnz    NotID
  943.     dec    al            ; 0 >> ff
  944.     iret
  945.  
  946. NotID:
  947.     cmp    al,RemKeyMpxFn        ; our (only) function code
  948.     jne    UseOldMpx
  949.     mov    ax,bcdVersion
  950.     mov    bx,"Re"
  951.     mov    cx,"mK"
  952.     mov    dx,"ey"
  953.     push    cs            ; return segment of resident code in es
  954.     pop    es
  955.     iret
  956.  
  957. UseOldMpx:
  958.     jmp    [OldMpx]
  959.  
  960. NewMpx endp
  961.  
  962. ;=======================================================================
  963.  
  964. InitUart proc near
  965.     assume ds:nothing,ss:nothing,es:nothing
  966.  
  967.     mov    dx,UartBase        ; get base address of UART
  968.     add    dx,lcont        ; move to line control register
  969.     mov    al,80h            ; enable DLAB
  970.     out    dx,al
  971.  
  972.     add    dx,(dlab_l - lcont)    ; move to low byte of DLAB
  973.     mov    al,low baudRateDivisor
  974.     out    dx,al
  975.  
  976.     inc    dx            ; move to high byte of DLAB
  977.     mov    al,high baudRateDivisor
  978.     out    dx,al
  979.  
  980.     add    dx,(lcont - dlab_h)    ; move back to line control register
  981.     mov    al,sioConfig        ; set our configuration
  982.     out    dx,al
  983.  
  984.     add    dx,(mcont - lcont)    ; move to modem control register
  985.     mov    al,3            ; raise dtr, rts
  986.     out    dx,al
  987.  
  988.     mov    dx,UartBase        ; get base address of UART
  989.     in    al,dx            ; flush the UART
  990.     jmp    $+2            ; delay
  991.     in    al,dx
  992.  
  993.     ret
  994. InitUart endp
  995.  
  996. ; Returns non-zero if swap fails
  997. BannerSwap proc near
  998.     assume ds:nothing,ss:nothing,es:nothing
  999.     push    ax
  1000.     push    bx
  1001.     push    cx
  1002.     push    dx
  1003.     push    di
  1004.     push    si
  1005.     push    ds
  1006.     mov    ax,seg biosdata        ; set ds to the
  1007.     mov    ds,ax            ; BIOS data seg at 40h
  1008.     assume    ds:biosdata
  1009.     mov    cx,0b000h        ;assume seg. of mono
  1010.     mov    al,crt_mode
  1011.     cmp    al,7            ;mono mode ?
  1012.     je    mode_ok
  1013.     and    al,0feh            ;test for 2 or 3
  1014.     cmp    al,2            ;(80*25 b&w or color)
  1015.     jne    @@Exit            ;return non-zero for unsupported mode
  1016.     mov    ch,0b8h            ;color seg.
  1017. mode_ok:
  1018.     mov    ax,crt_cols        ;1..80 -> 0..79
  1019.     dec    si
  1020.     mov    si,ax            ; * 25 (19h) bytes, 12.5 lines
  1021.     add    ax,ax
  1022.     add    ax,ax
  1023.     add    ax,ax
  1024.     add    si,ax
  1025.     add    ax,ax
  1026.     add    si,ax
  1027.     add    si,crt_start        ;start of buffer
  1028.     mov    ds,cx            ;set the seg.
  1029.     assume ds:nothing
  1030.     mov    cx,bannerLength
  1031.     sub    si,cx            ; center banner on line
  1032.     and    si, not 1        ; force to even address
  1033.     lea    di,banner
  1034.     pushf                ; save current interrupt enable state
  1035.     cli                ; disable interrupts
  1036. BannerLoop:
  1037.     mov    ax,cs:[di]        ;get char/attrib to swap in
  1038.     xchg    ax,[si]
  1039.     mov    cs:[di],ax        ;save swapped char/attrib
  1040.     inc    si
  1041.     inc    si
  1042.     inc    di
  1043.     inc    di
  1044.     loop    BannerLoop
  1045.     popf                ; restore interrupt enable state
  1046.     sub    ax,ax            ; return zero flag for success
  1047. @@Exit:
  1048.     pop    ds
  1049.     assume ds:nothing
  1050.     pop    si
  1051.     pop    di
  1052.     pop    dx
  1053.     pop    cx
  1054.     pop    bx
  1055.     pop    ax
  1056.     ret
  1057. BannerSwap endp
  1058.  
  1059. ; Returns with zero flag if hot-key caught
  1060. CheckForHot proc near
  1061.     assume ds:nothing,ss:nothing,es:nothing
  1062.     cmp    ax,HotKey        ; hot key?
  1063.     jne    @@Exit
  1064.     xor    Enabled,1        ; toggle enable
  1065.     jz    Disabled
  1066.     call    SpeedUp
  1067.     call    Tweedle
  1068.     jmp    short @@ExitWithZero
  1069.  
  1070. Disabled:
  1071.     call    SlowDown
  1072.     call    Buzz
  1073. @@ExitWithZero:
  1074.     sub    ax,ax
  1075. @@Exit:
  1076.     ret
  1077. CheckForHot endp
  1078.  
  1079. NewKeyRecv    proc far
  1080.     assume ds:nothing,ss:nothing,es:nothing
  1081.     cmp    ah,0
  1082.     je    CheckRead    ;  0 - Read Key
  1083.     cmp    ah,10h
  1084.     je    CheckRead    ; 10 - Extended Read Key
  1085.     cmp    Is100LX,0    ; for a PC the rest default to CheckBefore
  1086.     jz    CheckBefore
  1087.     cmp    ah,13h
  1088.     je    CheckEvent    ; 13 - Event Wait
  1089.     cmp    ah,14h
  1090.     je    CheckEvent    ; 14 - Event Wait with Timeout
  1091. ; Otherwise for these and any unknown functions use CheckBefore:
  1092. ;  1 - Check Key
  1093. ;  2 - Get Shift Status
  1094. ;  3 - Set Repeat Rate
  1095. ;  5 - Stuff Key
  1096. ; 11 - Extended Check Key
  1097. ; 12 - Extended Shift Status
  1098. CheckBefore:
  1099.     push    ax            ; save function code
  1100. ; Peek at the next key. Use the old vector to prevent recursion.
  1101.     mov    ah,11h
  1102. ; Simulate an int through the old vector:
  1103.     pushf
  1104.     call    [OldKey]
  1105.     jz    NotHot
  1106.     call    CheckForHot
  1107.     jnz    NotHot
  1108.     mov    ah,10h            ; eat the hot-key
  1109.     pushf
  1110.     call    [OldKey]
  1111. NotHot:
  1112.     pop    ax            ; recover original function code
  1113.     jmp    [OldKey]
  1114.  
  1115. CheckRead:
  1116.     push    ax            ; save function code
  1117. ; Simulate an int through the old vector:
  1118.     pushf
  1119.     call    [OldKey]        ; do read operation
  1120.     call    CheckForHot
  1121.     jnz    DidNotReadHot
  1122.     pop    ax            ; recover saved function code
  1123.     jmp    CheckRead        ; read another key
  1124.  
  1125. DidNotReadHot:
  1126.     add    sp,2            ; discard saved function code
  1127.     ret    2        ; discard flags pushed by original int 16
  1128.  
  1129. CheckEvent:
  1130. ; Simulate an int through the old vector:
  1131.     pushf
  1132.     call    [OldKey]        ; do event-wait operation
  1133.     pushf                ; save event flags
  1134.     push    ax            ; save key or shift flags
  1135.     jz    NotHotEvent        ; if zero no key so no hot key
  1136.     call    CheckForHot
  1137. NotHotEvent:
  1138.     pop    ax            ; recover key or shift flags
  1139.     popf                ; recover event flags
  1140.     ret    2        ; discard flags pushed by original int 16
  1141.  
  1142.  
  1143. NewKeyRecv endp
  1144.  
  1145. ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  1146.  
  1147. RecvEnd label byte      ; End of receiver TSR, all above is resident code
  1148.  
  1149. ; We need to send two bytes (ASCII, then scan code) in a form that can
  1150. ; be validated. To do this we send three bytes: low, high and low XOR
  1151. ; ROL(high) XOR 5a.  The receiver collects bytes and knows that a valid
  1152. ; word has been received when 1st XOR ROL(2nd) XOR = 5ah. This scheme
  1153. ; is not perfect because the right pair of mis-sent words could look
  1154. ; like a single valid packet. But in general it should tend to
  1155. ; synchronize.
  1156.  
  1157. SendWord proc near
  1158.     assume ds:nothing,ss:nothing,es:nothing
  1159.     push    ax
  1160.     push    cx
  1161.  
  1162.     mov    cx,ax            ; save word to send in cx
  1163.     mov    al,cl            ; send the low byte
  1164.     call    SendByte
  1165.     mov    al,ch            ; send the high byte
  1166.     call    SendByte
  1167. ; send low XOR rol(high) XOR 5a as the end of a valid packet
  1168.     mov    al,ch
  1169.     rol    al,1
  1170.     xor    al,cl
  1171.     xor    al,5ah
  1172.     call    SendByte
  1173.  
  1174.     pop    cx
  1175.     pop    ax
  1176.     ret
  1177. SendWord endp
  1178.  
  1179. ; Send the byte in al out the serial port
  1180. SendByte proc near
  1181.     assume ds:nothing,ss:nothing,es:nothing
  1182.  
  1183. if debug
  1184.     push    ax
  1185.     mov    al,"S"
  1186.     call    PutChar
  1187.     pop    ax
  1188.     push    ax
  1189.     call    PutByte
  1190.     mov    al," "
  1191.     call    PutChar
  1192.     pop    ax
  1193. endif
  1194.  
  1195.     push    dx
  1196.     push    ax
  1197.     mov    dx,UartBase    ; get base address of UART
  1198.     add    dx,lstat    ; move to line status register
  1199. UartWait:
  1200.     in    al,dx
  1201.     test    al,20h        ; is the transmit buffer empty?
  1202.     je    UartWait
  1203.     pop    ax
  1204.     add    dx,(tx - lstat)    ; move to the transmit buffer register
  1205.     out    dx,al
  1206.     pop    dx
  1207.     ret
  1208. SendByte endp
  1209.  
  1210. NewKeySend proc far
  1211.     assume ds:nothing,ss:nothing,es:nothing
  1212.  
  1213.     push    ax
  1214.     push    bx
  1215.     push    cx
  1216.     push    dx
  1217.     push    es
  1218.  
  1219. ; Peek at the next key. Use the old vector to prevent recursion.
  1220.     mov    ah,11h
  1221.     pushf
  1222.     call    [OldKey]
  1223.     jz    @@Exit
  1224.     cmp    ax,HotKey        ; hot key?
  1225.     jne    @@Exit
  1226.     mov    ah,10h            ; eat the hot-key
  1227.     pushf
  1228.     call    [OldKey]
  1229.     call    BannerSwap        ; put up "RemKey Active" message
  1230.     jz    BannerUp
  1231.     call    Tweedle            ; give audible signal if banner fails
  1232.     call    SendKeys
  1233.     call    Buzz            ; give audible signal on exit
  1234.     jmp    short @@Exit
  1235.  
  1236. BannerUp:
  1237.     call    SendKeys
  1238.     call    BannerSwap        ; restore screen
  1239. @@Exit:
  1240.     pop    es
  1241.     pop    dx
  1242.     pop    cx
  1243.     pop    bx
  1244.     pop    ax
  1245.     jmp    [OldKey]
  1246. NewKeySend endp
  1247.  
  1248. ; Special PC scan codes and what they map to on the 100LX
  1249. ScanMap label word
  1250. ;          PC    100LX
  1251.     dw    03300h,    0d100h    ; Alt "," to DATE
  1252.     dw    03400h,    0d200h    ; Alt "." to TIME
  1253.     dw    0a300h,    0d400h    ; Alt Del to CUT
  1254.     dw    00e00h,    0d500h    ; Alt Bsp to COPY
  1255.     dw    0a200h,    0d600h    ; Alt Ins to PASTE
  1256.     dw     7800h,    0a800h    ; Alt ! to Filer
  1257.     dw     7900h,    0ac00h    ; Alt @ to cc:Mail
  1258.     dw     7a00h,    0b000h    ; Alt # to Appt
  1259.     dw     7b00h,    0b400h    ; Alt $ to Phone
  1260.     dw     7d00h,    0b800h    ; Alt ^ to Memo
  1261.     dw     7e00h,    0bc00h    ; Alt & to Lotus
  1262.     dw     8000h,    0c000h    ; Alt ( to HP Calc
  1263.     dw     8100h,    0a400h    ; Alt ) to More
  1264.  
  1265. ; The Ctrl-Blue keys:
  1266. ; The 0f5h in the lower byte of these PC scan codes encodes the
  1267. ; fact that these pairs are used when Ctrl and Alt are pushed.
  1268.  
  1269.     dw     78f5h,    0ae00h    ; Ctrl Alt ! to Setup
  1270.     dw     79f5h,    0b200h    ; Ctrl Alt @ to Data Comm
  1271.     dw     7af5h,    0b600h    ; Ctrl Alt # to Stopwatch
  1272.     dw     7bf5h,    0ba00h    ; Ctrl Alt $ to Database
  1273.     dw     7df5h,    0be00h    ; Ctrl Alt ^ to Note Taker
  1274.     dw     7ef5h,    0c200h    ; Ctrl Alt & to DOS
  1275.     dw     80f5h,    0c600h    ; Ctrl Alt ( to World Time
  1276.     dw     81f5h,    0aa00h    ; Ctrl Alt ) to System Macros
  1277.  
  1278.     dw     68f5h, 0db00h    ; Ctrl Alt F1  to Fn F1,  macro 1
  1279.     dw     69f5h, 0dc00h    ; Ctrl Alt F2  to Fn F2,  macro 2
  1280.     dw     6af5h, 0dd00h    ; Ctrl Alt F3  to Fn F3,  macro 3
  1281.     dw     6bf5h, 0de00h    ; Ctrl Alt F4  to Fn F4,  macro 4
  1282.     dw     6cf5h, 0df00h    ; Ctrl Alt F5  to Fn F5,  macro 5
  1283.     dw     6df5h, 0e000h    ; Ctrl Alt F6  to Fn F6,  macro 6
  1284.     dw     6ef5h, 0e100h    ; Ctrl Alt F7  to Fn F7,  macro 7
  1285.     dw     6ff5h, 0e200h    ; Ctrl Alt F8  to Fn F8,  macro 8
  1286.     dw     70f5h, 0e300h    ; Ctrl Alt F9  to Fn F9,  macro 9
  1287.     dw     71f5h, 0e400h    ; Ctrl Alt F10 to Fn F10, macro 10
  1288.  
  1289. ; The Alt-Blue keys:
  1290. ; The 0f6h in the lower byte of these PC scan codes encodes the
  1291. ; fact that these pairs are used when Shift and Alt are pushed.
  1292.  
  1293.     dw     78f6h,    0ab00h    ; Shift Alt ! to Alt Filer
  1294.     dw     79f6h,    0af00h    ; Shift Alt @ to Alt cc:Mail
  1295.     dw     7af6h,    0b300h    ; Shift Alt # to Alt Appt
  1296.     dw     7bf6h,    0b700h    ; Shift Alt $ to Alt Phone
  1297.     dw     7df6h,    0bb00h    ; Shift Alt ^ to Alt Memo
  1298.     dw     7ef6h,    0bf00h    ; Shift Alt & to Alt Lotus
  1299.     dw     80f6h,    0c300h    ; Shift Alt ( to Alt HP Calc
  1300.     dw     81f6h,    0a700h    ; Shift Alt ) to Alt More
  1301.  
  1302.  
  1303. ; Shifted cursor keys:
  1304. ; In a number of places in the 100LX apps a cursor key combined with
  1305. ; shift is used. Most commonly this is used in a multi-line text field
  1306. ; to select or highlight a portion of the text. Memo, note fields and
  1307. ; the system macro edit screen are all examples of this. Also in Appt
  1308. ; shift up-cursor and shift down-cursor can move to the previous or
  1309. ; next week.
  1310. ; The scan codes that these apps are checking for appear to be the scan
  1311. ; codes of the digits on a normal PCs numeric pad. This makes sense as
  1312. ; that is what this pad will produce when NumLock is off, shift is held
  1313. ; and a digit is pressed. But what about the dedicated cursor keys in
  1314. ; the inverted "T" on an extended keyboard?  These produce the scancode
  1315. ; of a cursor key in the high byte and 0e0h in the low byte. RemKey
  1316. ; just replaces the 0e0h with 0 which produces the scancodes of the
  1317. ; numeric pad cursor keys. Most apps in the 100LX will check if the
  1318. ; shift key is down and treat such an event as a "shift selection".
  1319. ; Memo and note fields behave as expected.
  1320. ; But the system macro editor and the caledar in Appt appear to only
  1321. ; check for the shift bit and the numeric pad scancodes. So the
  1322. ; following entries map the dedicated up, down, left, right, home, end,
  1323. ; Page Up and Page Down keys to the numeric pad scan codes when the
  1324. ; shift key is down.  The 0f7h in the low byte of the first
  1325. ; scancode/ASCII pair artificially endcodes that only the shift key was
  1326. ; pressed.
  1327.  
  1328.     dw    4ff7h, 4f31h    ; Shift end   (pad 1)
  1329.     dw    50f7h, 5032h    ; Shift down  (pad 2)
  1330.     dw    51f7h, 5133h    ; Shift pg dn (pad 3)
  1331.     dw    4bf7h, 4b34h    ; Shift left  (pad 4)
  1332.  
  1333.     dw    4df7h, 4d36h    ; Shift right (pad 6)
  1334.     dw    47f7h, 4737h    ; Shift home  (pad 7)
  1335.     dw    48f7h, 4838h    ; Shift up    (pad 8)
  1336.     dw    49f7h, 4939h    ; Shift pg up (pad 9)
  1337.  
  1338. ScanEntries equ ($-ScanMap)/4    ; 4 bytes, 2 word per entry
  1339.  
  1340. ; These are stuffed via int 9 when the corresponding code is received.
  1341. ;
  1342. ;    40:17    byte    Keyboard flag byte 0
  1343. ;
  1344. ;        │7│6│5│4│3│2│1│0│ keyboard flag byte 0
  1345. ;         │ │ │ │ │ │ │ └─── right shift key depressed
  1346. ;         │ │ │ │ │ │ └──── left shift key depressed
  1347. ;         │ │ │ │ │ └───── CTRL key depressed
  1348. ;         │ │ │ │ └────── ALT key depressed
  1349. ;         │ │ │ └─────── scroll-lock is active
  1350. ;         │ │ └──────── num-lock is active
  1351. ;         │ └───────── caps-lock is active
  1352. ;         └────────── insert is active
  1353. ;
  1354. ShiftMap label byte
  1355.  
  1356. ; codes for released keys must come first
  1357.     db    RShiftScan+80h    ; stuffed via int 9 when UnRShiftCode received
  1358.     db    LShiftScan+80h    ;    "     "   "  "  "   UnLShiftCode  "
  1359.  
  1360. ; followed by codes for pressed keys
  1361.     db    RShiftScan    ; stuffed via int 9 when RShiftCode received
  1362.     db    LShiftScan    ;    "     "   "  "  "   LShiftCode  "
  1363.  
  1364. ShiftEntries equ ($-ShiftMap)
  1365.  
  1366. ; Send keys pressed on the local keyboard out the serial port until the
  1367. ; hot key is pressed.
  1368. ;
  1369. ; The dl reg holds the current shift state, dh holds the previous.
  1370. ; bh holds bits which have changed from 0 to 1, bl from 1 to 0
  1371. ;
  1372. ; The si register is used to detect a press and release of either Alt-key:
  1373. ;   bit 0 - either Alt-key was pressed
  1374.  
  1375. SendKeys proc near
  1376.     assume ds:nothing,ss:nothing,es:nothing
  1377.     push    ax
  1378.     push    bx
  1379.     push    cx
  1380.     push    dx
  1381.     push    di
  1382.     push    si
  1383.     push    ds
  1384.     push    es
  1385.  
  1386.     call    ForceUART
  1387.  
  1388.     mov    ah,2        ; get shift status
  1389.     pushf            ; go through old vector to prevent recursion
  1390.     call    [OldKey]
  1391.     mov    dl,al        ; put current shift state in dl
  1392.  
  1393. KeyLoop:
  1394.     mov    dh,dl        ; save previous shift state in dh
  1395.     mov    ah,2        ; get shift status
  1396.     pushf            ; go through old vector to prevent recursion
  1397.     call    [OldKey]
  1398.     mov    dl,al        ; save new shift state in dl
  1399.  
  1400. ; Calculate which bits have changed from 0 to 1 in bh and from 1 to 0 in bl
  1401.     mov    bl,dh        ; get last shift state
  1402.     xor    bl,dl        ; calculate which bits changed either way
  1403.     mov    bh,bl        ; save in bh
  1404.     and    bh,dl        ; isolate bits that changed from 0 to 1
  1405.     and    bl,dh        ; isolate bits that changed from 1 to 0
  1406.  
  1407. ; Although the ASCII and scan codes we send already contains our shift state
  1408. ; we need to explicitly set the shift state on the remote 100LX because
  1409. ; some of the built-in apps directly test the shift state. Examples are
  1410. ; shift-cursor in Memo and Appt. Other DOS programs also directly test the
  1411. ; state of Ctrl and Alt so we send changes on these also.
  1412.  
  1413. ; The first half of the loops look for released keys, the second for
  1414. ; pressed keys.
  1415.     mov    cl,bl        ; gets bits that have changed from 1 to 0
  1416.     sub    di,di        ; initial offset into ShiftMap is zero
  1417. ShiftMapLoop:
  1418.     cmp    di,ShiftEntries/2
  1419.     jne    NotHalfway
  1420.     mov    cl,bh        ; gets bits that have changed from 0 to 1
  1421. NotHalfway:
  1422.     test    cl,1        ; did bit change?
  1423.     jz    NoShiftChg
  1424.     mov    al,ShiftMap[di]
  1425.     mov    ah,0f5h
  1426.     call    SendWord    ; send scan code to effect shift change
  1427. NoShiftChg:
  1428.     shr    cl,1
  1429.     inc    di
  1430.     cmp    di,ShiftEntries
  1431.     jb    ShiftMapLoop
  1432.  
  1433. ; has Caps Lock state changed?
  1434.     mov    ax,LockCode    ; assume Caps Lock is on
  1435.     test    bh,40h        ; did Caps Lock bit change to 1?
  1436.     jnz    SendLock
  1437.     mov    ax,UnlockCode    ; assume Caps Lock is off
  1438.     test    bl,40h        ; did Caps Lock bit change to 0?
  1439.     jz    NoLockChange
  1440. SendLock:
  1441.     call    SendWord    ; send encoding of Caps (Un)Lock
  1442. NoLockChange:
  1443. ; If either "Alt" is pressed and released then we simulate a press of the
  1444. ; MENU key on the remote 100LX
  1445.  
  1446.     test    bh,8        ; either Alt-key changed to pressed?
  1447.     jz    CheckAltUp    ; jump if no change
  1448.     mov    si,1        ; remember that Alt-key state changed
  1449.     jmp    short CheckBIOS
  1450.  
  1451. CheckAltUp:
  1452.     test    bl,8        ; either Alt-key changed to released?
  1453.     jz    CheckBIOS    ; jump if no change
  1454. ; Was Alt just pressed and released with no intervening keystroke?
  1455.     or    si,si
  1456.     mov    si,0        ;  (reset the flags no matter what)
  1457.     jz    CheckBIOS
  1458.     mov    ax,MenuKeyCode    ; scan code for menu key
  1459.     jmp    SendIt
  1460.  
  1461. CheckBIOS:
  1462.     mov    ah,11h        ; key waiting ?
  1463.     pushf
  1464.     call    [OldKey]
  1465.     jz    KeyLoop
  1466.     mov    ah,10h        ; read key
  1467.     pushf
  1468.     call    [OldKey]
  1469. if debug
  1470.     push    ax
  1471.     mov    al,"K"
  1472.     call    PutChar
  1473.     pop    ax
  1474.     push    ax
  1475.     call    PutWord
  1476.     mov    al," "
  1477.     call    PutChar
  1478.     pop    ax
  1479. endif
  1480.     sub    si,si        ; Any key cancels Alt key down-tap state
  1481.     test    dl,8        ; Alt key down?
  1482.     jz    NotAltSpace
  1483.     cmp    ax,3920h    ; space bar ?
  1484.     jne    NotAltSpace
  1485.     mov    ax,0d000h    ; Send Alt-space as ZOOM
  1486.     jmp    SendIt
  1487.  
  1488. NotAltSpace:
  1489.     cmp    ax,HotKey
  1490.     je    @@Exit
  1491.  
  1492. ; Some funny keys like the "/" and the <enter> on the numeric pad have a
  1493. ; scan code of 0e0h in the high byte with the correct ASCII in the low byte.
  1494. ; But this somehow conflicts with the 100LX's use of scan code 0e0h for
  1495. ; Fn F6 and is not recognized. The hack is to replace the 0e0h scan code
  1496. ; with zero.
  1497.     cmp    ah,0e0h        ; fold extended scan codes into normal ones
  1498.     jne    NotHiE0
  1499.     or    al,al        ; Let Fn F6 (0e000h) through
  1500.     je    NotHiE0        ; (this will only happen when 100LX -> 100LX)
  1501.     sub    ah,ah        ; LX apps don't recognize 0E0xx scan codes
  1502. NotHiE0:
  1503.  
  1504. ; Encode special keys by searching for them in the "exception" table
  1505.  
  1506. ; If a Shift and Alt key is pressed and the low byte is zero then
  1507. ; set the low byte to 0f6h.
  1508. ;
  1509. ; If a Ctrl and Alt key is pressed and the low byte is zero then
  1510. ; set the low byte to 0f5h.
  1511. ; If a Shift is pressed and the low byte is 0e0h then
  1512. ; set the low byte to 0f7h.
  1513. ;
  1514. ; These artificial encodings allows us to have entries for an
  1515. ; Alt-key, Shift-key and Ctrl-Alt-key in the same table.
  1516.  
  1517.     mov    cx,ax        ; save unmodified scan/ASCII pair
  1518.  
  1519.     test    dl,8        ; Alt key pressed?
  1520.     jz    NotAlt
  1521.     or    al,al        ; low byte of scan/ASCII zero?
  1522.     jnz    NotAlt
  1523.     test    dl,4        ; Ctrl key pressed?
  1524.     jz    NotCtrlAlt
  1525.     mov    al,0f5h        ; Mark key as Ctrl-Alt'ed
  1526.     jmp    short MatchScancode
  1527.  
  1528. NotCtrlAlt:
  1529.     test    dl,3        ; either shift key pressed?
  1530.     jz    MatchScancode
  1531.     mov    al,0f6h        ; Mark key as Shift-Alt'ed
  1532.     jmp    short MatchScancode
  1533.  
  1534. NotAlt:
  1535. ; Test for the dedicated cursor keys with shift
  1536.  
  1537. ; Some funny keys like the cursor inverted "T" pad contain the expected
  1538. ; scan code in the high byte but have 0e0h instead of zero in the low byte.
  1539.     cmp    al,0e0h        ; fold extended scan codes into normal ones
  1540.     jne    NotLoE0
  1541.     or    ah,ah        ; let alpha (Alt-224 or 0e0h) through
  1542.     je    NotLoE0
  1543.     sub    cl,cl        ; LX apps don't recognize xxE0 scan codes
  1544.     test    dl,3        ; either shift key pressed?
  1545.     jz    NonMapped    ; if not shifted just pass through
  1546.     mov    al,0f7h        ; Mark key as Shift-cursor
  1547.     jmp    short MatchScancode
  1548.  
  1549. NotLoE0:
  1550.  
  1551. MatchScancode:
  1552.     mov    di,(ScanEntries-1)*4 ; offset of last entry
  1553. ScanMapLoop:
  1554.     cmp    ax,ScanMap[di]
  1555.     jne    NoMatch
  1556.     mov    ax,ScanMap[di+2]
  1557.     jmp    short SendIt
  1558. NoMatch:
  1559.     sub    di,4        ; move to next pair of words
  1560.     cmp    di,-4
  1561.     jne    ScanMapLoop
  1562. NonMapped:
  1563.     mov    ax,cx        ; recover saved unmodified scancode/ASCII
  1564. ; if the scan-code/ASCII pair are not in the exception list and we
  1565. ; fall out of the loop then just send them as is:
  1566.  
  1567. SendIt:
  1568.     call    SendWord
  1569. IgnoreKey:
  1570.     jmp    KeyLoop
  1571.  
  1572. @@Exit:
  1573. ; The only way to exit is with the hot key so...
  1574.     mov    Enabled,0        ; set enabled false
  1575.     pop    es
  1576.     pop    ds
  1577.     pop    si
  1578.     pop    di
  1579.     pop    dx
  1580.     pop    cx
  1581.     pop    bx
  1582.     pop    ax
  1583.     ret
  1584. SendKeys endp
  1585.  
  1586. ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  1587.  
  1588. SendEnd label byte      ; End of sender TSR, all above is resident code
  1589.  
  1590. if fileOpt
  1591. AscToScan label byte
  1592.     db    03h    ; Control "@"
  1593.     db    1eh    ; Control "A"
  1594.     db    30h    ; Control "B"
  1595.     db    2eh    ; Control "C"
  1596.     db    20h    ; Control "D"
  1597.     db    12h    ; Control "E"
  1598.     db    21h    ; Control "F"
  1599.     db    22h    ; Control "G"
  1600.     db    0eh    ; Backspace
  1601.     db    0fh    ; Tab
  1602.     db    24h    ; Control "J"
  1603.     db    25h    ; Control "K"
  1604.     db    26h    ; Control "L"
  1605.     db    1ch    ; Enter
  1606.     db    31h    ; Control "N"
  1607.     db    18h    ; Control "O"
  1608.     db    19h    ; Control "P"
  1609.     db    10h    ; Control "Q"
  1610.     db    13h    ; Control "R"
  1611.     db    1fh    ; Control "S"
  1612.     db    14h    ; Control "T"
  1613.     db    16h    ; Control "U"
  1614.     db    2fh    ; Control "V"
  1615.     db    11h    ; Control "W"
  1616.     db    2dh    ; Control "X"
  1617.     db    15h    ; Control "Y"
  1618.     db    2ch    ; Control "Z"
  1619.     db    01h    ; Esc
  1620.     db    2bh    ; Control "\"
  1621.     db    1bh    ; Control "]"
  1622.     db    07h    ; Control "^"
  1623.     db    0ch    ; Control "_"
  1624.     db    39h    ; SPACE
  1625.     db    02h    ; "!"
  1626.     db    28h    ; Double Quote
  1627.     db    04h    ; "#"
  1628.     db    05h    ; "$"
  1629.     db    06h    ; "%"
  1630.     db    08h    ; "&"
  1631.     db    28h    ; "'"
  1632.     db    0ah    ; "("
  1633.     db    0bh    ; ")"
  1634.     db    09h    ; "*"
  1635.     db    0dh    ; "+"
  1636.     db    33h    ; ","
  1637.     db    0ch    ; "-"
  1638.     db    34h    ; "."
  1639.     db    35h    ; "/"
  1640.     db    0bh    ; "0"
  1641.     db    02h    ; "1"
  1642.     db    03h    ; "2"
  1643.     db    04h    ; "3"
  1644.     db    05h    ; "4"
  1645.     db    06h    ; "5"
  1646.     db    07h    ; "6"
  1647.     db    08h    ; "7"
  1648.     db    09h    ; "8"
  1649.     db    0ah    ; "9"
  1650.     db    27h    ; ":"
  1651.     db    27h    ; ";"
  1652.     db    33h    ; "<"
  1653.     db    0dh    ; "="
  1654.     db    34h    ; ">"
  1655.     db    35h    ; "?"
  1656.     db    03h    ; "@"
  1657.     db    1eh    ; "A"
  1658.     db    30h    ; "B"
  1659.     db    2eh    ; "C"
  1660.     db    20h    ; "D"
  1661.     db    12h    ; "E"
  1662.     db    21h    ; "F"
  1663.     db    22h    ; "G"
  1664.     db    23h    ; "H"
  1665.     db    17h    ; "I"
  1666.     db    24h    ; "J"
  1667.     db    25h    ; "K"
  1668.     db    26h    ; "L"
  1669.     db    32h    ; "M"
  1670.     db    31h    ; "N"
  1671.     db    18h    ; "O"
  1672.     db    19h    ; "P"
  1673.     db    10h    ; "Q"
  1674.     db    13h    ; "R"
  1675.     db    1fh    ; "S"
  1676.     db    14h    ; "T"
  1677.     db    16h    ; "U"
  1678.     db    2fh    ; "V"
  1679.     db    11h    ; "W"
  1680.     db    2dh    ; "X"
  1681.     db    15h    ; "Y"
  1682.     db    2ch    ; "Z"
  1683.     db    1ah    ; "["
  1684.     db    2bh    ; "\"
  1685.     db    1bh    ; "]"
  1686.     db    07h    ; "^"
  1687.     db    0ch    ; "_"
  1688.     db    29h    ; "`"
  1689.     db    1eh    ; "a"
  1690.     db    30h    ; "b"
  1691.     db    2eh    ; "c"
  1692.     db    20h    ; "d"
  1693.     db    12h    ; "e"
  1694.     db    21h    ; "f"
  1695.     db    22h    ; "g"
  1696.     db    23h    ; "h"
  1697.     db    17h    ; "i"
  1698.     db    24h    ; "j"
  1699.     db    25h    ; "k"
  1700.     db    26h    ; "l"
  1701.     db    32h    ; "m"
  1702.     db    31h    ; "n"
  1703.     db    18h    ; "o"
  1704.     db    19h    ; "p"
  1705.     db    10h    ; "q"
  1706.     db    13h    ; "r"
  1707.     db    1fh    ; "s"
  1708.     db    14h    ; "t"
  1709.     db    16h    ; "u"
  1710.     db    2fh    ; "v"
  1711.     db    11h    ; "w"
  1712.     db    2dh    ; "x"
  1713.     db    15h    ; "y"
  1714.     db    2ch    ; "z"
  1715.     db    1ah    ; "{"
  1716.     db    2bh    ; "|"
  1717.     db    1bh    ; "}"
  1718.     db    29h    ; "~"
  1719.     db    0eh    ; Delete (Ctrl-Backspace)
  1720. endif
  1721.  
  1722. Logon label byte
  1723.     db 13,10
  1724.     db 'RemKey V1.00 - Remote Keyboard Via Serial Port.'
  1725.     db 13,10
  1726.     db '$'
  1727.  
  1728. UninstMess label byte
  1729.     db 'RemKey has been uninstalled.'
  1730.     db 13,10
  1731.     db '$'
  1732.  
  1733. MpxErrMess label byte
  1734.     db 'Multiplex error.'
  1735.     db 13,10
  1736.     db '$'
  1737.  
  1738. Not100LXMess label byte
  1739.     db 'RemKey only works on a standard PC or the HP 100LX palmtop.'
  1740.     db 13,10
  1741.     db '$'
  1742.  
  1743. NotInstMess label byte
  1744.     db 'RemKey is not already installed, it can not be uninstalled.'
  1745.     db 13,10
  1746.     db '$'
  1747.  
  1748. Installed label byte
  1749.     db 'RemKey has been installed, press Alt 8 to toggle enable.'
  1750.     db 13,10
  1751.     db 'Type "REMKEY /U" to uninstall.'
  1752.     db 13,10,'$'
  1753.  
  1754. SendHelp label byte
  1755.     db 13,10
  1756.     db 'Ctrl+Alt: Setup  Data   Stop   Data   Note    DOS   World  System',13,10
  1757.     db '│                Comm   watch  base   Taker         Time   Macros',13,10
  1758.     db '│',13,10
  1759.     db '│    Alt: Filer   cc:   Appt   Phone   Memo  Lotus   HP     App',13,10
  1760.     db '│    │           Mail                         123   Calc    Mgr',13,10
  1761.     db '│    │',13,10
  1762.     db '│    │      !      @      #      $      ^      &      (      )',13,10
  1763.     db '└────┴───── 1      2      3      4      6      7      9      0',13,10
  1764.     db 13,10
  1765.     db 'Press Shift+Alt+[12346790] for Alt+Blue keys.',13,10
  1766.     db 13,10
  1767.     db '           Zoom   Date   Time    Cut  Copy  Paste',13,10
  1768.     db 'Press Alt+ Space  Comma  Period  Del  BSpc  Ins',13,10
  1769.     db 13,10
  1770.     db 'Press Ctrl+Alt+F1..F10 to play back macro, ',13,10
  1771.     db '      Shift+Ctrl+Alt+F1..F10 to record.',13,10
  1772.     db 13,10
  1773.     db 'Tap either Alt key for the Menu key, press Alt+8 to exit.'
  1774.     db 13,10,0
  1775.  
  1776. HelpMess label byte
  1777.     db 13,10
  1778.     db 'Usage: RemKey /[1,2,3,4] /[S,R,T] /[E,D] /C /U',13,10
  1779.     db '  /1,2,3,4: Comm Port, default is configured as '
  1780. HelpCommDef label byte
  1781.     db '1',13,10
  1782.     db 13,10
  1783.     db '  /S Send keys to remote computer (default on PC).',13,10
  1784.     db '  /R Receive keys, install as TSR (default on 100LX).',13,10
  1785.     db '  /T TSR sender, the hot-key is Alt+8.',13,10
  1786.     db 13,10
  1787.     db '  /E Enable this or already installed program (default for receive).',13,10
  1788.     db '  /D Disable this or already installed program (default for send).',13,10
  1789.     db 13,10
  1790.     db '  /C Configure specified serial port as default and exit.',13,10
  1791.     db '  /U Unload a previously loaded copy and exit.',13,10
  1792.     db 13,10
  1793.     db 'Example - RemKey receiving on COM3, start disabled:',13,10
  1794.     db '  remkey /r/3/d',13,10
  1795. CrLf label byte
  1796.     db 13,10
  1797.     db '$'
  1798.  
  1799. ComMess label byte
  1800.     db 'Using serial port COM'
  1801. ComNum    db '1.'
  1802.     db 13,10
  1803.     db '$'
  1804.  
  1805. ResMess label byte
  1806.     db 'RemKey is currently installed.'
  1807.     db 13,10,'$'
  1808.  
  1809. UpdateMess label byte
  1810.     db 'The parameters have been updated.'
  1811.     db 13,10,'$'
  1812.  
  1813. IncompatMess label byte
  1814.     db 'Incompatible versions.'
  1815.     db 13,10,'$'
  1816.  
  1817. NoUninstMess label byte
  1818.     db "Can't uninstall RemKey, try exiting AppMgr completely."
  1819.     db 13,10,'$'
  1820.  
  1821. NoInstMess label byte
  1822.     db "Can't install RemKey, try exiting AppMgr completely."
  1823.     db 13,10,'$'
  1824.  
  1825. ConfigMess label byte
  1826.     db 'Configuring '
  1827. ; Nothing between ConfigMess and OurName, please!
  1828. OurName db 128 dup (0)
  1829.  
  1830. WrtErrMess label byte
  1831.     db 'Error updating file.'
  1832.     db 13,10,'$'
  1833.  
  1834. if fileOpt
  1835. ReadErrMess label byte
  1836.     db 'Error reading file.'
  1837.     db 13,10,'$'
  1838.  
  1839. SendAbortMess label byte
  1840.     db 'File send terminated.'
  1841.     db 13,10,'$'
  1842. endif
  1843.  
  1844. UartBases dw com1, com2, com3, com4
  1845.  
  1846. FindMpxNum proc near
  1847.     assume ds:nothing,ss:nothing,es:nothing
  1848.  
  1849. ; Save all registers because the multiplex code we probe could change
  1850. ; any register.
  1851.  
  1852.     push    ax
  1853.     push    bx
  1854.     push    cx
  1855.     push    dx
  1856.     push    si
  1857.     push    di
  1858.     push    bp
  1859.     push    ds
  1860.     push    es
  1861.  
  1862. FindMpxLoop:
  1863.     mov    ah,ResidentMpxNum
  1864.     mov    al,0            ; MPX number in use?
  1865.     int    MpxInt
  1866.     cmp    al,0
  1867.     je    FreeMpxNum
  1868.     mov    ah,ResidentMpxNum
  1869.     mov    al,RemKeyMpxFn        ; in use by RemKey?
  1870.     int    MpxInt
  1871.     cmp    bx,"Re"
  1872.     jne    NextMpxNum
  1873.     cmp    cx,"mK"
  1874.     jne    NextMpxNum
  1875.     cmp    dx,"ey"
  1876.     je    FoundResident        ; exit if RemKey already present
  1877. NextMpxNum:
  1878.     inc    ResidentMpxNum
  1879.     jnz    FindMpxLoop        ; keep going until ID wraps to zero
  1880.     stc
  1881.     jmp    short ExitFindMpx
  1882.  
  1883. FreeMpxNum:
  1884.     mov    al,ResidentMpxNum
  1885.     mov    OurMpxNum,al
  1886. FoundResident:
  1887.     clc    
  1888.  
  1889. ExitFindMpx:
  1890.     pop    es
  1891.     pop    ds
  1892.     pop    bp
  1893.     pop    di
  1894.     pop    si
  1895.     pop    dx
  1896.     pop    cx
  1897.     pop    bx
  1898.     pop    ax
  1899.  
  1900.     ret    
  1901.  
  1902. FindMpxNum endp
  1903.  
  1904. ; Finds the name of the file that this program executed from and copies
  1905. ; it to "OurName".
  1906. GetOurName proc near
  1907.     assume ds:nothing,ss:nothing,es:nothing
  1908.     push    es
  1909.     push    ax
  1910.     push    cx
  1911.     push    di
  1912.     push    si
  1913.     mov    ax,cs:[2ch]        ; get the segment of our environment
  1914.     or    ax,ax            ; no environment if zero
  1915.     jz    @@Exit
  1916.     mov    es,ax
  1917.     mov    di,0            ; scan env from 0
  1918.     mov    cx,6000            ; give up after 6000 bytes of env
  1919. SkipEnv:
  1920.     cmp    word ptr es:[di],0    ; end of env is marked by the null
  1921.                     ; at the end of the last string 
  1922.                     ; followed by one more null
  1923.     je    FoundEnd
  1924.     inc    di
  1925.     loop    SkipEnv
  1926.     jmp    short @@Exit        ; env too large to be real, give up
  1927.  
  1928. FoundEnd:
  1929.     add    di,2            ; move past final pair of nulls
  1930.     cmp    word ptr es:[di],10    ; is string count absurd (> 10)?
  1931.     ja    @@Exit
  1932.     add    di,2            ; move past string count to first str
  1933.     lea    si,OurName        ; copy string to OurName
  1934.     mov    cx,128            ; only room for 127 chars plus null
  1935. CopyName:
  1936.     mov    al,es:[di]
  1937.     mov    cs:[si],al
  1938.     or    al,al
  1939.     jz    @@Exit            ; exit if we copied final null
  1940.     inc    di
  1941.     inc    si
  1942.     loop    CopyName
  1943.     mov    OurName,0        ; if we run out of room then the
  1944.                     ; name is partial and invalid.
  1945.                     ; Zap it.
  1946.  
  1947. @@Exit:
  1948.     pop    si
  1949.     pop    di
  1950.     pop    cx
  1951.     pop    ax
  1952.     pop    es
  1953.     ret
  1954. GetOurName endp
  1955.  
  1956. ; Returns with zero flag set if this is an HP 100LX.
  1957. ; Sets Is100LX
  1958. Chk100LX proc near
  1959.     assume ds:nothing,ss:nothing,es:nothing
  1960.     ret
  1961. Chk100LX endp
  1962.  
  1963. ; Display a null-terminated string (can contain a "$").
  1964. StrOut proc near
  1965.     assume ds:nothing,ss:nothing,es:nothing
  1966. StrLoop:
  1967.     mov    bx,dx
  1968.     cmp    byte ptr [bx],0
  1969.     je    @@Exit
  1970.     mov    ah,40h        ; write file
  1971.     mov    bx,1        ; StdOut
  1972.     mov    cx,1
  1973.     int    21h
  1974.     inc    dx
  1975.     jmp    StrLoop
  1976.  
  1977. @@Exit:
  1978.     ret
  1979. StrOut endp
  1980.  
  1981. ToUpper proc near
  1982.     assume ds:nothing,ss:nothing,es:nothing
  1983.     cmp    al,'a'
  1984.     jb    NotLowerCase
  1985.     cmp    al,'z'
  1986.     ja    NotLowerCase
  1987.     sub    al,'a'-'A'        ; convert to upper case
  1988. NotLowerCase:
  1989.     ret
  1990. ToUpper endp
  1991.  
  1992. ; Convert '1'..'4' to com1..com4 I/O base
  1993. SetCom proc near
  1994.     assume ds:nothing,ss:nothing,es:nothing
  1995.     push    si
  1996.     mov    ComNum,al        ; Save the ASCII of selected com port
  1997.     sub    al,'1'            ; '1'..'4' -> 0..3
  1998.     sub    ah,ah            ; byte to word
  1999.     add    ax,ax            ; double to index table of words
  2000.     mov    si,ax            ; move to an index register
  2001.     mov    ax,UartBases[si]    ; fetch UART base from table
  2002.     mov    UartBase,ax
  2003.     pop    si
  2004.     ret
  2005. SetCom endp
  2006.  
  2007. if fileOpt
  2008. ScanFileName proc near
  2009.     assume ds:nothing,ss:nothing,es:nothing
  2010.     mov    SendFileName,bx        ; save pointer to start of path
  2011. ScanLoop:
  2012.     mov    al,[bx]
  2013.     cmp    al,' '            ; space or slash ends name
  2014.     je    ScanExit
  2015.     cmp    al,'/'
  2016.     je    ScanExit
  2017.     inc    bx
  2018.     loop    ScanLoop
  2019. ScanExit:
  2020.     mov    FileNameEnd,bx        ; save pointer to end of path
  2021.     ret
  2022. ScanFileName endp
  2023. endif
  2024.  
  2025. ; Call with es:cx pointing to code previously hooked to interrupt vector,
  2026. ; al equal to interrupt number.
  2027. ;
  2028. ; Returns zero if that code is still hooked, non-zero if not.
  2029. CheckVector proc near
  2030.     push    bx
  2031.     push    dx
  2032.     push    es
  2033.     mov    dx,es            ; save segment of resident code in dx
  2034.     mov    ah,35h            ; read the interrupt vector to es:bx
  2035.     int    21h
  2036.     mov    ax,es
  2037.     cmp    ax,dx
  2038.     jne    VectorMismatch        ; exit with not-zero
  2039.     cmp    cx,bx            ; set zero or not-zero and exit
  2040. VectorMismatch:
  2041.     pop    es
  2042.     pop    dx
  2043.     pop    bx
  2044.     ret
  2045. CheckVector endp
  2046.  
  2047. ;=======================================================================
  2048. Main proc near
  2049.     assume ds:_TEXT,ss:_TEXT,es:_TEXT
  2050.     call    GetOurName
  2051.     mov    ah,9
  2052.     lea    dx,Logon
  2053.     int    21h
  2054.  
  2055. ; Test if we are already installed or choose a multiplex ID if we are not.
  2056.     call    FindMpxNum
  2057.     jnc    FoundID
  2058.     lea    dx,MpxErrMess
  2059.     jmp    ExitWithMess
  2060.  
  2061. FoundID:
  2062.     cmp    OurMpxNum,0
  2063.     jne    NotInstalled
  2064.  
  2065.     lea    dx,ResMess
  2066.     mov    ah,9
  2067.     int    21h
  2068.  
  2069.     mov    ah,ResidentMpxNum
  2070.     mov    al,RemKeyMpxFn
  2071.     int    MpxInt            ; ES gets segment of resident code
  2072.                     ; AX gets version of resident code
  2073.     cmp    ax,bcdVersion
  2074.     je    AlreadyInstalled
  2075. IncompatExit:
  2076.     lea    dx,IncompatMess
  2077.     jmp    ExitWithMess
  2078.  
  2079. NotInstalled:
  2080.     mov    ax,cs        ; make sure ES still points to our segment
  2081.     mov    es,ax
  2082. AlreadyInstalled:
  2083.     mov    ax,4dd4h        ; check machine type
  2084.     int    15h
  2085.     cmp    bx,"HP"            ; should return "H" in bh, "P" in bl
  2086.     jnz    Not100LX
  2087.     cmp    ch,1            ; ch=1 is palmtop family
  2088.     jnz    Not100LX
  2089.     cmp    cl,2            ; cl=1 is 95LX, 2 is 100LX
  2090.     je    On100LX
  2091.     lea    dx,Not100LXMess
  2092.     jmp    ExitWithMess
  2093.  
  2094. Not100LX:
  2095.     mov    Receive,0        ; non-100LX defaults to sending
  2096.     mov    EnableDef,0        ;   and not enabled
  2097.     jmp    short StartParse
  2098.  
  2099. On100LX:
  2100.     mov    Is100LX,1        ; set to true
  2101. StartParse:
  2102.     mov    dx,100h            ; keep 1 (true) in dh, 0 (false) in dl
  2103.     mov    bx,80h            ; point bx at start of string
  2104.     mov    cl,[bx]            ; get param string length
  2105.     inc    bx            ; move past length byte
  2106.     sub    ch,ch            ; convert to word
  2107.     jcxz    SetConfigX
  2108. ParseLoop:
  2109.     mov    al,[bx]            ; get a char
  2110.     cmp    al,' '            ; ignore blanks
  2111.     je    NextChar
  2112.     cmp    al,'/'            ; start of an option?
  2113.     jne    Help
  2114.     inc    bx            ; move through string
  2115.     loop    ParseLetter
  2116.     jmp    Help            ; can't close with a slash
  2117.  
  2118. ParseLetter:
  2119.     mov    al,[bx]            ; get an option letter (we hope)
  2120.     call    ToUpper
  2121.     cmp    al,'1'            ; comm port number ("1".."4")?
  2122.     jb    Not1234
  2123.     cmp    al,'4'
  2124.     ja    Not1234
  2125.     call    SetCom
  2126.     jmp    short NextChar
  2127.     
  2128. Not1234:
  2129. if fileOpt
  2130.     cmp    al,'F'            ; send a file?
  2131.     jne    NotSendFile
  2132.     inc    bx            ; move past option letter "F"
  2133.     loop    ScanName
  2134.     jmp    Help            ; something must follow "F"
  2135.  
  2136. ScanName:
  2137.     mov    Receive,dl        ; /F forces send mode (dl = 0)
  2138.     call    ScanFileName
  2139.     jcxz    SetConfigX        ; exit parse loop if no more chars
  2140.     jmp    short NextChar
  2141.  
  2142. NotSendFile:
  2143. endif
  2144.     cmp    al,'S'            ; act as sender?
  2145.     jne    NotSender
  2146.     mov    Receive,dl        ; dl = 0
  2147.     jmp    short NextChar
  2148.  
  2149. NotSender:
  2150.     cmp    al,'R'            ; act as receiver?
  2151.     jne    NotReceiver
  2152.     mov    Receive,dh        ; dh = 1
  2153.     mov    EnableDef,dh        ; receiver default is enabled (dh = 1)
  2154.     jmp    short NextChar
  2155.  
  2156. NotReceiver:
  2157.     cmp    al,'E'            ; enable?
  2158.     jne    NotEnable
  2159.     mov    Enabled,dh        ; dh = 1
  2160.     mov    UseEnDef,dl        ; do not default (dl = 0)
  2161.     jmp    short NextChar
  2162.  
  2163. SetConfigX:
  2164.     jmp    short SetConfig
  2165.  
  2166. ParseLoopX:
  2167.     jmp    ParseLoop
  2168.  
  2169. NotEnable:
  2170.     cmp    al,'D'            ; disable?
  2171.     jne    NotDisable
  2172.     mov    Enabled,dl        ; dl = 0)
  2173.     mov    UseEnDef,dl        ; do not default (dl = 0)
  2174.     jmp    short NextChar
  2175.  
  2176. NotDisable:
  2177.     cmp    al,'T'            ; Sender installs as a TSR ?
  2178.     jne    NotSendTSR
  2179.     mov    SendTSR,dh        ; dh = 1
  2180.     mov    Receive,dl        ; TSR implies Sender (dl = 0)
  2181.     mov    EnableDef,dl        ; Send TSR default is disabled (dl = 0)
  2182.     jmp    short NextChar
  2183.  
  2184. NotSendTSR:
  2185.     cmp    al,'Z'            ; enable 40*25 zoom?
  2186.     jne    NotZoomOpt
  2187.     mov    OptZa,82h        ; modify linked-lists to include
  2188.     mov    OptZb,83h        ;   40*25 zoom state
  2189.     jmp    short NextChar
  2190.  
  2191. NotZoomOpt:
  2192.     cmp    al,'U'            ; uninstall?
  2193.     je    Uninstall
  2194.     cmp    al,'C'            ; Configure new defaults?
  2195.     jne    NotConfig
  2196.     mov    DoConfig,dh        ; dh = 1
  2197.     jmp    short NextChar
  2198.  
  2199. NotConfig:
  2200.     jmp    Help            ; unrecognized chars trigger help
  2201.  
  2202. NextChar:
  2203.     inc    bx            ; move through string
  2204.     loop    ParseLoopX
  2205. SetConfig:
  2206.     cmp    DoConfig,0
  2207.     jnz    ConfigDefs
  2208.     cmp    UseEnDef,0        ; use default?
  2209.     jz    NoDefault
  2210.     mov    al,EnableDef
  2211.     mov    Enabled,al
  2212. NoDefault:
  2213.     cmp    Enabled,0        ; enable now?
  2214.     je    NotEnabled
  2215. if 1
  2216.     call    ForceUART        ; force UART to our config
  2217. else
  2218.     cmp    Is100LX,0
  2219.     jz    NoInitialPower
  2220.     mov    ax,4900h        ; route serial port to wire
  2221.     int    15h
  2222.     mov    ax,4a01h        ; turn serial port on
  2223.     int    15h
  2224. NoInitialPower:
  2225.     call    InitUart        ; force UART to our config
  2226. endif
  2227. NotEnabled:
  2228.     mov    ah,9            ; display com port
  2229.     lea    dx,ComMess
  2230.     int    21h
  2231.  
  2232.     mov    ax,es            ; is another copy already loaded?
  2233.     mov    bx,cs
  2234.     cmp    ax,bx
  2235.     je    JustUs
  2236.     cmp    es:version,bcdVersion
  2237.     jne    IncompatExit
  2238.     mov    ax,UartBase        ; copy parameters
  2239.     mov    es:UartBase,ax
  2240.     mov    al,Enabled
  2241.     cmp    al,es:Enabled
  2242.     je    DoneEnChg        ; nothing to do if they are the same
  2243.     mov    es:Enabled,al        ; update resident enable flag
  2244.     or    al,al            ; change to enabled?
  2245.     jz    ChgToDisabled
  2246.     call    SpeedUp
  2247.     jmp    short DoneEnChg
  2248.  
  2249. ChgToDisabled:
  2250.     call    SlowDown
  2251. DoneEnChg:
  2252.     lea    dx,UpdateMess
  2253.     jmp    ExitWithMess
  2254.  
  2255. JustUs:
  2256.     mov    ax,3516h          ; Get keyboard interrupt vector
  2257.     int    21h
  2258.     assume es:nothing
  2259.  
  2260.     mov    OldKeyOff,bx
  2261.     mov    OldKeySeg,es
  2262.  
  2263.     cmp    Receive,0        ; send or receive mode?
  2264.     jz    Sender
  2265.  
  2266. Receiver:
  2267.     mov    NewKey,offset NewKeyRecv
  2268.     lea    dx,RecvEnd+15        ; +15 to round to start of next segment
  2269. TsrExit:
  2270.     cmp    Is100LX,0
  2271.     jz    NoSysMgr
  2272.     mov    ax,5101h        ; read mailbox word one
  2273.     int    15h
  2274.     jc    NoSysMgr        ; if carry no SysMgr
  2275.     cmp    ax,7072h        ; mailbox signature of SysMgr
  2276.     jne    NoSysMgr
  2277.     lea    dx,NoInstMess
  2278.     jmp    ExitWithMess
  2279.  
  2280. NoSysMgr:
  2281.     push    dx            ; save end of resident code
  2282.     mov    es,cs:[2Ch]        ; Grab our environment segment
  2283.                     ; from the PSP
  2284.     mov    ah,49h
  2285.     int    21h            ; Free that darn environment
  2286.  
  2287.     lea    dx,Installed        ; Print installed message
  2288.     mov    ah,9
  2289.     int    21h
  2290.  
  2291.     cmp    SendTSR,0
  2292.     jne    NoHookTick
  2293.     mov    ax,3508h          ; Get Tick -- we hook this to poll UART
  2294.     int    21h
  2295.  
  2296.     mov    OldTickOff,bx
  2297.     mov    OldTickSeg,es
  2298.  
  2299.     mov    ax,cs
  2300.     mov    ds,ax
  2301.  
  2302.     lea    dx,NewTick
  2303.     mov    ax,2508h
  2304.     int    21h
  2305.     cmp    Enabled,0        ; enabled?
  2306.     je    NoSpeedUp
  2307.     call    SpeedUp            ; reprogram systick timer for new rate
  2308. NoSpeedUp:
  2309. NoHookTick:
  2310.  
  2311.     call    HookKey            ; hook into the keyboard BIOS int
  2312.  
  2313.     mov    ax,352fh          ; link into the multiplex int chain
  2314.     int    21h
  2315.  
  2316.     mov    OldMpxOff,bx
  2317.     mov    OldMpxSeg,es
  2318.  
  2319.     mov    ax,cs
  2320.     mov    ds,ax
  2321.  
  2322.     lea    dx,NewMpx
  2323.     mov    ax,252fh
  2324.     int    21h
  2325.  
  2326.  
  2327. ; Calculate ending segment of TSR
  2328.     pop    dx            ; recover end of resident code
  2329.     shr    dx,1            ; convert offset to segment,
  2330.     shr    dx,1            ;  (divide by 16)
  2331.     shr    dx,1
  2332.     shr    dx,1
  2333.     mov    ah,31h        ; Terminate and stay resident
  2334.     int    21h
  2335.  
  2336. Help:
  2337.     lea    dx,HelpMess
  2338. ExitWithMess:
  2339.     mov    ah,9
  2340.     int    21h
  2341.     jmp    Exit
  2342.  
  2343. Sender:
  2344. if fileOpt
  2345.     mov    dx,SendFileName    ; anything to send?
  2346.     or    dx,dx
  2347.     jnz    SendFile
  2348. endif
  2349.     lea    dx,SendHelp
  2350.     call    StrOut
  2351.     cmp    SendTSR,0
  2352.     je    SendAsProg
  2353.     lea    dx,SendEnd+15    ; +15 to round to start of next segment
  2354.     jmp    TsrExit
  2355.  
  2356. SendAsProg:
  2357.     call    SendKeys
  2358. Exit:
  2359.     mov    ax,4c00h
  2360.     int    21h
  2361.  
  2362. if fileOpt
  2363. SendFile:
  2364.     mov    bx,FileNameEnd        ; get pointer to end of path
  2365.     mov    byte ptr [bx],0        ; terminate path with a null
  2366.     mov    ax,3d00h        ; open code file for read
  2367.     int    21h
  2368.     jc    ReadError
  2369.     mov    bx,ax            ; save handle in bx
  2370. SendFileLoop:
  2371.     mov    ah,3fh            ; read the file in
  2372.     lea    dx,FileBuf
  2373.     mov    cx,1            ; ask for more one byte
  2374.     int    21h
  2375.     jc    ReadError
  2376.     cmp    ax,1            ; did we get our one byte?
  2377.     jne    ReadDone        ; jump if EOF
  2378.     mov    al,FileBuf        ; get the char
  2379.     cmp    al,0ah            ; linefeed?
  2380.     je    SendFileLoop
  2381.     cmp    al,1ah            ; EOF?
  2382.     je    ReadDone
  2383.     mov    ah,al            ; save ASCII portion in ah
  2384.     push    bx            ; save handle
  2385.     lea    bx,AscToScan        ; recreate scan code for ASCII value
  2386.     xlat    AscToScan
  2387.     pop    bx
  2388.     xchg    al,ah            ; scancode in ah, ASCII in al
  2389.     call    SendWord
  2390.     mov    ah,0bh            ; check for key press
  2391.     int    21h
  2392.     cmp    al,0ffh
  2393.     je    AbortSend
  2394. ; Just a kludge for our test, this produced about 11 cps on a 50MHz 486DX
  2395.     mov    cx,0
  2396.     mov    ax,4
  2397. SendDelay:
  2398.     jmp    $+2
  2399.     jmp    $+2
  2400.     loop    SendDelay
  2401.     dec    ax
  2402.     jnz    SendDelay
  2403.     jmp    SendFileLoop
  2404.  
  2405. AbortSend:
  2406.     mov    ah,8            ; eat the abort key
  2407.     int    21h
  2408.     or    al,al            ; extended char
  2409.     jnz    NotExtended
  2410.     mov    ah,8            ; grab the second byte
  2411.     int    21h
  2412. NotExtended:
  2413.     lea    dx,SendAbortMess
  2414.     jmp    short ReadMess
  2415.  
  2416. ReadError:
  2417.     lea    dx,ReadErrMess
  2418. ReadMess:
  2419.     mov    ah,9
  2420.     int    21h
  2421. ReadDone:
  2422.     mov    ax,3e00h        ; close the file
  2423.     int    21h
  2424.     jmp    short Exit
  2425. endif
  2426.  
  2427. Uninstall:
  2428. ; Remember: ES is pointing to our old code seg
  2429.     mov    ax,cs
  2430.     mov    dx,es
  2431.     cmp    ax,dx
  2432.     jne    Uninst
  2433.     lea    dx,NotInstMess
  2434.     mov    ah,9
  2435.     int    21h
  2436. if fileOpt
  2437.     jmp    Exit
  2438. else
  2439.     jmp    short Exit
  2440. endif
  2441.  
  2442. Uninst:
  2443. ; Do all the redirected interrupt vectors still point to our code?
  2444.  
  2445.     mov    al,16h
  2446.     mov    cx,es:NewKey        ; fetch which NewKeyXXX
  2447.                     ; the resident code is using
  2448.     call    CheckVector
  2449.     jnz    CanNotUninst
  2450.  
  2451.     mov    al,2fh
  2452.     lea    cx,NewMpx
  2453.     call    CheckVector
  2454.     jnz    CanNotUninst
  2455.  
  2456.     cmp    es:SendTSR,0
  2457.     jne    DoNotCheckTick
  2458.  
  2459.     mov    al,8
  2460.     lea    cx,NewTick
  2461.     call    CheckVector
  2462.     jnz    CanNotUninst
  2463.  
  2464. DoNotCheckTick:
  2465.  
  2466.     push    ds            ; save our ds
  2467.  
  2468.     mov    dx,es:OldKeyOff        ; First restore the old key vector
  2469.     mov    ds,es:OldKeySeg        ; by grabbing it out of resident CS
  2470.     mov    ax,2516h
  2471.     int    21h
  2472.  
  2473.     mov    dx,es:OldMpxOff        ; Restore the old multiplex vector
  2474.     mov    ds,es:OldMpxSeg        ; by grabbing it out of resident CS
  2475.     mov    ax,252fh
  2476.     int    21h
  2477.  
  2478.     pop    ds
  2479.  
  2480.     cmp    es:SendTSR,0
  2481.     jne    NoFreeTick
  2482.  
  2483.     push    ds            ; save our ds
  2484.  
  2485.     mov    dx,es:OldTickOff    ; restore the old Tick vector
  2486.     mov    ds,es:OldTickSeg    ; by grabbing it out of resident CS
  2487.     mov    ax,2508h
  2488.     int    21h
  2489.  
  2490.     pop    ds
  2491.  
  2492. ; restore normal systick rate, div = 65536 (0)
  2493.     call    SlowDown        
  2494.  
  2495. NoFreeTick:
  2496. ; es is our old code segment -- that's the segment we free
  2497.     mov    ah,49h
  2498.     int    21h
  2499.  
  2500.     lea    dx,UninstMess
  2501.     jmp    ExitWithMess
  2502.  
  2503. CanNotUninst:
  2504.     lea    dx,NoUninstMess
  2505.     jmp    ExitWithMess
  2506.  
  2507. ConfigDefs:
  2508.     mov    ah,9            ; display com port
  2509.     lea    dx,ComMess
  2510.     int    21h
  2511.  
  2512.     lea    dx,ConfigMess
  2513.     call    StrOut
  2514.     lea    dx,CrLf
  2515.     mov    ah,9
  2516.     int    21h
  2517.  
  2518.     mov    ax,3d02h        ; open code file for R/W
  2519.     lea    dx,OurName
  2520.     int    21h
  2521.     jc    WriteError
  2522.     mov    bx,ax            ; save handle in bx
  2523.     mov    ax,3f00h        ; read the file in
  2524.     lea    dx,FileBuf
  2525.     mov    cx,8000h        ; ask for more than there is
  2526.     int    21h
  2527.     jc    WriteError
  2528.     cmp    ax,CodeLength
  2529.     jne    WriteError
  2530.     mov    si,FileBuf-Begin
  2531.     cmp    version[si],bcdVersion
  2532.     jne    IncompatExit
  2533.     mov    al,ComNum        ; Save the ASCII of selected com port
  2534.     mov    ComNum[si],al
  2535.     mov    HelpCommDef[si],al
  2536.     mov    ax,UartBase        ; Save the actual base address
  2537.     mov    UartBase[si],ax
  2538.     mov    ax,4200h        ; seek relative to start
  2539.     sub    cx,cx            ; offset is zero
  2540.     mov    dx,cx
  2541.     int    21h
  2542.     jc    WriteError
  2543.     mov    ax,4000h        ; write the code back
  2544.     lea    dx,FileBuf
  2545.     mov    cx,CodeLength
  2546.     int    21h
  2547.     jc    WriteError
  2548.     mov    ax,3e00h        ; close the file
  2549.     int    21h
  2550.     jmp    Exit
  2551.  
  2552. WriteError:
  2553.     lea    dx,WrtErrMess
  2554.     jmp    ExitWithMess
  2555.  
  2556. Main endp
  2557.  
  2558. CodeLength equ $-100h
  2559.  
  2560. FileBuf label byte
  2561.  
  2562.     end Begin
  2563.