home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / KEYBOARD / STUFF310.ZIP / STUFFIT.ASM next >
Encoding:
Assembly Source File  |  1991-04-21  |  47.9 KB  |  1,997 lines

  1.    PAGE 80,132
  2.    TITLE "StuffIt, Delayed keyboard stuffer. (C) Terje Mathisen 1989-91"
  3.  
  4. Version EQU 310h
  5. VerStr  EQU '3.10ß'
  6.  
  7. ;       LOCALS                  ; Use TASM features for easier development.
  8. ;       NOJUMPS
  9.  
  10. TicksPrDay      EQU 1573041
  11. TicksPrHour     EQU 65543
  12.  
  13. BIOS SEGMENT AT 40h
  14.         ORG 1Ah
  15. BufferHead      dw ?
  16. BufferTail      dw ?
  17. BufferStart     dw 16 dup (?)
  18. BufferEnd       LABEL word
  19.  
  20.         ORG 49h
  21. VideoMode       db ?
  22. CrtWidth        dw ?
  23.  
  24.         ORG 4Eh
  25. CurrStart       dw ?
  26. Cursor          dw ?
  27.  
  28.         ORG 6Ch
  29. BIOS_Timer      dw 2 dup (?)
  30. BIOS ENDS
  31.  
  32. BOOT SEGMENT AT 0F000h
  33.         ORG 0FFF0h
  34. RebootLocation  LABEL FAR
  35. BOOT ENDS
  36.  
  37. ; To reduce the resident size of StuffIt, the script is compressed into tokens
  38. ; using the following algorithm:
  39. ;
  40. ; 0 means that this is a character code which will be followed by a scan code
  41. ;
  42. ; 224 means the same as 0, (224 is lead-in code for the cursor keypad on the
  43. ; enhanced keyboard.)
  44. ;
  45. ; 254 is an escape character, followed by a char:scan pair. This is also used
  46. ; if we need to enter Chr(254) or Chr(255)
  47. ;
  48. ; 255 is the lead-in for an extended function code. It will be followed by
  49. ; one of these codes:
  50. ;
  51. ; Codes for extended functions, i.e not normal keys:
  52.  
  53. REBOOT_CODE     = 0
  54. ATTIME_CODE     = 1
  55. DELTATIME_CODE  = 2
  56. FIND_CODE       = 3
  57. PROMPT_CODE     = 4
  58. PRTSCRN_CODE    = 5
  59. BREAK_CODE      = 6
  60.  
  61. ; Use 255 to signal that the following is an extended function
  62.  
  63. EXTENDED_CODE   = 255
  64.  
  65. ; Use 254 as lead-in for keys that need both char & scan.
  66.  
  67. GETWORD_CODE    = 254
  68.  
  69. ; All other codes (1-221,223-253) are presumed to be single characters to
  70. ; place in the kbd buffer. All of them will receive the same scan code (2).
  71. ; If your application cannot accept this, you must use the {c} or [c] syntax,
  72. ; where <c> is any character. This will be translated to the US std enh kbd
  73. ; char:scan pair.
  74.  
  75.  
  76. CODE SEGMENT PARA PUBLIC 'code'
  77.         ASSUME CS:CODE,DS:NOTHING,ES:NOTHING
  78.         ORG 0
  79. PspStart label byte
  80.  
  81.         ORG 5Ch
  82. ResidentSize    dw ?
  83.  
  84. ;LowStart label byte
  85.  
  86.         ORG 80h
  87. CommandLen      db ?
  88. CommandLine     LABEL BYTE
  89.  
  90.         ORG 100h
  91. start:
  92.         jmp init
  93.  
  94. Semafor equ 'ST'                        ; Short for STuffit
  95.  
  96. LowStart label byte
  97. HighStart label byte
  98.  
  99. MoveDown EQU HighStart - LowStart       ; Relocation factor for resident
  100.                                         ; part of StuffIt
  101.  
  102. Int2F   proc far
  103.         cmp ax,0E000h
  104.          je @@maybe
  105.  
  106. @@chain:
  107. ;       jmp [OldInt2F]
  108.         db 0EAh
  109. OldInt2F dd ?
  110.  
  111. @@maybe:
  112.         cmp dx,Semafor                  ; Be safe, insist on semafor in DX
  113.          jne @@chain
  114.  
  115. @@We_Are_Here:
  116.         mov al,0FFh
  117.         mov dx,cs
  118.         mov bx,Version
  119.  
  120.         iret
  121.  
  122. Int2F   endp
  123.  
  124.  
  125. ; Here comes the actual, INT 8, code, which will run on every timer tick to
  126. ; execute the tokenized script.
  127. ;
  128. ; To reduce the performance overhead of having StuffIt loaded, I use a dirty
  129. ; trick: Self-modifying code.
  130. ;
  131. ; When the script has finished, the total overhead is reduced to just
  132. ; 3 instructions: PUSHF / CALL (FAR IMMEDIATE) OldTimer / IRET
  133.  
  134. PUSH_AX_OPCODE EQU  50h                 ; Opcode for PUSH AX, used when active
  135. IRET_OPCODE    EQU 0CFh                 ; Opcode for IRET, used when disabled
  136.  
  137. MyTimer PROC FAR
  138.         pushf
  139. ;       call [OldTimer]                 ; Call the old timer code first, to
  140.         db 09Ah                         ; do it's stuff and re-enable the HW.
  141. OldTimer dw ?,?
  142.  
  143. SelfModify label byte                   ; This will be IRET when idle
  144.         push ax                         ; PUSH AX = 50h, IRET = 0CFh
  145.  
  146. ; The [active] flag is initialized to -1. This way I can use an INC
  147. ; instruction to detect the first entry into this code. Multiple
  148. ; invocations will jump directly to the exit code, with very little
  149. ; overhead. (A total of only 7 instructions and 1 short jump.)
  150.  
  151.         inc byte ptr [cs:active-MoveDown] ;  INC from -1 to 0
  152.          jnz Already_Active
  153.  
  154.         STI
  155.         CLD
  156.  
  157.         push bx
  158.         push cx
  159.  
  160.         push dx
  161.         push si
  162.         push di
  163.         push ds
  164.         push es
  165.  
  166.         push cs
  167.         pop ds
  168.         ASSUME DS:CODE
  169.  
  170.         mov es,[BiosSeg-MoveDown]       ; I store 40h in a memory variable and
  171.                                         ; load ES from it, as this saves one
  172.                                         ; instruction vs MOV AX,40/MOV ES,AX
  173.         ASSUME ES:BIOS
  174.  
  175.         call word ptr [StuffMode-MoveDown] ; State machine, call the current
  176.                                         ; state handler.
  177.  
  178.         pop es
  179.         pop ds
  180.         pop di
  181.         pop si
  182.         pop dx
  183.  
  184.         pop cx
  185.         pop bx
  186.  
  187.         ASSUME CS:CODE,DS:NOTHING,ES:NOTHING
  188.  
  189.         CLI
  190.  
  191. Already_Active:
  192.  
  193.         dec byte ptr [cs:active-MoveDown]
  194.  
  195.         pop ax
  196.         iret
  197. MyTimer ENDP
  198.  
  199.         ASSUME CS:CODE,DS:CODE,ES:BIOS
  200.  
  201. StuffFinished:
  202.         mov [SelfModify-MoveDown],IRET_OPCODE   ; Disable by self-modifying
  203.                                         ; May be re-enabled by a later
  204.                                         ; invocation of StuffIt.
  205.         ret
  206.  
  207. NextKey proc near
  208.         mov si,[StuffPtr-MoveDown]
  209. GetNext:
  210.         cmp si,[StuffEnd-MoveDown]
  211.          jae StuffFinished
  212.  
  213.         lodsb
  214.         cmp al,GETWORD_CODE
  215.          ja Extended                    ; Extended function
  216.          je @@GetBoth                   ; 254 => char, scan follows
  217.  
  218.         mov ah,2                        ; Simulate scan = 2 for normal chars
  219.         cmp al,224                      ; Character for Enh.Kbd new keys
  220.          je @@GetScan
  221.  
  222.         or al,al
  223.          jne stuff
  224.  
  225. @@GetScan:
  226.         mov ah,[si]
  227.         inc si
  228.          jmp short stuff
  229.  
  230. @@GetBoth:
  231.         lodsw
  232. Stuff:
  233.         CLI
  234.         mov di,[BufferTail]
  235.         stos word ptr [BIOS:di]
  236.         cmp di, OFFSET BufferEnd
  237.          jb @@1
  238.         mov di, OFFSET BufferStart
  239. @@1:
  240.         cmp di,[BufferHead]
  241.          je @@Overflow
  242.         mov [BufferTail],di
  243.         STI
  244. StuffOK:
  245.         mov [StuffPtr-MoveDown],si
  246.          jmp GetNext
  247.  
  248. @@OverFlow:
  249.         STI
  250.         ret
  251. NextKey ENDP
  252.  
  253. ExtendedTable label word
  254.   dw Reboot      - MoveDown
  255.   dw AbsTime     - MoveDown
  256.   dw DeltaTime   - MoveDown
  257.   dw StartFind   - MoveDown
  258.   dw StartPrompt - MoveDown
  259.   dw PrtScrn     - MoveDown
  260.   dw CtrlBreak   - MoveDown
  261.  
  262. NrOfCodes = ($ - offset ExtendedTable) SHR 1
  263.  
  264. Extended PROC near
  265.         lodsb                           ; Get function code!
  266.         cmp al,NrOfCodes
  267.          jae StuffFinished              ; Program Error! Abort the script!
  268.  
  269.         cbw                             ; All codes are <= 127, so CBW is OK!
  270.         mov bx,ax
  271.         shl bx,1
  272.         jmp ExtendedTable[bx - MoveDown] ; Jump to function handler
  273.  
  274. Extended endp
  275.  
  276. Reboot proc near
  277.         mov word ptr [BIOS: 72h],1234h  ; == Warm Boot (CtrlAltDel)
  278.         jmp RebootLocation              ; == F000:FFF0
  279. Reboot endp
  280.  
  281. PrtScrn proc near
  282.         int     5
  283.         jmp     GetNext
  284. PrtScrn endp
  285.  
  286. CtrlBreak proc near
  287.         int     1Bh
  288.         xor     ax,ax
  289.         jmp     Stuff
  290. CtrlBreak endp
  291.  
  292. AbsTime:                                ; Both Abs time & Delta time land here
  293. DeltaTime:                              ; ---- " ----
  294.  
  295. GetTime proc near
  296.  
  297.         cmp al, DELTATIME_CODE
  298.         lodsw                           ; Next 3 bytes is # of ticks
  299.         mov dl,[si]
  300.          jz @@TimeOk                    ; Delta time, so wait # of ticks
  301.  
  302. ; Wait until time equal: Calculate remaining ticks
  303.  
  304.         sub ax,[Bios_Timer]
  305.         sbb dl,BYTE PTR [Bios_Timer+2]
  306.          jae @@TimeOk
  307.  
  308.         add ax,TicksPrDay AND 0FFFFh
  309.         adc dl,TicksPrDay Shr 16
  310.  
  311. @@TimeOK:
  312.         inc si
  313.         mov [StuffPtr-MoveDown],si               ; Point to next byte
  314.  
  315.         sub dh,dh                       ; Fill top of DX with 0
  316.         or ax,dx
  317.          jz @@WaitZero                  ; Special case, wait for empty kbd
  318.  
  319.         mov [CountLow-MoveDown],ax
  320.         mov [CountHigh-MoveDown],dl
  321.         mov [StuffMode-MoveDown], OFFSET CountDown - MoveDown
  322.         ret
  323.  
  324. @@WaitZero:
  325.         mov [StuffMode-MoveDown], OFFSET WaitEmpty - Movedown
  326.         ret
  327. GetTime endp
  328.  
  329. CountDown proc near
  330.         DEC [CountLow-MoveDown]
  331.          jnz NoChange
  332.         SUB [CountHigh-MoveDown],1
  333.          jae NoChange
  334.  
  335. StartNextKey:
  336.         mov [StuffMode-MoveDown], OFFSET NextKey - MoveDown
  337. NoChange:
  338.         ret
  339. CountDown endp
  340.  
  341. WaitEmpty proc near
  342.         mov ax,[BufferHead]
  343.         cmp ax,[BufferTail]
  344.          je StartNextKey
  345.  
  346.         ret
  347. WaitEmpty endp
  348.  
  349. StartPrompt:
  350.  
  351.         mov [StuffMode-MoveDown], offset ScanPrompt - MoveDown
  352.         mov [StuffPtr-MoveDown],si
  353.  
  354. ; si -> dx:BYTE, dy:BYTE, count:WORD, len:BYTE, attr:BYTE, st:BYTE * len
  355.  
  356. ScanPrompt:                             ; Find start posn'n
  357.  
  358.         mov si,[StuffPtr-MoveDown]
  359.  
  360.         lodsw                           ; AL = dx, AH = dy
  361.         mov cx,[Cursor]                 ; CL = X,  CH = y
  362.         sub cl,al
  363.          jae @@1
  364.         sub cl,cl
  365. @@1:
  366.         sub ch,ah
  367.          jae @@2
  368.         sub ch,ch
  369. @@2:
  370.         mov al,byte ptr [CrtWidth]
  371.         mul ch                          ; AX = Y-offset
  372.         sub ch,ch
  373.         add ax,cx
  374.         shl ax,1                        ; AX = Start of scan
  375.  
  376.          jmp short Scan1
  377.  
  378. StartFind:
  379.         mov [StuffMode-MoveDown], offset ScanText - MoveDown
  380.         mov [StuffPtr-MoveDown],si
  381.  
  382. ; si -> start:WORD, count:WORD, len:BYTE, attr:BYTE, st:BYTE * len
  383.  
  384. ScanText proc near
  385.  
  386.         mov si,[StuffPtr-MoveDown]
  387.         lodsw                           ; starting offset in screen
  388.  
  389. Scan1:
  390.         mov di,ax
  391.         lodsw
  392.         mov cx, ax                      ; # of char cells to search
  393.  
  394.         lodsw                           ; AL = len, AH = attr
  395.         mov dl,al
  396.         xor dh,dh                       ; Text len
  397.  
  398.         dec dx                          ; Skip first char in length
  399.  
  400.         mov bx, 0B000h
  401.         cmp [VideoMode],7
  402.          je @@1
  403.         cmp [VideoMode],3
  404.          ja @@done
  405.         mov bh,0B8h
  406. @@1:
  407.         mov es,bx                       ; ES -> video segment
  408. ASSUME ES:NOTHING
  409.  
  410.         inc si                          ; Skip first char
  411.         cmp ah,255                      ; ATTR = 255 -> no attr
  412.          je @@FindChar
  413.  
  414. @@FindCharAttr:
  415.         mov al,[si-1]                   ; First char to match
  416.   repne scasw
  417.          jne @@done
  418.         or dx,dx                        ; Remaining length = 0
  419.          jz @@found
  420.  
  421.         push cx
  422.         push si
  423.         push di
  424.         mov cx,dx
  425. @@l2:
  426.         lodsb
  427.         scasw
  428.          loope @@l2
  429.  
  430.         pop di
  431.         pop si
  432.         pop cx
  433.          je @@found                     ; Yes, all chars match!
  434.          jcxz @@done                    ; No more room!
  435.          jne @@FindCharAttr             ;  this BUG was found by rbabcock!
  436.  
  437. @@FindChar:
  438.         mov al,[si-1]
  439.         dec di
  440. @@l3:
  441.          jcxz @@done                    ; No more room to start in!
  442. @@l31:
  443.         inc di
  444.         scasb
  445.          loopne @@l31
  446.  
  447.          jne @@done
  448.  
  449.         or dx,dx
  450.          jz @@found
  451.  
  452.         push cx
  453.         push si
  454.         push di
  455.         mov cx,dx
  456. @@l4:
  457.         inc di
  458.         cmpsb
  459.          loope @@l4
  460.  
  461.         pop di
  462.         pop si
  463.         pop cx
  464.          jne @@l3
  465.  
  466. @@found:
  467.         add si,dx                       ; Point after text to search for
  468.         mov [StuffMode-MoveDown], offset NextKey - MoveDown
  469.         mov [StuffPtr-MoveDown],si
  470. @@done:
  471.         ret
  472. ScanText Endp
  473.  
  474.         ALIGN 2
  475.  
  476. BiosSeg         dw 40h
  477.  
  478. FirstByteToCopy label byte
  479.  
  480. StuffPtr        dw OFFSET StuffBuffer - MoveDown
  481. StuffEnd        dw ?
  482. StuffMode       dw OFFSET NextKey - MoveDown
  483.  
  484. CountLow        dw ?
  485. CountHigh       db ?                    ; Use 24 bits for tick counter
  486.  
  487. active          db -1
  488.  
  489. ResidentEnd     EQU $
  490.  
  491. StuffBuffer     LABEL byte
  492.  
  493. StartMsg db 'StuffIt V',VerStr,' (C) Terje Mathisen 1989-91',13,10,'$'
  494.  
  495. SyntaxMsg label byte
  496.  
  497. db 'Syntax: Stuffit <commands>',13,10
  498. db '  +|=[[hh:]mm:]ss     | Delay for(+) or until(=) a specified time.',13,10
  499. db '                         +45 will wait for 45 seconds.',13,10
  500. db '                         =14:: will wait until 2pm.',13,10
  501. db '                         +0 will wait until the kbd buffer is empty.',13,10
  502. db '  <character code>    | Stuff a given character code.',13,10
  503. db '                         27 = <Esc>, 13 = <CR> etc.',13,10
  504. db '  @<scan code>        | Stuff a given scan code (char=0).',13,10
  505. db '                         @68 = F10, @73 = PgUp etc.',13,10
  506. db '  <char>:<scan>       | Specify both character and scan.',13,10
  507. db '                         43:74 = <Num+>',13,10
  508. db "  'TEXT'",' or "TEXT"    | Stuff all the characters in TEXT',13,10
  509. db '  F<x>,<y>,<n>[,attr],"STRING" | Find "STRING" in an area starting at (X,Y),',13,10
  510. db "  (or {Find}X,Y etc)     and beeing (N) char's long.",13,10
  511. db '                         Ignore text attributes, unless <attr> is specified.',13,10
  512. db '  P<dx>,<dy>,<n>[,attr],"STRING" | Find Prompt "STRING", starting at',13,10
  513. db "  (or {Prompt}           CursorX-DX,CursorY-DY. OBS Negative values!",13,10
  514. db '  !                   | Reboot. (=0 ! will reboot at midnight.)',13,10
  515. db '  /F:FileName         | Read commands from <FileName>.',13,10
  516. db '  /B:nnnn             | Allocate room for <nnnn> bytes in TSR(512 default).',13,10
  517. db '  /R                  | Remove (Unload) StuffIt from RAM.',13,10
  518. db ' {KeyName}or[KeyName] | Stuff <KeyName>. ({F1},{^Left},{Home} etc.)',13,10
  519. db '  /L                  | List all mnemonic key names.',13,10
  520. db '$'
  521.  
  522. ErrorMsg1 db 'SYNTAX ERROR ON LINE $'
  523.  
  524. ErrorMsg2 label byte
  525. CRLF db 13,10,'$'
  526.  
  527. UpdateMsg       db 'Resident copy updated!',13,10,'$'
  528.  
  529. StayResMsg      db 'Resident code loaded!',13,10,'$'
  530.  
  531. RemovedMsg      db 'Resident code removed from RAM!',13,10,'$'
  532.  
  533. WrongVerMsg db 'A different version of StuffIt is already resident!',13,10,'$'
  534.  
  535. NotRemovedMsg  label byte
  536.  db 'Resident code cannot be removed, as another '
  537.  db   'program is using the',13,10
  538.  db 'Timer (Int 8) and/or the Multiplex (Int 2F) '
  539.  db   'vector. Please remove all',13,10
  540.  db 'programs loaded after StuffIt, '
  541.  db   'and retry the operation.',13,10,'$'
  542.  
  543. RamErrMsg label byte
  544.  db 'Not enough RAM! (Need at least 128 kB to initialize program.)',13,10,'$'
  545.  
  546. FileErrMsg label byte
  547.  db 'Error reading input file!',13,10,'$'
  548.  
  549. ResidentToSmallMsg label byte
  550.  db 'Resident buffer to small! Try to remove it (/R) and reload.',13,10,'$'
  551.  
  552. ;FirstBlock      dw ?
  553. ;SecondBlock     dw ?
  554.  
  555.         JUMPS        ; Allow inefficient code in transient part of code!
  556.                      ; This makes it more readable.
  557.  
  558. Init proc near
  559. ASSUME DS:CODE, ES:CODE
  560.  
  561.         mov     ax,OFFSET StuffBuffer - MoveDown + 512 + 15
  562.         and     ax,0FFF0h
  563.         mov     [ResidentSize], ax
  564.  
  565. ; Start by relocating the program into a second segment
  566.  
  567. ;        mov     [FirstBlock],CS         ; Save segment addr
  568.  
  569.         mov     ah,4Ah
  570.         mov     bx,2000h                ; Realloc to 128 kB
  571. ;       mov     es,[FirstBlock]
  572.         int     21h
  573.         mov     dx, offset RamErrMsg
  574.          jc     ErrorMessage
  575.  
  576. ;        mov     ah,48h
  577. ;        mov     bx,1000h
  578. ;        int     21h                     ; Alloc second 64kB block
  579. ;        mov     dx, offset RamErrMsg
  580. ;         jc     ErrorMessage
  581.  
  582.         mov     ax,cs
  583.         add     ax,1000h                ; Point after 1st 64kB
  584.  
  585.         mov     es,ax
  586. ;        mov     [SecondBlock],es
  587.         sub     si,si
  588.         mov     di,si
  589.         mov     cx,(OFFSET ProgramEnd - OFFSET PspStart + 1) Shr 1
  590.         cld
  591.         rep     movsw
  592.  
  593.         push    es
  594.         mov     ax, OFFSET Continue
  595.         push    ax
  596.         retf
  597.  
  598. ;        pop     cs                      ; Jump into second copy of program!
  599. Continue:
  600.         push    ds
  601.         push    cs
  602.         pop     ds
  603.         pop     es                      ; DS=CS = SecondBlock, ES=FirstBlock
  604.  
  605. ; Move resident part of code as low as possible:
  606.  
  607.         mov     si, OFFSET HighStart
  608.         mov     di, OFFSET LowStart
  609.         mov     cx, OFFSET ResidentEnd - OFFSET HighStart
  610.         rep     movsb
  611.  
  612.         mov dx, OFFSET StartMsg
  613.         mov ah,9
  614.         int 21h
  615.  
  616.         call Parse
  617.  
  618.         cmp di, OFFSET StuffBuffer - MoveDown
  619.         mov dx, offset SyntaxMsg
  620.          je ErrorMessage                ; No parameters!
  621.  
  622.         push    es
  623.         pop     ds                      ; DS,ES,SS = FirstBlock
  624.  
  625.         mov [StuffEnd-MoveDown],di
  626. ;       mov [StuffMode-MoveDown], OFFSET NextKey - MoveDown
  627. ;       mov [StuffPtr-MoveDown], OFFSET StuffBuffer - MoveDown
  628.  
  629. ;       mov [active-MoveDown],-1                 ; Initialize [active] flag
  630. ;       mov [BiosSeg-MoveDown],40h               ; Fast load of ES: when resident
  631.  
  632.         call TestSecond                 ; Don't return if second copy!
  633.  
  634. ; Get old int 2F interrupt
  635.  
  636.         mov ax,352Fh
  637.         int 21h
  638.         mov WORD PTR [OldInt2F-MoveDown],BX
  639.         mov WORD PTR [OldInt2F+2-MoveDown],ES
  640.  
  641. ; Get old timer interrupt
  642.         mov ax,3508h
  643.         int 21h
  644.         mov WORD PTR [OldTimer-MoveDown],BX
  645.         mov Word Ptr [OldTimer+2-Movedown],ES
  646.  
  647. ; Enter our routine first
  648.  
  649.         mov ax,252Fh
  650.         mov dx, OFFSET Int2F - MoveDown
  651.         int 21h
  652.  
  653.         mov ax,2508h
  654.         mov dx, OFFSET MyTimer - MoveDown
  655.         int 21h
  656.  
  657.         mov ES, [DS:2Ch]
  658.         mov ah,49h
  659.         int 21h
  660.         mov word ptr [DS:2Ch],0         ; Signal no environment!
  661.  
  662.         push ds
  663.         push cs
  664.         pop ds
  665.         mov dx, OFFSET StayResMsg
  666.         mov ah,9
  667.         int 21h
  668.         pop ds
  669.  
  670.         mov cx,5
  671. @@CloseLoop:
  672.         mov bx,cx
  673.         dec bx
  674.         mov ah,3Eh
  675.         int 21h
  676.          loop @@CloseLoop
  677.  
  678.         push ss
  679.         pop ds
  680.         mov dx, [DS:ResidentSize]
  681.         cmp dx, [DS:Stuffend-MoveDown]
  682.          ja @@OK
  683.         mov dx, [DS:StuffEnd-MoveDown] ; DX = MAX(ResidentSize, StuffEnd)
  684.         mov [DS:ResidentSize],dx
  685. @@OK:
  686.         add dx,15
  687.         mov cl,4
  688.         shr dx,cl
  689.         mov ax,3100h
  690.         int 21h                         ; Go TSR with first block
  691.  
  692. Init Endp
  693.  
  694. FindFirst proc near
  695.  
  696.         mov dx,Semafor
  697.         mov ax,0E000h
  698.         xor bx,bx
  699.         int 2Fh
  700.  
  701.         cmp al,0FFh
  702.          jne @@done
  703.  
  704.         cmp bx, Version
  705.          je @@done
  706.  
  707.         mov dx, offset WrongVerMsg
  708.          jmp ErrorMessage
  709.  
  710. @@done:
  711.         ret                             ; Return ZERO if found
  712.  
  713. FindFirst endp
  714.  
  715. TestSecond proc near
  716.         call FindFirst
  717.          jne @@NotFound
  718.  
  719.         mov es,dx                       ; Save segment
  720.  
  721. ; This is the second copy! ES -> to first copy
  722. ; Test if enough room in resident program:
  723.  
  724.         mov ax,[StuffEnd-MoveDown]
  725.         cmp ax,[ES:ResidentSize]
  726.         mov dx, OFFSET ResidentToSmallMsg
  727.          ja ErrorMessage
  728.  
  729. ; Will now move all data into first copy, including pointers and StuffMode
  730.  
  731.         mov [ES:SelfModify-MoveDown], IRET_OPCODE ; Stop resident program
  732.         mov [DS:SelfModify-MoveDown], IRET_OPCODE ; Stop this version!
  733.  
  734.         mov si, offset FirstByteToCopy - MoveDown
  735.         mov di, si
  736.         mov cx, ax                      ; [StuffEnd]
  737.         sub cx,si
  738.         rep movsb
  739.  
  740.         mov [ES:SelfModify-MoveDown], PUSH_AX_OPCODE ; Restart resident version
  741.  
  742.         mov dx, OFFSET UpdateMsg
  743.         mov ah,9
  744.         int 21h
  745.  
  746.         mov ax,4C00h
  747.         int 21h
  748.  
  749. @@NotFound:
  750. ParseFinish:
  751.         ret
  752.  
  753. TestSecond ENDP
  754.  
  755. LocalSyntax:
  756.         jmp Syntax
  757.  
  758. EOF     EQU 26
  759. LF      EQU 10                  ; Use LineFeed to count lines
  760.  
  761. LineNr  dw      ?
  762.  
  763. Parse Proc near
  764.         cld
  765.         mov si, OFFSET CommandLine
  766.  
  767.         mov bl,[si-1]
  768.         sub bh,bh
  769.         mov word ptr [si+bx],13 + (EOF * 256) ; EOF is ending marker
  770.  
  771. RestartParse:
  772.         mov di, OFFSET StuffBuffer - MoveDown
  773.  
  774.         mov     [LineNr],0
  775. NewLine:
  776.         inc     [LineNr]
  777.  
  778. ParseNextChar:
  779.         lodsb
  780.  
  781.         cmp al,EOF
  782.          je ParseFinish
  783.  
  784.         cmp     al,LF
  785.          je     NewLine                 ; LF marks the end of one line
  786.  
  787.         cmp     al,';'                  ; ';' makes the rest of the line a
  788.          je     @@Comment               ; comment
  789.  
  790.         cmp     al,' '
  791.          jbe    ParseNextChar
  792.  
  793.         cmp al,'0'
  794.          jb @@NotDigit
  795.         cmp al,'9'
  796.          ja @@NotDigit
  797. @@Digit:
  798.         dec si
  799.         mov bx,255
  800.         call GetNumber
  801.         cmp al,':'
  802.         mov al,bl
  803.         mov ah,2
  804.          jne @@NotTwo
  805.  
  806.         push ax
  807.         mov bx,255
  808.         call GetNumber
  809.         cmp al,':'
  810.          je LocalSyntax
  811.  
  812.         pop ax
  813.         mov ah,bl
  814.  
  815. @@NotTwo:
  816.         Call SaveChar
  817.          jmp ParseNextChar
  818.  
  819. @@Comment:
  820.         lodsb
  821.  
  822.         cmp     al,EOF
  823.          je     ParseFinish
  824.  
  825.         cmp     al,13
  826.          je     ParseNextChar
  827.  
  828.         cmp     al,LF
  829.          jne    @@Comment
  830.  
  831.         jmp     NewLine
  832.  
  833. @@NotDigit:
  834.         cmp al,'/'
  835.          je ParseOption
  836.  
  837.         cmp al,'@'
  838.         jne @@NotFunc
  839.         mov bx,255
  840.         call GetNumber
  841.         mov ah,bl
  842.         xor al,al
  843.         stosw
  844.          jmp ParseNextChar
  845.  
  846. @@NotFunc:
  847.         cmp al,"'"
  848.          je @@Quote
  849.         cmp al,'"'
  850.          jne @@NotQuote
  851. @@Quote:
  852.         mov bl,al                       ; Save starting quote char
  853. @@2:
  854.         lodsb
  855.         cmp al,13                       ; Missing last quote
  856.          je Syntax
  857.         cmp al,bl                       ; Ending quote?
  858.          je ParseNextChar               ; Yes, restart
  859.         mov ah,2                        ; Assume scan = 2
  860.         call SaveChar
  861.          jmp @@2
  862.  
  863. @@NotQuote:
  864.         cmp al,'+'
  865.          jne @@3
  866.          jmp DeTime
  867. @@3:
  868.         cmp al,'='
  869.          jne @@4
  870.          jmp AtTime
  871.  
  872. @@4:
  873. ; Use ! to signal Reboot
  874.         cmp al,'!'
  875.          je SignalReboot
  876.  
  877.         cmp al,'f'
  878.          je FindNear
  879.         cmp al,'F'
  880.          je FindNear
  881.  
  882.         cmp al,'p'
  883.          je PromptNear
  884.         cmp al,'P'
  885.          je PromptNear
  886.  
  887.         cmp  al,'{'                     ; Start of token!
  888.          je  StartToken
  889.         cmp  al,'['                     ; Start of token!
  890.          je  StartToken
  891.  
  892.  
  893. ; Fall into syntax error!
  894. Parse endp
  895.  
  896. Syntax Proc near
  897.         push    cs
  898.         pop     ds
  899.  
  900.         mov     dx, OFFSET SyntaxMsg
  901.         mov     ah,9
  902.         int     21h
  903.  
  904.         mov     dx, OFFSET ErrorMsg1
  905.         mov     ah,9
  906.         int     21h
  907.  
  908.         mov     ax,[LineNr]
  909.         xor     cx,cx
  910.         call    PrintAX
  911.         mov     dx, OFFSET ErrorMsg2
  912.  
  913. ErrorMessage:
  914.         push cs
  915.         pop ds
  916.  
  917.         mov     ah,9
  918.         int     21h
  919.  
  920.         mov ax,4C01h
  921.         int 21h
  922. Syntax Endp
  923.  
  924. StartToken:
  925.         push    es di
  926.  
  927.         push    ds
  928.         pop     es
  929.  
  930.         call    GetToken
  931.         call    FindToken
  932.         pop     di es
  933.          jc     Syntax
  934.  
  935.         cmp     al,EXTENDED_CODE
  936.          je     @@SaveExtended
  937.  
  938.         call    SaveChar
  939.         jmp     ParseNextChar
  940.  
  941. @@SaveExtended:
  942.         cmp     ah,ATTIME_CODE
  943.          je     AtTime
  944.         cmp     ah,DELTATIME_CODE
  945.          je     DeTime
  946.         cmp     ah,FIND_CODE
  947.          je     FindNear
  948.         cmp     ah,PROMPT_CODE
  949.          je     PromptNear
  950. ;        cmp     ah,PRTSCRN_CODE
  951. ;         je     @@Save2
  952. ;        cmp     ah,REBOOT_CODE
  953. ;         je     @@Save2
  954. @@Save2:
  955.         stosw
  956.         jmp     ParseNextChar
  957.  
  958. FindNear:
  959.         jmp FindText
  960.  
  961. PromptNear:
  962.         jmp PromptText
  963.  
  964. SignalReBoot:
  965.         mov ax,EXTENDED_CODE + (REBOOT_CODE * 256)
  966.         stosw
  967.          jmp ParseNextChar
  968.  
  969. ReadFile proc near
  970.  
  971.         cmp byte ptr [si],':'
  972.          jne @@Skip
  973.         inc si
  974. @@Skip:
  975.         mov dx,si
  976. @@Next:
  977.         lodsb
  978.         cmp al,' '
  979.          ja @@Next
  980.         dec si
  981.  
  982.         mov ax,3D00h                    ; Open file for Read_Only
  983.         mov byte ptr [si],al            ; Make ASCIIZ filename
  984.  
  985.         int 21h
  986.         mov dx, OFFSET FileErrMsg
  987.          jc ErrorMessage
  988.  
  989.         mov bx,ax
  990.         mov ah,3Fh                      ; Read File
  991.         mov dx, OFFSET ProgramEnd
  992.         mov si,dx
  993.         mov cx, - (OFFSET ProgramEnd)   ; Max Size in segment
  994.         int 21h
  995.         mov dx, OFFSET FileErrMsg
  996.          jc ErrorMessage
  997.  
  998.         add si, ax
  999.         mov byte ptr [si],EOF
  1000.         sub si, ax                      ; Point back to start of filebuffer
  1001.  
  1002.         mov ah,3Eh
  1003.         int 21h                         ; Close this file
  1004.  
  1005.          jmp RestartParse               ; Parse file buffer!
  1006. ReadFile endp
  1007.  
  1008. SetBufferSize proc near
  1009.  
  1010.         cmp byte ptr [si],':'
  1011.          jne @@Skip
  1012.         inc si
  1013. @@Skip:
  1014.         mov bx,-( OFFSET StuffBuffer - MoveDown) + 32; Max text buffer
  1015.         call GetNumber
  1016.         add bx,OFFSET StuffBuffer - MoveDown + 15
  1017.         and bx,0FFF0h
  1018.         mov [ES:ResidentSize],bx
  1019.          jmp ParseNextChar
  1020.  
  1021. SetBufferSize endp
  1022.  
  1023. ParseOption Proc near
  1024.         lodsb
  1025.         cmp al,'a'
  1026.          jb @@Upper
  1027.         cmp al,'z'
  1028.          ja @@Upper
  1029.         sub al,'a'-'A'
  1030. @@Upper:
  1031.         cmp al,'B'
  1032.          je SetBufferSize
  1033.         cmp al,'F'
  1034.          je ReadFile
  1035.         cmp al,'L'
  1036.          je ListKeys
  1037.         cmp al,'R'
  1038.          jne Syntax
  1039. @@Remove:
  1040.         call FindFirst
  1041.          jnz Syntax                     ; First copy, nothing to remove!
  1042.  
  1043.         mov ax,3508h
  1044.         int 21h
  1045.         cmp bx, OFFSET MyTimer - MoveDown
  1046.          jne @@CannotRemove
  1047.         mov ax, es
  1048.         cmp ax, dx
  1049.          jne @@CannotRemove             ; Other TSR has timer vector!
  1050.  
  1051.         mov ax,352Fh
  1052.         int 21h
  1053.         cmp bx, OFFSET Int2F - MoveDown
  1054.          jne @@CannotRemove
  1055.         mov ax, es
  1056.         cmp ax, dx
  1057.          jne @@CannotRemove             ; Other TSR has Int 2F vector!
  1058.  
  1059. ; OK to remove previous copy from RAM
  1060. ; First, restore timer vector
  1061.  
  1062.         push ds
  1063.         mov dx,[word ptr ES:OldTimer-MoveDown]
  1064.         mov ds,[word ptr ES:OldTimer+2-MoveDown]
  1065.         mov ax,2508h
  1066.         int 21h
  1067.  
  1068. ; Then, retore Int 2F vector
  1069.  
  1070.         mov dx,[word ptr ES:OldInt2F-MoveDown]
  1071.         mov ds,[word ptr ES:OldInt2F+2-MoveDown]
  1072.         mov ax,252Fh
  1073.         int 21h
  1074.  
  1075.         pop ds
  1076.  
  1077. ; Next, release the memory segment
  1078. ; ES -> to previos copy!
  1079.  
  1080.         mov ah,49h
  1081.         int 21h
  1082.  
  1083.         mov dx, OFFSET RemovedMsg
  1084.         mov ah,9
  1085.         int 21h
  1086.  
  1087.         mov ax,4C00h
  1088.         int 21h
  1089.  
  1090. @@CannotRemove:
  1091.         mov dx, OFFSET NotRemovedMsg
  1092.         mov ah,9
  1093.         int 21h
  1094.  
  1095.         mov ax,4C01h
  1096.         int 21h
  1097.  
  1098. ParseOption Endp
  1099.  
  1100. hour    dw ?
  1101. min     dw ?
  1102. sec     dw ?
  1103.  
  1104. DeTime Proc near
  1105.         mov ax,EXTENDED_CODE + (DELTATIME_CODE * 256)
  1106.          jmp short ParseTime
  1107. AtTime:
  1108.         mov ax,EXTENDED_CODE + (ATTIME_CODE * 256)
  1109.  
  1110. ParseTime:
  1111.         stosw                           ; Save marker for time
  1112.  
  1113. ; FIX BUG found by davidgb. HOUR and Min MUST be initialized to ZERO!
  1114.  
  1115.         xor ax,ax
  1116.         mov [hour],ax
  1117.         mov [min],ax
  1118.  
  1119. ; END OF bug-fix
  1120.  
  1121.         mov bx,59                       ; Max value
  1122.         call GetNumber
  1123.         cmp al,':'
  1124.          jne @@SaveSec
  1125.  
  1126.         mov [min],bx
  1127.         mov bx,59
  1128.         call GetNumber
  1129.         cmp al,':'
  1130.          jne @@SaveSec
  1131.  
  1132.         xchg bx,[min]
  1133.         mov [hour],bx
  1134.         mov bx,59
  1135.         call GetNumber
  1136.         cmp al,':'
  1137.          je NearError
  1138.  
  1139. @@SaveSec:
  1140.         mov [sec],bx
  1141.  
  1142. ; Now convert hour:min:sec into Timer ticks:
  1143. ; Ticks= (hour*TicksPrHour) + (((min*60)+sec) * 34829 + 956) DIV 1913
  1144. ; 34829 / 1913 is 18.206482, which is the closest possible result to
  1145. ; the true value of 18.206493 Ticks/second, using only 16 bit mul and div.
  1146.  
  1147.         mov ax,(TicksPrHour - 65536)
  1148.         mul [hour]
  1149.         add dx,[hour]                   ; DX:AX = hour*TicksPrHour
  1150.         push ax                         ; Save DX:AX
  1151.         push dx                         ; --- " ---
  1152.  
  1153.         mov al,60
  1154.         mul [byte ptr min]
  1155.         add ax,[sec]                    ; AX has # of seconds
  1156.  
  1157.         mov dx,34829
  1158.         mul dx
  1159.         add ax,1913 Shr 1               ; Add 1913/2 to get automatic rounding
  1160.         adc dx,0                        ; of fractional timer ticks
  1161.         mov bx,ax
  1162.         mov ax,dx
  1163.         xor dx,dx
  1164.         mov cx,1913
  1165.         div cx
  1166.         xchg ax,bx
  1167.         div cx                          ; BX:AX = Ticks in (min*60+sec)
  1168.  
  1169.         pop dx
  1170.         pop cx                          ; DX:CX = Ticks in hours
  1171.  
  1172.         add ax,cx
  1173.         adc dx,bx                       ; DX:AX = Total Ticks
  1174.  
  1175.         stosw                           ; Save Low word of count
  1176.         mov al,dl
  1177.         stosb                           ; Save high byte of count
  1178.  
  1179.          jmp ParseNextChar
  1180. DeTime Endp
  1181.  
  1182. PromptText:
  1183.         mov ax,EXTENDED_CODE + (PROMPT_CODE * 256)
  1184.         stosw
  1185.         mov bx,127
  1186.         call GetNumber
  1187.         cmp al,','
  1188.          jne NearError
  1189.         mov al,bl                       ; Save X value
  1190.         stosb
  1191.  
  1192.         mov bx,60                       ; 0<y<=60
  1193.         call GetNumber
  1194.         cmp al,','
  1195.          jne NearError
  1196.         mov al,bl
  1197.         stosb                           ; Save Y value
  1198.  
  1199.          jmp short Text1
  1200.  
  1201. NearError:
  1202.         jmp syntax
  1203.  
  1204. FindText proc near
  1205.         mov ax,EXTENDED_CODE + (FIND_CODE * 256) ; 255 + 3 -> flag for Find Text
  1206.         stosw
  1207.  
  1208.         mov bx,132                      ; 0<x<=132
  1209.         call GetNumber
  1210.         cmp al,','
  1211.          jne GetError
  1212.         sub bl,1
  1213.          jb GetError
  1214.         mov cx,bx                       ; Save X value
  1215.  
  1216.         mov bx,60                       ; 0<y<=60
  1217.         call GetNumber
  1218.         cmp al,','
  1219.          jne GetError
  1220.         sub bl,1
  1221.          jb GetError
  1222.  
  1223.         mov al,80
  1224.         mul bl
  1225.         add ax, cx
  1226.         shl ax, 1
  1227.         stosw                           ; Save X,Y as starting offset
  1228.  
  1229. Text1:
  1230.         mov bx,(132*60)                 ; Get length to search in
  1231.         call GetNumber
  1232.         cmp al,','
  1233.          jne NearError
  1234.         or bx,bx
  1235.          jz NearError                   ; Count must be > 0
  1236.  
  1237.         mov ax,bx
  1238.         stosw                           ; Save buffer length
  1239.  
  1240.         mov bx,255
  1241.         cmp byte ptr [si],'"'
  1242.          je @@SkipAttr
  1243.         cmp byte ptr [si],"'"
  1244.          je @@SkipAttr
  1245.  
  1246.         call GetNumber
  1247.         cmp al,','
  1248.          jne NearError
  1249.  
  1250. @@SkipAttr:
  1251.         mov ah,bl
  1252.         lodsb
  1253.         cmp al,'"'
  1254.          je @@1
  1255.         cmp al,"'"
  1256.          jne GetError
  1257. @@1:
  1258.         mov bx,di                       ; Save current pos for len
  1259.         stosw                           ; Store len + attr
  1260.  
  1261.         mov ah,al
  1262.         xor cx,cx
  1263. @@2:
  1264.         lodsb
  1265.         cmp al,EOF
  1266.          je GetError
  1267.         cmp al,13
  1268.          je GetError
  1269.         cmp al,ah
  1270.          je @@3
  1271.  
  1272.         inc cx                          ; INC len
  1273.         stosb                           ; Save Text
  1274.         jmp @@2
  1275.  
  1276. @@3:
  1277.         mov [ES:bx],cl                  ; Save actual length!
  1278.  
  1279.         jmp ParseNextChar
  1280.  
  1281. FindText endp
  1282.  
  1283.  
  1284. GetError:
  1285.         jmp syntax
  1286.  
  1287. GetNumber proc near
  1288. ; input: SI -> first char to convert, BX = max value
  1289.         push cx                         ; Use as temp buffer
  1290.         push dx                         ; For mul
  1291.         push di                         ; For sign flag
  1292.  
  1293.         sub cx,cx
  1294.         mov ah,ch
  1295.         mov di,cx                       ; Zero DI -> Positive
  1296.  
  1297.         cmp byte ptr [si],'-'
  1298.          jne @@GetLoop
  1299.         dec di
  1300.         inc si
  1301.  
  1302. @@GetLoop:
  1303.         lodsb
  1304.         cmp al,' '
  1305.          jbe @@GetEnd
  1306.         cmp al,':'
  1307.          je @@GetEnd
  1308.         cmp al,','
  1309.          je @@GetEnd
  1310.  
  1311.         sub al,'0'
  1312.          jb GetError
  1313.         cmp al,9
  1314.          ja GetError
  1315.  
  1316. ; Valid decimal digit!
  1317.         xchg ax,cx
  1318.         mov dx,10
  1319.         mul dx
  1320.         add cx,ax
  1321.          jmp @@GetLoop
  1322. @@GetEnd:
  1323.         cmp al,EOF
  1324.          jne @@1
  1325.         dec si                          ; Prepare to reload AL
  1326. @@1:
  1327.         cmp cx,bx                       ; Valid value?
  1328.          ja GetError
  1329.         mov bx,cx
  1330.  
  1331.         or di,di
  1332.          jz @@done
  1333.  
  1334.         neg bx                          ; return -BX
  1335.  
  1336. @@done:
  1337.         pop di
  1338.         pop dx
  1339.         pop cx
  1340.  
  1341.         ret
  1342. GetNumber endp
  1343.  
  1344. SaveChar proc near
  1345. ; AL, AH = char, scan to save in StuffBuffer
  1346.  
  1347.         or al,al
  1348.          je @@Save2
  1349.         cmp al,224
  1350.          je @@Save2
  1351.  
  1352.         cmp al,254
  1353.          jae @@Save3
  1354.         cmp ah,2                        ; Scan for normal chars
  1355.          jne @@Save3
  1356.  
  1357.         mov ah,14
  1358.         cmp al,8
  1359.          je @@Save3
  1360.         mov ah,15
  1361.         cmp al,9
  1362.          je @@Save3
  1363.         mov ah,28
  1364.         cmp al,13
  1365.          je @@Save3
  1366.         mov ah,1
  1367.         cmp al,27
  1368.          je @@Save3
  1369.  
  1370. ; Normal character, store just the char itself
  1371.  
  1372.         stosb
  1373.         ret
  1374.  
  1375. @@Save2:
  1376.         stosw
  1377.         ret
  1378.  
  1379. @@Save3:
  1380.         mov byte ptr [es:di],254
  1381.         inc di
  1382.         stosw
  1383.         ret
  1384. SaveChar endp
  1385.  
  1386. MaxTokenSize = 8
  1387.  
  1388. CurToken   db MaxTokenSize dup (0)
  1389.  
  1390. TokenTable      label byte
  1391.  
  1392. T_S     struc
  1393. tname   db      MaxTokenSize dup (0)
  1394. tchr    db      ?
  1395. tscn    db      ?
  1396.         ends
  1397.  
  1398.         T_S     <"ESC",27,1>
  1399.  
  1400. TokenRecSize    = $ - offset TokenTable
  1401.  
  1402.         T_S     <"aEsc",0,1>
  1403.  
  1404.         T_S     <"^@",0,3>
  1405.         T_S     <"^A",1,30>
  1406.         T_S     <"^B",2,48>
  1407.         T_S     <"^C",3,46>
  1408.         T_S     <"^D",4,32>
  1409.         T_S     <"^E",5,18>
  1410.         T_S     <"^F",6,33>
  1411.         T_S     <"^G",7,34>
  1412.         T_S     <"^H",8,35>
  1413.         T_S     <"^I",9,23>
  1414.         T_S     <"^J",10,36>
  1415.         T_S     <"^K",11,37>
  1416.         T_S     <"^L",12,38>
  1417.         T_S     <"^M",13,50>
  1418.         T_S     <"^N",14,49>
  1419.         T_S     <"^O",15,24>
  1420.         T_S     <"^P",16,25>
  1421.         T_S     <"^Q",17,16>
  1422.         T_S     <"^R",18,19>
  1423.         T_S     <"^S",19,31>
  1424.         T_S     <"^T",20,20>
  1425.         T_S     <"^U",21,22>
  1426.         T_S     <"^V",22,47>
  1427.         T_S     <"^W",23,17>
  1428.         T_S     <"^X",24,45>
  1429.         T_S     <"^Y",25,21>
  1430.         T_S     <"^Z",26,44>
  1431.         T_S     <"^[",27,26>
  1432.         T_S     <"^\",28,43>
  1433.         T_S     <"^]",29,27>
  1434.         T_S     <"^^",30,7>
  1435.         T_S     <"^_",31,12>
  1436.  
  1437. FirstChar       label byte              ; Use to translate characters into
  1438.                                         ; char:scan pairs
  1439.  
  1440.         T_S     <" ",32,57>
  1441.         T_S     <"!",33,2>
  1442.         T_S     <'"',34,40>
  1443.         T_S     <"#",35,4>
  1444.         T_S     <"$",36,5>
  1445.         T_S     <"%",37,6>
  1446.         T_S     <"&",38,8>
  1447.         T_S     <"'",39,40>
  1448.         T_S     <"(",40,10>
  1449.         T_S     <")",41,11>
  1450.         T_S     <"*",42,9>
  1451.         T_S     <"+",43,13>
  1452.         T_S     <",",44,51>
  1453.         T_S     <"-",45,12>
  1454.         T_S     <".",46,52>
  1455.         T_S     <"/",47,53>
  1456.  
  1457.         T_S     <"0",48,11>
  1458.         T_S     <"1",49,2>
  1459.         T_S     <"2",50,3>
  1460.         T_S     <"3",51,4>
  1461.         T_S     <"4",52,5>
  1462.         T_S     <"5",53,6>
  1463.         T_S     <"6",54,7>
  1464.         T_S     <"7",55,8>
  1465.         T_S     <"8",56,9>
  1466.         T_S     <"9",57,10>
  1467.         T_S     <":",58,39>
  1468.         T_S     <";",59,39>
  1469.         T_S     <"<",60,51>
  1470.         T_S     <"=",61,13>
  1471.         T_S     <">",62,52>
  1472.         T_S     <"?",63,53>
  1473.  
  1474.         T_S     <"@",'@',3>
  1475.         T_S     <"A",'A',30>
  1476.         T_S     <"B",'B',48>
  1477.         T_S     <"C",'C',46>
  1478.         T_S     <"D",'D',32>
  1479.         T_S     <"E",'E',18>
  1480.         T_S     <"F",'F',33>
  1481.         T_S     <"G",'G',34>
  1482.         T_S     <"H",'H',35>
  1483.         T_S     <"I",'I',23>
  1484.         T_S     <"J",'J',36>
  1485.         T_S     <"K",'K',37>
  1486.         T_S     <"L",'L',38>
  1487.         T_S     <"M",'M',50>
  1488.         T_S     <"N",'N',49>
  1489.         T_S     <"O",'O',24>
  1490.         T_S     <"P",'P',25>
  1491.         T_S     <"Q",'Q',16>
  1492.         T_S     <"R",'R',19>
  1493.         T_S     <"S",'S',31>
  1494.         T_S     <"T",'T',20>
  1495.         T_S     <"U",'U',22>
  1496.         T_S     <"V",'V',47>
  1497.         T_S     <"W",'W',17>
  1498.         T_S     <"X",'X',45>
  1499.         T_S     <"Y",'Y',21>
  1500.         T_S     <"Z",'Z',44>
  1501.         T_S     <"[",'[',26>
  1502.         T_S     <"\",'\',43>
  1503.         T_S     <"]",']',27>
  1504.         T_S     <"^",'^',7>
  1505.         T_S     <"_",'_',12>
  1506.  
  1507.         T_S     <"`",'`',41>
  1508.         T_S     <"a",'a',30>
  1509.         T_S     <"b",'b',48>
  1510.         T_S     <"c",'c',46>
  1511.         T_S     <"d",'d',32>
  1512.         T_S     <"e",'e',18>
  1513.         T_S     <"f",'f',33>
  1514.         T_S     <"g",'g',34>
  1515.         T_S     <"h",'h',35>
  1516.         T_S     <"i",'i',23>
  1517.         T_S     <"j",'j',36>
  1518.         T_S     <"k",'k',37>
  1519.         T_S     <"l",'l',38>
  1520.         T_S     <"m",'m',50>
  1521.         T_S     <"n",'n',49>
  1522.         T_S     <"o",'o',24>
  1523.         T_S     <"p",'p',25>
  1524.         T_S     <"q",'q',16>
  1525.         T_S     <"r",'r',19>
  1526.         T_S     <"s",'s',31>
  1527.         T_S     <"t",'t',20>
  1528.         T_S     <"u",'u',22>
  1529.         T_S     <"v",'v',47>
  1530.         T_S     <"w",'w',17>
  1531.         T_S     <"x",'x',45>
  1532.         T_S     <"y",'y',21>
  1533.         T_S     <"z",'z',44>
  1534.         T_S     <"{",'{',26>
  1535.         T_S     <"|",'|',43>
  1536.         T_S     <"}",'}',27>
  1537.         T_S     <"~",'~',7>
  1538.  
  1539.         T_S     <"æ",'æ',40>
  1540.         T_S     <"¢",'¢',39>
  1541.         T_S     <"å",'å',26>
  1542.         T_S     <"Æ",'Æ',40>
  1543.         T_S     <"¥",'¥',39>
  1544.         T_S     <"Å",'Å',26>
  1545.  
  1546. LastChar        label byte
  1547.  
  1548.         T_S     <"a1",0,120>
  1549.         T_S     <"a2",0,121>
  1550.         T_S     <"a3",0,122>
  1551.         T_S     <"a4",0,123>
  1552.         T_S     <"a5",0,124>
  1553.         T_S     <"a6",0,125>
  1554.         T_S     <"a7",0,126>
  1555.         T_S     <"a8",0,127>
  1556.         T_S     <"a9",0,128>
  1557.         T_S     <"a0",0,129>
  1558.         T_S     <"a-",0,130>
  1559.         T_S     <"a=",0,131>
  1560.  
  1561.         T_S     <"BS",8,14>
  1562.         T_S     <"aBS",0,14>
  1563.         T_S     <"^BS",127,14>
  1564.  
  1565.         T_S     <"Tab",9,15>
  1566.         T_S     <"sTab",0,15>
  1567.         T_S     <"^Tab",0,148>
  1568.  
  1569.         T_S     <"CR",13,28>
  1570.         T_S     <"^CR",10,28>
  1571.         T_S     <"aCR",0,28>
  1572.  
  1573.         T_S     <"aQ",0,16>
  1574.         T_S     <"aW",0,17>
  1575.         T_S     <"aE",0,18>
  1576.         T_S     <"aR",0,19>
  1577.         T_S     <"aT",0,20>
  1578.         T_S     <"aY",0,21>
  1579.         T_S     <"aU",0,22>
  1580.         T_S     <"aI",0,23>
  1581.         T_S     <"aO",0,24>
  1582.         T_S     <"aP",0,25>
  1583.         T_S     <"a[",0,26>
  1584.         T_S     <"a]",0,27>
  1585.  
  1586.         T_S     <"aA",0,30>
  1587.         T_S     <"aS",0,31>
  1588.         T_S     <"aD",0,32>
  1589.         T_S     <"aF",0,33>
  1590.         T_S     <"aG",0,34>
  1591.         T_S     <"aH",0,35>
  1592.         T_S     <"aJ",0,36>
  1593.         T_S     <"aK",0,37>
  1594.         T_S     <"aL",0,38>
  1595.         T_S     <"a;",0,39>
  1596.         T_S     <"a'",0,40>
  1597.         T_S     <"a`",0,41>
  1598.  
  1599.         T_S     <"a\",0,43>
  1600.         T_S     <"aZ",0,44>
  1601.         T_S     <"aX",0,45>
  1602.         T_S     <"aC",0,46>
  1603.         T_S     <"aV",0,47>
  1604.         T_S     <"aB",0,48>
  1605.         T_S     <"aN",0,49>
  1606.         T_S     <"aM",0,50>
  1607.         T_S     <"a,",0,51>
  1608.         T_S     <"a.",0,52>
  1609.         T_S     <"a/",0,53>
  1610.  
  1611.         T_S     <"a*",0,55>
  1612.  
  1613.         T_S     <"F1",0,59>
  1614.         T_S     <"F2",0,60>
  1615.         T_S     <"F3",0,61>
  1616.         T_S     <"F4",0,62>
  1617.         T_S     <"F5",0,63>
  1618.         T_S     <"F6",0,64>
  1619.         T_S     <"F7",0,65>
  1620.         T_S     <"F8",0,66>
  1621.         T_S     <"F9",0,67>
  1622.         T_S     <"F10",0,68>
  1623.  
  1624.         T_S     <"n/",'/',224>
  1625.         T_S     <"n*",'*',55>
  1626.         T_S     <"n7",'7',74>
  1627.         T_S     <"n8",'8',72>
  1628.         T_S     <"n9",'9',73>
  1629.         T_S     <"n-",'-',74>
  1630.         T_S     <"n4",'4',75>
  1631.         T_S     <"n5",'5',76>
  1632.         T_S     <"n6",'6',77>
  1633.         T_S     <"n+",'+',78>
  1634.         T_S     <"n1",'1',79>
  1635.         T_S     <"n2",'2',80>
  1636.         T_S     <"n3",'3',81>
  1637.         T_S     <"n0",'0',82>
  1638.         T_S     <"n.",'.',83>
  1639.         T_S     <"Enter",13,224>
  1640.  
  1641.         T_S     <"Home",0,71>
  1642.         T_S     <"Up"  ,0,72>
  1643.         T_S     <"PgUp",0,73>
  1644.         T_S     <"Left",0,75>
  1645.         T_S     <"Right",0,77>
  1646.         T_S     <"End" ,0,79>
  1647.         T_S     <"Down",0,80>
  1648.         T_S     <"PgDn",0,81>
  1649.         T_S     <"Ins" ,0,82>
  1650.         T_S     <"Del" ,0,83>
  1651.  
  1652.         T_S     <"^n/",0,149>
  1653.         T_S     <"^n*",0,150>
  1654.         T_S     <"^Home",0,119>
  1655.         T_S     <"^Up"  ,0,141>
  1656.         T_S     <"^PgUp",0,132>
  1657.         T_S     <"^n-",0,142>
  1658.         T_S     <"^Left",0,115>
  1659.         T_S     <"^n5",0,143>
  1660.         T_S     <"^Right",0,116>
  1661.         T_S     <"^n+",0,144>
  1662.         T_S     <"^End" ,0,117>
  1663.         T_S     <"^Down",0,145>
  1664.         T_S     <"^PgDn",0,118>
  1665.         T_S     <"^Ins" ,0,146>
  1666.         T_S     <"^Del" ,0,147>
  1667.         T_S     <"^Enter",10,224>
  1668.  
  1669.         T_S     <"an/",0,164>
  1670.         T_S     <"an*",0,55>
  1671.         T_S     <"an-",0,74>
  1672.         T_S     <"an+",0,78>
  1673.         T_S     <"aEnter",10,224>
  1674.  
  1675.         T_S     <"eHome",224,71>
  1676.         T_S     <"eUp"  ,224,72>
  1677.         T_S     <"ePgUp",224,73>
  1678.         T_S     <"eLeft",224,75>
  1679.         T_S     <"eRight",224,77>
  1680.         T_S     <"eEnd" ,224,79>
  1681.         T_S     <"eDown",224,80>
  1682.         T_S     <"ePgDn",224,81>
  1683.         T_S     <"eIns" ,224,82>
  1684.         T_S     <"eDel" ,224,83>
  1685.  
  1686.         T_S     <"^eHome",224,119>
  1687.         T_S     <"^eUp"  ,224,141>
  1688.         T_S     <"^ePgUp",224,132>
  1689.         T_S     <"^eLeft",224,115>
  1690.         T_S     <"^eRight",224,116>
  1691.         T_S     <"^eEnd" ,224,117>
  1692.         T_S     <"^eDown",224,145>
  1693.         T_S     <"^ePgDn",224,118>
  1694.         T_S     <"^eIns" ,224,146>
  1695.         T_S     <"^eDel" ,224,147>
  1696.  
  1697.         T_S     <"aeHome",0,151>
  1698.         T_S     <"aeUp"  ,0,152>
  1699.         T_S     <"aePgUp",0,153>
  1700.         T_S     <"aeLeft",0,155>
  1701.         T_S     <"aeRight",0,157>
  1702.         T_S     <"aeEnd" ,0,159>
  1703.         T_S     <"aeDown",0,160>
  1704.         T_S     <"aePgDn",0,161>
  1705.         T_S     <"aeIns" ,0,162>
  1706.         T_S     <"aeDel" ,0,163>
  1707.  
  1708.         T_S     <"sF1",0,84>
  1709.         T_S     <"sF2",0,85>
  1710.         T_S     <"sF3",0,86>
  1711.         T_S     <"sF4",0,87>
  1712.         T_S     <"sF5",0,88>
  1713.         T_S     <"sF6",0,89>
  1714.         T_S     <"sF7",0,90>
  1715.         T_S     <"sF8",0,91>
  1716.         T_S     <"sF9",0,92>
  1717.         T_S     <"sF10",0,93>
  1718.  
  1719.         T_S     <"^F1",0,94>
  1720.         T_S     <"^F2",0,95>
  1721.         T_S     <"^F3",0,96>
  1722.         T_S     <"^F4",0,97>
  1723.         T_S     <"^F5",0,98>
  1724.         T_S     <"^F6",0,99>
  1725.         T_S     <"^F7",0,100>
  1726.         T_S     <"^F8",0,101>
  1727.         T_S     <"^F9",0,102>
  1728.         T_S     <"^F10",0,103>
  1729.  
  1730.         T_S     <"aF1",0,104>
  1731.         T_S     <"aF2",0,105>
  1732.         T_S     <"aF3",0,106>
  1733.         T_S     <"aF4",0,107>
  1734.         T_S     <"aF5",0,108>
  1735.         T_S     <"aF6",0,109>
  1736.         T_S     <"aF7",0,110>
  1737.         T_S     <"aF8",0,111>
  1738.         T_S     <"aF9",0,112>
  1739.         T_S     <"aF10",0,113>
  1740.  
  1741.         T_S     <"^PrtScrn",0,114>
  1742.  
  1743.         T_S     <"F11",0,133>
  1744.         T_S     <"F12",0,134>
  1745.         T_S     <"sF11",0,135>
  1746.         T_S     <"sF12",0,136>
  1747.         T_S     <"^F11",0,137>
  1748.         T_S     <"^F12",0,138>
  1749.         T_S     <"aF11",0,139>
  1750.         T_S     <"aF12",0,140>
  1751.  
  1752. ; Special functions with full names:
  1753.  
  1754.         T_S     <"PrtScrn",255,PRTSCRN_CODE>
  1755.         T_S     <"Boot"   ,255,REBOOT_CODE>
  1756.         T_S     <"^aDel"  ,255,REBOOT_CODE>     ; Ctrl-Alt-Del is an alias
  1757.                                                 ; for Boot!
  1758.         T_S     <"AtTime" ,255,ATTIME_CODE>
  1759.         T_S     <"Wait"   ,255,DELTATIME_CODE>
  1760.         T_S     <"Find"   ,255,FIND_CODE>
  1761.         T_S     <"Prompt" ,255,PROMPT_CODE>
  1762.         T_S     <"^Break" ,255,BREAK_CODE>
  1763.  
  1764. EndToken        label byte
  1765.  
  1766. GetToken        proc                    ; AL = '{', SI -> next char
  1767.  
  1768.         push    cx di
  1769.  
  1770.         mov     ah,'}'                  ; Assume '{ for start of token
  1771.         cmp     al,'{'
  1772.          je     @@l1
  1773.         mov     ah,']'                  ; No, so it must be '['
  1774. @@l1:
  1775.         mov     di, offset CurToken
  1776.         mov     cx, MaxTokenSize
  1777.         lodsb
  1778. @@next:
  1779.         cmp     al,'a'
  1780.          jb     @@upper
  1781.         cmp     al,'z'
  1782.          ja     @@upper
  1783.         sub     al,'a'-'A'
  1784. @@upper:
  1785.         stosb
  1786.         dec     cx
  1787.          jz     @@full
  1788.         lodsb
  1789.         cmp     al,' '
  1790.          jbe    @@MissingBrace
  1791.         cmp     al,ah
  1792.          jne    @@next
  1793.  
  1794.         mov     al,0
  1795.         rep     stosb
  1796. @@full:
  1797.         pop     di cx
  1798.         ret
  1799.  
  1800. @@MissingBrace:
  1801.         jmp     Syntax
  1802. GetToken        endp
  1803.  
  1804. TokenUpper      db 0
  1805.  
  1806. FindToken proc near             ; bx => token
  1807. ; Return CLC (and AX) if found, STC if not found
  1808.  
  1809.         push    cx dx si di
  1810.  
  1811.         cmp     [TokenUpper],0
  1812.          jne    @@TokenUpper
  1813.         call    UpcaseToken
  1814.         mov     [TokenUpper],1
  1815.  
  1816. @@TokenUpper:
  1817.         mov     dx, offset TokenTable - TokenRecSize
  1818.  
  1819. @@testtoken:
  1820.         add     dx, TokenRecSize
  1821.         cmp     dx, offset EndToken
  1822.         cmc
  1823.          jc     @@done
  1824.  
  1825.         mov     si, offset CurToken
  1826.         mov     di,dx
  1827.         mov     cx, MaxTokenSize SHR 1
  1828.    repe cmpsw
  1829.          jne     @@TestToken
  1830.  
  1831.         mov     ax,[di]
  1832.         clc
  1833. @@done:
  1834.         pop     di si dx cx
  1835.         ret
  1836. FindToken       endp
  1837.  
  1838. UpcaseToken     proc
  1839.         push    cx dx di
  1840.  
  1841.         mov     dx, offset TokenTable
  1842.  
  1843. @@testtoken:
  1844.         mov     di,dx
  1845.         mov     cx, MaxTokenSize
  1846.  
  1847. @@upcase:
  1848.         mov     al,[di]
  1849.         or      al,al
  1850.          jz     @@end
  1851.         cmp     al,'a'
  1852.          jb     @@upper
  1853.         cmp     al,'z'
  1854.          ja     @@upper
  1855.         sub     al,'a'-'A'
  1856. @@upper:
  1857.         stosb
  1858.          loop   @@upcase
  1859. @@end:
  1860.         add     dx, TokenRecSize
  1861.         cmp     dx, offset EndToken
  1862.          jb     @@testtoken
  1863.  
  1864. @@done:
  1865.         pop     di dx cx
  1866.         ret
  1867. UpcaseToken     endp
  1868.  
  1869. ListKeys        proc    near
  1870.         push    bx cx dx si di
  1871.  
  1872.         mov     si, offset TokenTable
  1873.  
  1874. @@print3:
  1875.         mov     di, 3
  1876.  
  1877. @@testtoken:
  1878.         cmp     si, offset EndToken
  1879.          jae    @@done
  1880.  
  1881.         mov     bx,'[' + ']' * 256
  1882.         cmp     byte ptr [si],'{'
  1883.          je     @@UseBrackets
  1884.         cmp     byte ptr [si],'}'
  1885.          je     @@UseBrackets
  1886.  
  1887.         mov     bx,'{' + '}' * 256      ; Use '{}' pair for all others
  1888.  
  1889. @@UseBrackets:
  1890.         mov     dl,bl
  1891.         mov     ah,2
  1892.         int     21h
  1893.  
  1894.         mov     cx, MaxTokenSize
  1895. @@nextchar:
  1896.         mov     al,[si]
  1897.         or      al,al
  1898.          jz     @@zero
  1899.         inc     si
  1900.         mov     dl,al
  1901.         mov     ah,2
  1902.         int     21h
  1903.          loop   @@nextchar
  1904. @@zero:
  1905.         mov     dl,bh
  1906.         mov     ah,2
  1907.         int     21h
  1908.  
  1909.         add     si,cx
  1910.         call    Space
  1911.  
  1912.         lodsb                   ; Character value
  1913.         xor     ah,ah
  1914.         mov     cx,3
  1915.         call    PrintAX
  1916.         mov     dl,':'
  1917.         mov     ah,2
  1918.         int     21h
  1919.         lodsb                   ; Character value
  1920.         xor     ah,ah
  1921.         xor     cx,cx
  1922.         call    PrintAX
  1923.         dec     di
  1924.          jz     @@Eoln
  1925.  
  1926.         add     cx,8
  1927.         call    Space
  1928.          jmp    @@testtoken
  1929. @@Eoln:
  1930.         mov     dx, offset CRLF
  1931.         mov     ah,9
  1932.         int     21h
  1933.          jmp    @@print3
  1934. @@done:
  1935.         pop     di si dx cx bx
  1936.  
  1937.         mov     ax,4C00h
  1938.         int     21h
  1939.  
  1940.         ret
  1941. ListKeys        endp
  1942.  
  1943. PrintAX proc    near
  1944.         push    dx si di
  1945.  
  1946.         mov     si,ax
  1947.         mov     bx,10
  1948.         mov     di,cx
  1949.         xor     cx,cx
  1950. @@next:
  1951.         mov     ax,si
  1952.         xor     dx,dx
  1953.         div     bx
  1954.         mov     si,ax
  1955.         mov     ah,2
  1956.         add     dl,'0'
  1957.         push    dx
  1958.         inc     cx
  1959.         or      si,si
  1960.          jnz    @@next
  1961.  
  1962.         sub     di,cx
  1963.          jbe    @@popDL
  1964.  
  1965.         xchg    cx,di
  1966.         call    Space
  1967.         xchg    cx,di
  1968. @@popDL:
  1969.         pop     dx
  1970.         mov     ah,2
  1971.         int     21h
  1972.          loop   @@popDL
  1973.  
  1974.         mov     cx,di
  1975.         pop     di si dx
  1976.         ret
  1977. PrintAX endp
  1978.  
  1979. Space   proc
  1980.  
  1981.          jcxz   @@done
  1982. @@space:
  1983.         mov     dl,' '
  1984.         mov     ah,2
  1985.         int     21h
  1986.          loop   @@space
  1987. @@done:
  1988.         ret
  1989.  
  1990. Space   endp
  1991.  
  1992. ProgramEnd label byte
  1993.  
  1994. CODE ENDS
  1995.         END start
  1996.  
  1997.