home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / pcmagazi / 1990 / 02 / batchman.asm < prev    next >
Assembly Source File  |  1989-12-10  |  92KB  |  1,923 lines

  1. ; DADA dada DADA dada DADA dada ... BATCHMAN!
  2. ;-----------------------------------------------;
  3. ;  BATCHMAN * PC Magazine * Michael J. Mefford  ;
  4. ;  Batch file enhancer.                         ;
  5. ;-----------------------------------------------;
  6.  
  7. BIOS_DATA      SEGMENT AT 40H
  8.                ORG     1AH
  9. BUFFER_HEAD    DW      ?
  10. BUFFER_TAIL    DW      ?
  11.                ORG     80H
  12. BUFFER_START   DW      ?
  13. BUFFER_END     DW      ?
  14. BIOS_DATA      ENDS
  15.  
  16. BOOT_SEG       SEGMENT AT 0FFFFH
  17. RESET          LABEL   WORD
  18.                ORG     05H
  19. DATE_STAMP     DB      ?
  20. BOOT_SEG       ENDS
  21.  
  22. _TEXT          SEGMENT PUBLIC 'CODE'
  23.                ASSUME  CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT
  24.                ORG     100H
  25. START:         JMP     MAIN
  26.  
  27. ;              DATA AREA
  28. ;              ---------
  29.                DB      CR,SPACE,SPACE,SPACE,CR,LF
  30.  
  31. COPYRIGHT      DB      "BATCHMAN 1.0 (c) 1989 Ziff Communications Co. ",BOX
  32. BATCHMAN_DATA  LABEL   BYTE
  33. PROGRAMMER     DB      " PC Magazine ",BOX," Michael J. Mefford",CR,CR,CR
  34.  
  35. DB 3 DUP (TAB),"SOCK!  KRUNCH!  BANG!  BOOM!  ZOWIE! ... BATCHMAN!",CR,CR
  36. DB 3 DUP (TAB),"Syntax: BATCHMAN [command] [arguments] [/R]",CR,CR
  37. DB 4 DUP (TAB),"/R = Display ErrorLevel",CR,CR
  38. DB 5 DUP (TAB),"Commands",CR
  39. DB 5 DUP (TAB),"~~~~~~~~",CR,CR
  40. DB 4 DUP (TAB),"EL = DOS ErrorLevel",CR,0
  41.  
  42. HELP1          LABEL   BYTE
  43. DB " CLS [nn]  nn[H]=color H=hex",9,9,"CECHO [C] [nn,]string  nn=color;C=no CR"
  44. DB CR
  45. DB " SETLOOP n  n=loops (0-255)",9,9,"DEC  decrements SETLOOP  EL=SETLOOP",CR
  46. DB " QFORMAT [d:] [N] d:=A: or B: N=No ask",9,"BREAK  EL=1 if break ON",CR
  47. DB " PUSHPATH  EL=0 if successful",9,9,"POPPATH  EL=0 if successful",CR
  48. DB " ANSI  EL=0 if installed",9,9,"BEEP [m,n[;m,n]...]  m=freq. n=1/18 sec",CR
  49. DB " WAITTIL hh:mm[:ss]",9,9,9,"WAITFOR [mm:]ss",CR
  50. DB " CURSORTYPE m,n  m=start; n=stop line",9,"DRIVEEXIST d:  EL=1 if exist",CR
  51. DB " DIREXIST directory  EL=1 if exist",9,"ISVOL [d:]volume  EL=1 if exist",CR
  52. DB 0
  53.  
  54. HELP2          LABEL   BYTE
  55. DB " YEAR  EL=year from 1980 (0-199)",9,"MONTH  EL=(1-12)",CR
  56. DB " DAY   EL=(1-31)",9,9,9,"WEEKDAY  EL=(0-6) Sun=0; Sat=6",CR
  57. DB " HOUR  EL=(0-23)",9,9,9,"MINUTE  EL=(0-59)",CR
  58. DB " SECOND EL=(0-59)",9,9,9,"VIDEOMODE  EL=(0-19)",CR
  59. DB " ROWS  EL=display rows",9,9,9,"COLS  EL=display columns",CR
  60. DB " SETCURSOR m,n  m=row; n=col",9,9,"E43V50",CR
  61. DB " PRTSC [F]  F=formfeed ",9,9,9,"COMPARE string string  EL=0 if match",CR
  62. DB " CANCOPY filespec [d:]  EL=0 if room to copy",CR,0
  63.  
  64. HELP3          LABEL   BYTE
  65. DB " WARMBOOT",9,9,9,9,"COLDBOOT",CR
  66. DB " SHIFT ALT | CTRL  EL=1 if depressed",9,"NUMLOCK [ON | OFF]",CR
  67. DB " CAPSLOCK [ON | OFF]",9,9,9,"SCROLLOCK [ON | OFF]",CR
  68. DB " RENDIR old new  EL=0 if successful",9,"ROMDATE  display BIOS date",CR
  69. DB " GETKEY ['string' n] n=Function key EL=position; EL=scan code if no list",CR
  70. DB " DOSVER  EL=x where x=(major*32)+minor; eg. DOS 3.30=(3*32+30)=126",CR
  71. DB " MAINMEM n | R  main memory;  n=K bytes needed; EL=0 if enough; R=report",CR
  72. DB " EXPMEM n | R  expanded memory",9,9,"EXTMEM n | R  extended memory",CR
  73. DB 0
  74.  
  75. HELP4          LABEL   BYTE
  76. DB " DISPLAY  EL=",9,9,9,9,             "CPU  EL=",CR
  77. DB "  1=MDA         7=VGA mono",9,9,    " 1=8086/8088   3=80286",CR
  78. DB "  2=CGA         8=VGA color",9,9,   " 2=80186       4=80386",CR
  79. DB "  4=EGA color  11=MCGA mono",CR
  80. DB "  5=EGA mono   12=MCGA color",CR
  81. DB "  6=PGS",CR,0
  82.  
  83. HELP5          LABEL   BYTE
  84. DB " WINDOW m,n,w,h[,c,b]",CR
  85. DB "  m=row; n=col; w=width; h=height; c=color; b= -/= single/double border",CR
  86. DB CR
  87. DB " TYPEMATIC [m,n | N]",CR
  88. DB "  m=typematic rate (0 - 31); larger m=faster rate",CR
  89. DB "  n=initial delay  (0 - 3);  larger n=longer delay",CR
  90. DB "  N=normal:  m=20; n=1",CR
  91. DB "  default:   m=27; n=0",CR,0
  92.  
  93. DB CTRL_Z
  94.  
  95. MORE DB 3 DUP (TAB),"any key for more; ESC to quit",CR
  96.  
  97. PCMAG_LOGO     LABEL   BYTE
  98. DB   218,17 DUP (196),191,CR
  99. DB   195,196,6 DUP (219),220,196,220,5 DUP (219),220,196,180,CR
  100. DB   195,4 DUP (196,3 DUP (219)),196,180,CR
  101. DB   195,4 DUP (196,3 DUP (219)),196,180,CR
  102. DB   195,3 DUP (196,3 DUP (219)),5 DUP (196),180,CR
  103. DB   195,196,6 DUP (219),223,196,3 DUP (219),5 DUP (196),180,CR
  104. DB   195,196,3 DUP (219),5 DUP (196),2 DUP (3 DUP (219),196),180,CR
  105. DB   195,196,3 DUP (219),5 DUP (196),2 DUP (3 DUP (219),196),180,CR
  106. DB   195,196,3 DUP (219),5 DUP (196),223,5 DUP (219),223,196,180,CR
  107. DB   195,17 DUP (196),180,CR
  108. DB   179," M A G A Z I N E ",179,CR
  109. DB   192,17 DUP (196),217,CR,0
  110.  
  111. DIR_COUNT      =       6                 ;Increase this for more PATH stack.
  112. DIR_SPACE      =       65 + 3
  113. LOOP_COUNT     =       OFFSET BATCHMAN_DATA
  114. CURRENT_DIR    =       LOOP_COUNT + 1
  115. FIRST_DIR      =       CURRENT_DIR + 2
  116. STACK_TOP      =       FIRST_DIR + (DIR_COUNT * DIR_SPACE)
  117. LAST_DIR       =       STACK_TOP - DIR_SPACE
  118.  
  119. CR             EQU     13
  120. LF             EQU     10
  121. FF             EQU     12
  122. TAB            EQU     9
  123. CTRL_Z         EQU     26
  124. SPACE          EQU     32
  125. BOX            EQU     254
  126. DOUBLE_QUOTE   EQU     34
  127. SINGLE_QUOTE   EQU     39
  128. COMMA          EQU     ","
  129. ESC_SCAN       EQU     1
  130. Y_SCAN         EQU     15H
  131. ENTER_SCAN     EQU     1CH
  132. WHITE_ON_BLACK EQU     07H
  133. BLACK_ON_WHITE EQU     70H
  134. WHITE_ON_BLUE  EQU     17H
  135. BLUE_ON_WHITE  EQU     71H
  136. INTENSE_ON_RED EQU     4FH
  137. INTENSE        EQU     0FH
  138.  
  139. REPEAT_MAX     EQU     31
  140. REPEAT_NORMAL  EQU     REPEAT_MAX - 20
  141. REPEAT_DEFAULT EQU     27
  142. INIT_MAX       EQU     3
  143. INIT_NORMAL    EQU     1
  144. INIT_DEFAULT   EQU     0
  145.  
  146. BIOS_ACTIVE_PAGE       EQU     62H
  147. ACTIVE_PAGE    DB      ?
  148. BIOS_CRT_MODE          EQU     49H
  149. CRT_MODE       DB      ?
  150. CRT_COLS       DW      ?
  151. CRT_LEN        DW      ?
  152. CRT_START      DW      ?
  153. CRT_DATA_LENGTH        EQU     $ - CRT_MODE
  154. CURSOR_POSN    LABEL   WORD
  155. CURSOR_COL     DB      ?
  156. CURSOR_ROW     DB      ?
  157. BIOS_CRT_ROWS  EQU     84H
  158. CRT_ROWS       DB      ?
  159.  
  160. DOS_VERSION    LABEL   WORD
  161. DOS_MAJOR      DB      ?
  162. DOS_MINOR      DB      ?
  163. REPORT_FLAG    DB      0                       ;Equals 1 if Echo EL to CON.
  164. TSR            DB      0                       ;Equals 1 if need to TSR data.
  165. DATA_SEG       DW      0                       ;Non-zero if data resident.
  166. CURSOR_FLAG    DB      0                       ;Set to 1 if CECHO skips CR.
  167.  
  168. CHAR_CNT       DW      0                       ;Count of GETKEY character keys.
  169. FUNC_CNT       DW      0                       ;Count of GETKEY function keys.
  170. KBD_TYPE       DB      0                       ;0=standard KBD; 10h=extended.
  171.  
  172. ALT            DB      "ALT"
  173. CTRL           DB      "CTRL"
  174. KBD_FLAG       EQU     17H
  175. CTRL_SHIFT     EQU     04H
  176. ALT_SHIFT      EQU     08H
  177. SCROLL_STATE   EQU     10H
  178. NUM_STATE      EQU     20H
  179. CAPS_STATE     EQU     40H
  180.  
  181. DRIVE          DB      ?
  182. SECTOR         DW      ?
  183. FAT_BYTES      DW      ?
  184.  
  185. SPACES         DB      7 DUP (SPACE)
  186. SINGLE_BOX     DB      218,196,191,179,192,196,217
  187. DOUBLE_BOX     DB      201,205,187,186,200,205,188
  188. WIN_WIDTH      DW      ?
  189. WIN_HEIGHT     DW      ?
  190. WIN_CURSOR     DW      ?
  191.  
  192. MATCHING       STRUC
  193. RESERVED       DB      21 DUP (?)
  194. ATTRIBUTE      DB              ?
  195. FILE_TIME      DW              ?
  196. FILE_DATE      DW              ?
  197. SIZE_LOW       DW              ?
  198. SIZE_HIGH      DW              ?
  199. FILE_NAME      DB      13 DUP (?)
  200. MATCHING       ENDS
  201.  
  202. C_NOTE         EQU     1046
  203. CON            DB      "CON"
  204. CON_OFFSET     EQU     10
  205. ANSICOM        DB      CR,SPACE,SPACE,SPACE,CR,LF,"ANSI"
  206. ANSICOM_LENGTH EQU     $ - ANSICOM
  207. EMM            DB      "EMMXXXX0"
  208. EMM_LENGTH     EQU     $ - EMM
  209. BYTES_FREE     DB      "K Bytes free",CR,LF,"$"
  210. ON             DB      "ON"
  211. OFF            DB      "OFF"
  212.  
  213. FORMAT_PROMPT1 DB CR,LF,"WARNING: All data of drive $"
  214. FORMAT_PROMPT2 DB " will be permanently lost.",CR,LF
  215.                DB "Do you wish to continue?  Y/N$"
  216. FORMAT_PROMPT3 DB CR,LF,"Place the disk you wish to Quick Format in drive $"
  217. FORMAT_PROMPT4 DB " and press Enter.",CR,LF
  218.                DB "Note: the disk must have been previously formatted by DOS$"
  219.  
  220. COMMANDS LABEL BYTE; Format: length,"command"
  221. DB 3,"CLS",        5,"CECHO",      8,"PUSHPATH",   7,"POPPATH",    7,"SETLOOP"
  222. DB 3,"DEC",        7,"QFORMAT",    7,"WEEKDAY",    3,"DAY",        5,"MONTH"
  223. DB 4,"YEAR",       4,"HOUR",       6,"MINUTE",     6,"SECOND",     5,"BREAK"
  224. DB 4,"ROWS",       4,"COLS",       9,"VIDEOMODE",  7,"COMPARE",    7,"CANCOPY"
  225. DB 9,"SETCURSOR",  8,"WARMBOOT",   8,"COLDBOOT",   4,"BEEP",       4,"ANSI"
  226. DB 3,"CPU",        7,"DISPLAY",    7,"MAINMEM",    6,"EXTMEM",     6,"EXPMEM"
  227. DB 7,"WAITFOR",    7,"WAITTIL",    6,"GETKEY",     5,"SHIFT",      9,"SCROLLOCK"
  228. DB 7,"NUMLOCK",    8,"CAPSLOCK",  10,"DRIVEEXIST", 5,"ISVOL",      8,"DIREXIST"
  229. DB 9,"TYPEMATIC", 10,"CURSORTYPE", 5,"PRTSC",      6,"DOSVER",     6,"WINDOW"
  230. DB 7,"ROMDATE",    6,"RENDIR",     6,"E43V50"
  231. DB   0
  232.  
  233. DISPATCH_TABLE LABEL WORD
  234. DW    CLS,            CECHO,          PUSHPATH,       POPPATH,        SETLOOP
  235. DW    DECLOOP,        QFORMAT,        WEEKDAY,        DAY,            MONTH
  236. DW    YEAR,           HOUR,           MINUTE,         SECOND,         BREAK
  237. DW    ROWS,           COLS,           VIDEOMODE,      COMPARE,        CANCOPY
  238. DW    SETCURSOR,      WARMBOOT,       COLDBOOT,       BEEP,           ANSI
  239. DW    CPU,            DISPLAYS,       MAINMEM,        EXTMEM,         EXPMEM
  240. DW    WAITFOR,        WAITTIL,        GETKEY,         SHIFT,          SCROLLOCK
  241. DW    NUMLOCK,        CAPSLOCK,       DRIVEEXIST,     ISVOL,          DIREXIST
  242. DW    TYPEMATIC,      CURSORTYPE,     PRTSC,          DOSVER,         WINDOW
  243. DW    ROMDATE,        RENDIR,         E43V50
  244.  
  245. TWELVE_BIT_FAT         EQU     4087
  246.  
  247. BOOT_SECTOR            STRUC
  248. JUMP                   DB      3 DUP(?)
  249. OEM                    DB      8 DUP(?)
  250. BYTES_PER_SECTOR       DW      ?
  251. SECTORS_PER_CLUSTER    DB      ?
  252. RESERVED_SECTORS       DW      ?
  253. NUMBER_OF_FATS         DB      ?
  254. ROOT_DIRECTORY_ENTRIES DW      ?
  255. TOTAL_SECTORS          DW      ?
  256. MEDIA_DESCRIPTOR_BYTE  DB      ?
  257. SECTORS_PER_FAT        DW      ?
  258. BOOT_SECTOR            ENDS
  259.  
  260. ;              CODE AREA
  261. ;              ---------
  262. MAIN           PROC    NEAR
  263.  
  264.                CLD                             ;String instructions forward.
  265.                MOV     AH,30H
  266.                INT     21H
  267.                MOV     DOS_VERSION,AX          ;Get DOS version and save.
  268.                MOV     BX,64 / 16 * 1024       ;Minimum of 64K required.
  269.                MOV     AH,4AH                  ;Request via DOS.
  270.                INT     21H
  271.                MOV     AL,1                    ;Assume not available.
  272.                JC      CK_TSR                  ;Exit if not.
  273.                MOV     SP,0FFFEH               ;Else, setup stack at end of seg.
  274.                CALL    GET_BIOS_DATA           ;Get BIOS video data and store.
  275.                MOV     DX,OFFSET DTA           ;Set Disk Transfer Address at
  276.                MOV     AH,1AH                  ; end of code so user parameters
  277.                INT     21H                     ; not spoiled by DOS calls.
  278.  
  279.                MOV     SI,81H                  ;Point to parameters.
  280. NEXT_SWITCH:   LODSB                           ;Get a byte.
  281.                CMP     AL,CR                   ;End?
  282.                JZ      PARSE                   ;If yes, done here.
  283.                CMP     AL,"/"                  ;Else, switch character?
  284.                JNZ     NEXT_SWITCH             ;If no, check next byte.
  285.                LODSB                           ;Else, get the char.
  286.                AND     AL,5FH                  ;Capitalize.
  287.                CMP     AL,"R"                  ;Is it /Report errorlevel switch?
  288.                JNZ     PARSE                   ;If no, done here.
  289.                MOV     REPORT_FLAG,1           ;Else, flag report for exit.
  290.  
  291. PARSE:         MOV     SI,81H                  ;Point to parameters again.
  292.                CALL    PARSE_DELIMIT           ;Parse off any delimiting chars.
  293.                MOV     BP,SI                   ;Save start of COMMAND.
  294.                CALL    CAPITALIZE              ;Capitalize COMMAND.
  295.  
  296.                XOR     CX,CX                   ;COMMAND length offset index.
  297.                XOR     BX,BX                   ;COMMAND dispatch index.
  298.                MOV     SI,OFFSET COMMANDS      ;COMMAND table pointer.
  299. NEXT_COMMAND:  INC     BX                      ;Add two for word offset.
  300.                INC     BX
  301.                ADD     SI,CX                   ;Add length to COMMAND pointer.
  302.                MOV     DI,BP                   ;Point to user requested COMMAND.
  303.                LODSB                           ;Get COMMAND length.
  304.                OR      AL,AL                   ;End of legal COMMANDs?
  305.                JZ      HELP_EXIT               ;If yes, exit with help msgs.
  306.                MOV     CL,AL                   ;Else, length in CX.
  307.                REPZ    CMPSB                   ;Compare COMMAND table with entry
  308.                JNZ     NEXT_COMMAND            ;If no match, try next command.
  309.                MOV     SI,DI                   ;Else, match; SI gets pointer.
  310. FIND_DELIMIT:  LODSB                           ;Parse off any COMMAND trailing
  311.                CMP     AL,SPACE                ; chars.
  312.                JA      FIND_DELIMIT
  313.                DEC     SI                      ;Adjust pointer.
  314.                CALL    PARSE_DELIMIT           ;Parse off any delimiting chars.
  315.                CALL    DISPATCH_TABLE[BX - 2]  ;Call the command function.
  316.                JMP     SHORT CK_REPORT         ;Check ErrorLevel report flag.
  317.  
  318. HELP_EXIT:     CALL    DISPLAY_HELP            ;Display list of valid COMMANDS.
  319.                XOR     AL,AL                   ;ErrorLevel = 0
  320.  
  321. CK_REPORT:     PUSH    CS                      ;Restore data segment.
  322.                POP     DS
  323.                CMP     REPORT_FLAG,1           ;ErrorLevel flag set?
  324.                JNZ     CK_TSR                  ;If no, done here.
  325.                XOR     AH,AH                   ;Else, zero in high half.
  326.                CALL    DEC_OUTPUT              ;Display the ErrorLevel.
  327.  
  328. CK_TSR:        PUSH    AX                      ;Preserve ErrorLevel.
  329.                CMP     TSR,1                   ;Are we to remain resident?
  330.                JNZ     CK_RELEASE              ;If no, done here.
  331.                MOV     AX,DS:[2CH]             ;Else, get environment segment.
  332.                MOV     ES,AX
  333.                MOV     AH,49H                  ;Free up environment.
  334.                INT     21H
  335.  
  336.                MOV     DX,OFFSET STACK_TOP     ;Resident PATH stack top.
  337.                ADD     DX,15                   ;Round up to paragraph.
  338.                MOV     CL,4
  339.                SHR     DX,CL                   ;Convert to paragraphs.
  340.                POP     AX                      ;Retrieve ErrorLevel.
  341.                MOV     AH,31H
  342.                INT     21H                     ;Terminate but stay resident.
  343.  
  344. CK_RELEASE:    CMP     DATA_SEG,0              ;Did we find our resident data?
  345.                JZ      EXIT                        ;If no, done here.
  346.                CMP     ES:WORD PTR CURRENT_DIR,0   ;Else, PATH stack empty?
  347.                JNZ     EXIT                        ;If no, exit.
  348.                CMP     ES:BYTE PTR LOOP_COUNT,0    ;Else, LOOP counter zero?
  349.                JNZ     EXIT                        ;If no, exit.
  350.  
  351. RELEASE:       MOV     AH,49H                  ;Else, release resident data
  352.                INT     21H                     ; back to memory pool.
  353.  
  354. EXIT:          POP     AX                      ;Retrieve ErrorLevel.
  355.                MOV     AH,4CH                  ;Terminate.
  356.                INT     21H
  357.  
  358. MAIN           ENDP
  359.  
  360. ; C * O * M * M * A * N * D * S
  361.  
  362. CLS:           CALL    GET_COLOR
  363.                CALL    FIND_HEX                ;Get argument.
  364.                JNZ     DO_CLS
  365.                MOV     AL,BL                   ;Use cursor color.
  366. DO_CLS:        MOV     BH,AL                   ;Attribute.
  367.                XOR     AL,AL                   ;Scroll entire window.
  368.                XOR     CX,CX                   ;Top left corner.
  369.                MOV     DH,CRT_ROWS             ;Bottom right row and
  370.                MOV     DL,BYTE PTR CRT_COLS    ; column.
  371.                DEC     DL                      ;Adjust logic.
  372.                MOV     AH,6                    ;Scroll active page.
  373.                INT     10H
  374.                XOR     DX,DX                   ;Home the cursor.
  375.                MOV     CURSOR_POSN,DX          ;Save it.
  376.                CALL    SET_CURSOR
  377.                XOR     AL,AL                   ;EL = 0.
  378.                RET
  379.  
  380. GET_COLOR:     XOR     BH,BH
  381.                MOV     AH,8
  382.                INT     10H
  383.                MOV     BL,AH
  384.                RET
  385.  
  386. ;----------------------------------------------;
  387. CECHO:         MOV     AL,BYTE PTR [SI]
  388.                AND     AL,5FH
  389.                CMP     AL,"C"
  390.                JNZ     CECHO_COLOR
  391.                CMP     BYTE PTR [SI + 1],SPACE
  392.                JA      CECHO_COLOR
  393.                INC     SI
  394.                INC     SI
  395.                MOV     CURSOR_FLAG,1           ;Else, flag skip CR,LF.
  396. CECHO_COLOR:   CALL    GET_COLOR
  397.                CALL    FIND_HEX
  398.                JZ      DO_CECHO
  399.                MOV     BL,AL                   ;Store in BL as color.
  400.                INC     SI
  401. DO_CECHO:      MOV     DX,CURSOR_POSN          ;Retrieve cursor position.
  402. NEXT_STRING:   LODSB                           ;Get a string byte.
  403.                CMP     AL,CR                   ;End of string?
  404.                JZ      COLOR_DONE              ;If yes, done here.
  405.                MOV     CX,1                    ;Else, one char to write.
  406.                CMP     AL,TAB                  ;Is character a TAB?
  407.                JNZ     DO_CHAR                 ;If no, process normally.
  408.                MOV     CX,DX                   ;Else, expand TAB to
  409.                AND     CX,7                    ; appropriate space characters.
  410.                NEG     CX
  411.                ADD     CX,8
  412. NEXT_CHAR:     MOV     AL,SPACE                ;And tab over with spaces.
  413. DO_CHAR:       CALL    ATTRIB_CHAR
  414.                LOOP    DO_CHAR
  415.                JMP     NEXT_STRING
  416.  
  417. ATTRIB_CHAR:   PUSH    CX
  418.                MOV     BH,ACTIVE_PAGE          ;Active video page.
  419.                MOV     CX,1                    ;Write Attribute/Character
  420.                MOV     AH,9                    ; at current cursor position
  421.                INT     10H                     ; via BIOS.
  422.                CALL    CK_ROW                  ;See if line wrap.
  423.                POP     CX
  424.                RET
  425.  
  426.  
  427. COLOR_DONE:    CMP     CURSOR_FLAG,1           ;Should we move cursor to new
  428.                JZ      COLOR_END               ; line?  If no, done here.
  429.                MOV     DL,254                  ;Else, fake large column length.
  430. CK_ROW:        INC     DL                      ;Next column.
  431.                CMP     DL,BYTE PTR CRT_COLS    ;Last video displayable columns?
  432.                JB      MOVE_CURSOR             ;If no, move cursor right.
  433.                INC     DH                      ;Else, next row.
  434.                XOR     DL,DL                   ;First column.
  435.                CMP     DH,CRT_ROWS             ;Bottom of displayable rows?
  436.                JBE     MOVE_CURSOR             ;If no, move cursor down.
  437.                CMP     CURSOR_FLAG,1
  438.                JZ      COLOR_END
  439.                DEC     DH                      ;Else, stay on last row.
  440.                PUSH    DX                      ;Save it.
  441.                XOR     CX,CX                   ;Top left.
  442.                MOV     DL,BYTE PTR CRT_COLS    ;Bottom right.
  443.                MOV     BH,WHITE_ON_BLACK
  444.                MOV     AX,601H
  445.                INT     10H
  446.                POP     DX
  447. MOVE_CURSOR:   CALL    SET_CURSOR              ;Else set cursor to new position.
  448. COLOR_END:     XOR     AL,AL                   ;EL = 0.
  449.                RET
  450.  
  451. ;----------------------------------------------;
  452. PUSHPATH:      CALL    CK_BAT_DATA             ;Search for our resident data.
  453.                PUSH    ES                      ;DS = ES.
  454.                POP     DS
  455.                MOV     SI,[CURRENT_DIR]        ;Get pointer to last PUSH.
  456.                OR      SI,SI                   ;Is this the first?
  457.                JNZ     FIND_IT                 ;If no, find end of last.
  458.                MOV     SI,OFFSET FIRST_DIR     ;Else, use storage start.
  459.                JMP     SHORT GOT_SPACE
  460. FIND_IT:       INC     SI                      ;Adjust.
  461. FIND_PUSH:     LODSB                           ;Look for end.
  462.                OR      AL,AL                   ;Zero marks end.
  463.                JNZ     FIND_PUSH               ;If not end, keep looking.
  464.                CMP     SI,OFFSET LAST_DIR      ;Enough room?
  465.                MOV     AL,1                    ;EL = 1 if fails.
  466.                JAE     PUSHPATH_END            ;If not enough, exit with error.
  467. GOT_SPACE:     MOV     DS:[CURRENT_DIR],SI     ;Else, store new PATH start.
  468.                MOV     DI,SI
  469.                MOV     AH,19H                  ;Get current drive.
  470.                INT     21H
  471.                ADD     AL,"A"                  ;Convert to ASCII.
  472.                STOSB                           ;Store it.
  473.                MOV     AL,"\"                  ;Store root delimiter.
  474.                STOSB
  475.                MOV     SI,DI                   ;Tack on current directory.
  476.                XOR     DL,DL
  477.                MOV     AH,47H
  478.                INT     21H
  479.                PUSH    CS                      ;Restore data segment.
  480.                POP     DS
  481.                CMP     DATA_SEG,0              ;Did we store in resident data?
  482.                JNZ     PUSHPATH_DONE           ;If yes, done here.
  483.                MOV     TSR,1                   ;Else, flag to TSR the data.
  484. PUSHPATH_DONE: XOR     AL,AL                   ;ErrorLevel = 0.
  485. PUSHPATH_END:  RET
  486.  
  487. ;----------------------------------------------;
  488. POPPATH:       CALL    CK_BAT_DATA             ;Search for our resident data.
  489.                PUSH    ES                      ;DS = ES.
  490.                POP     DS
  491.                MOV     SI,[CURRENT_DIR]        ;Get pointer to last PUSH.
  492.                OR      SI,SI                   ;Is there one on the stack?
  493.                MOV     AL,1                    ;Assume no; ErrorLevel = 1.
  494.                JZ      POPPATH_END             ;If none assume right, exit.
  495.                MOV     DL,BYTE PTR [SI]        ;Else, retrieve drive.
  496.                SUB     DL,"A"                  ;Convert to DOS format.
  497.                MOV     AH,0EH                  ;Select disk.
  498.                INT     21H
  499.                MOV     DX,SI
  500.                INC     DX
  501.                MOV     AH,3BH                  ;Change current directory.
  502.                INT     21H
  503.                CMP     SI,OFFSET FIRST_DIR     ;This the last on stack?
  504.                JNZ     GET_POP                 ;If no, pop stack.
  505.                XOR     SI,SI                   ;Else, use zero to flag empty.
  506.                JMP     SHORT SAVE_POP
  507. GET_POP:       DEC     SI                      ;Move point past ending zero
  508.                DEC     SI                      ; of next POP.
  509.                STD                             ;Search backwards of start.
  510. FIND_POP:      LODSB
  511.                OR      AL,AL                   ;Zero marks end.
  512.                JZ      FOUND_POP               ;If found, done.
  513.                CMP     SI,OFFSET FIRST_DIR     ;Else if only POP, done.
  514.                JAE     FIND_POP                ;Else, search until found.
  515.                DEC     SI                      ;Adjust.
  516.  
  517. FOUND_POP:     INC     SI                      ;Point to start.
  518.                INC     SI
  519. SAVE_POP:      MOV     DS:[CURRENT_DIR],SI     ;Save next POP.
  520.                CLD                             ;Direction flag back forward.
  521.                XOR     AL,AL                   ;ErrorLevel = 0.
  522. POPPATH_END:   RET
  523.  
  524. ;----------------------------------------------;
  525. SETLOOP:       CALL    DECIMAL_INPUT           ;Get argument.
  526.                PUSH    AX                      ;Save it.
  527.                CALL    CK_BAT_DATA             ;Search for our resident data.
  528.                POP     AX                      ;Retrieve argument.
  529.                MOV     ES:[LOOP_COUNT],AL      ;Store loop count request.
  530.                OR      AL,AL                   ;If set to zero, don't TSR.
  531.                JZ      SETLOOP_END
  532.                CMP     DATA_SEG,0              ;Did we find resident data?
  533.                JNZ     SETLOOP_END             ;If yes, done here.
  534.                MOV     TSR,1                   ;Else, flag to TSR.
  535. SETLOOP_END:   XOR     AL,AL                   ;ErrorLevel = 0
  536.                RET
  537.  
  538. ;----------------------------------------------;
  539. DECLOOP:       CALL    CK_BAT_DATA             ;Search for our resident data.
  540.                MOV     AL,ES:BYTE PTR [LOOP_COUNT]  ;Retrieve loop counter.
  541.                OR      AL,AL                        ;Is it zero?
  542.                JZ      DEC_END                      ;If yes, done here.
  543.                DEC     AL                           ;Else, decrement it.
  544.                MOV     ES:BYTE PTR [LOOP_COUNT],AL  ;Store it.
  545. DEC_END:       RET
  546.  
  547. ;----------------------------------------------;
  548. QFORMAT:       XOR     BP,BP                   ;Flag for prompt.
  549.                MOV     AH,19H                  ;Get default drive.
  550.                INT     21H
  551.                ADD     AL,"A"                  ;Convert to ASCII.
  552.                MOV     BL,AL                   ;Save in BL.
  553. NEXT_QFORMAT:  CALL    PARSE_DELIMIT
  554.                CALL    CAPITALIZE
  555.                LODSB
  556.                CMP     AL,SPACE                ;Anything there?
  557.                JBE     CK_DRIVE
  558.                CMP     AL,"N"                  ;No ask switch?
  559.                JNZ     SAVE_DRIVE              ;If no, check drive letter.
  560.                INC     BP                      ;Else, flag no prompt.
  561.                JMP     SHORT NEXT_QFORMAT
  562. SAVE_DRIVE:    MOV     BL,AL                   ;Save drive letter.
  563.                LODSB
  564.                CMP     AL,":"                  ;Drive specifier there?
  565.                JZ      NEXT_QFORMAT            ;If yes, continue
  566.                JMP     FAIL_FORMAT             ;Else, abort.
  567.  
  568. CK_DRIVE:      CMP     BL,"A"                  ;As safety precaution, abort
  569.                JZ      GOOD_DRIVE              ; if not drive A or B.
  570.                CMP     BL,"B"
  571.                JZ      GOOD_DRIVE
  572.                JMP     FAIL_FORMAT
  573. GOOD_DRIVE:    OR      BP,BP                    ;Should we skip prompt?
  574.                MOV     BP,BX                    ;Save ASCII drive letter.
  575.                JNZ     READ_BOOT                ;If no, skip prompt?
  576.                MOV     DX,OFFSET FORMAT_PROMPT1 ;Display warning.
  577.                CALL    PRINT_STRING
  578.                MOV     DX,BP                    ;And drive letter.
  579.                CALL    PRINT_CHAR
  580.                MOV     DX,OFFSET FORMAT_PROMPT2 ;Rest of warning.
  581.                CALL    PRINT_STRING
  582.                CALL    KEYPRESS                 ;Get a response.
  583.                CMP     AH,Y_SCAN                ;If "Y" yes, continue.
  584.                JZ      ENTER_PROMPT
  585.                JMP     FAIL_FORMAT              ;Else, abort.
  586. ENTER_PROMPT:  MOV     DX,OFFSET FORMAT_PROMPT3 ;Prompt user to place disk in
  587.                CALL    PRINT_STRING             ; drive and press Enter.
  588.                MOV     DX,BP
  589.                CALL    PRINT_CHAR
  590.                MOV     DX,OFFSET FORMAT_PROMPT4
  591.                CALL    PRINT_STRING
  592.                CALL    KEYPRESS                ;Get response.
  593.                CMP     AH,ENTER_SCAN           ;Enter pressed?
  594.                JZ      READ_BOOT               ;If yes, continue.
  595.                JMP     FAIL_FORMAT             ;Else, abort.
  596.  
  597. READ_BOOT:     SUB     BP,"A"                  ;Convert drive letter to logical.
  598.                MOV     AX,BP
  599.                MOV     DRIVE,AL                ;Save.
  600.                MOV     CX,1                    ;Direct sector read boot sector.
  601.                XOR     DX,DX
  602.                MOV     BX,OFFSET READ_BUFFER
  603.                INT     25H                     ; preserve logical drive.
  604.                POP     AX                      ;Call leaves word on stack; fix.
  605.                JNC     CK_BOOT                 ;If successful read, continue.
  606. LILLY_FAIL:    JMP     FAIL_FORMAT             ;Else, exit with error.
  607.  
  608. CK_BOOT:       CMP     BYTE PTR READ_BUFFER,0EBH      ;Is first byte short jump?
  609.                JZ      CK_SIGN                        ;If yes, continue.
  610.                CMP     BYTE PTR READ_BUFFER,0E9H      ;Else, is it long jump?
  611.                JNZ     LILLY_FAIL                     ;If no, not valid.
  612. CK_SIGN:       MOV     BX,READ_BUFFER.BYTES_PER_SECTOR
  613.                CMP     WORD PTR READ_BUFFER[BX-2],0AA55H ;Boot sector signature.
  614.                JNZ     LILLY_FAIL
  615. READ_FAT:      MOV     AL,DRIVE                         ;Logical drive.
  616.                MOV     CX,READ_BUFFER.SECTORS_PER_FAT   ;Sectors per FAT.
  617.                CMP     CX,80                            ;If too many sectors
  618.                JBE     FAT_OK                           ; BIOS block no good.
  619.                JMP     FAIL_FORMAT
  620. FAT_OK:        MOV     DX,READ_BUFFER.RESERVED_SECTORS  ;Starting FAT sector.
  621.                MOV     SECTOR,DX                        ;Save starting sector.
  622.                MOV     BX,OFFSET WRITE_BUFFER           ;Storage for FAT.
  623.                INT     25H
  624.                POP     AX
  625.                JNC     ZERO_FAT
  626.                JMP     FAIL_FORMAT
  627.  
  628. ZERO_FAT:      MOV     AX,READ_BUFFER.SECTORS_PER_FAT
  629.                MUL     READ_BUFFER.BYTES_PER_SECTOR
  630.                MOV     FAT_BYTES,AX                     ;Bytes per sector.
  631.  
  632.                MOV     CX,FAT_BYTES
  633.                SUB     CX,3                             ;Adjust for descriptors.
  634.                MOV     SI,OFFSET WRITE_BUFFER + 3       ;Skip media descriptor.
  635.                MOV     DI,SI
  636. NEXT_CLUSTER:  LODSW                           ;Get a FAT word.
  637.                MOV     BX,AX                   ;Save it.
  638.                AND     AX,0FFFH                ;Lower 12 bits.
  639.                CMP     AX,0FF7H                ;Is it marked bad?
  640.                JZ      EVEN_CLUSTER            ;If yes, leave it that way.
  641.                AND     BX,0F000H               ;Else, zero lower 12 bits.
  642.                MOV     [DI],BX
  643. EVEN_CLUSTER:  DEC     SI
  644.                INC     DI
  645.                LODSW                           ;Next FAT word.
  646.                MOV     BX,AX
  647.                AND     AX,0FFF0H               ;Do the same except the
  648.                CMP     AX,0FF70H               ; even FAT numbers use
  649.                JZ      LOOP_CLUSTER            ; upper 12 bits.
  650.                AND     BX,0000FH
  651.                MOV     [DI],BX
  652. LOOP_CLUSTER:  INC     DI
  653.                INC     DI
  654.                SUB     CX,3                    ;Do all of FAT.
  655.                JNC     NEXT_CLUSTER
  656.  
  657. NEXT_FAT:      MOV     AL,DRIVE                         ;Write FAT to disk.
  658.                MOV     CX,READ_BUFFER.SECTORS_PER_FAT
  659.                MOV     DX,SECTOR
  660.                ADD     SECTOR,CX                        ;Next sector to write.
  661.                MOV     BX,OFFSET WRITE_BUFFER
  662.                INT     26H
  663.                POP     AX
  664.                DEC     READ_BUFFER.NUMBER_OF_FATS       ;Do all copies of FAT.
  665.                JNZ     NEXT_FAT
  666.  
  667.                MOV     AX,READ_BUFFER.ROOT_DIRECTORY_ENTRIES  ;Get root entries.
  668.                CMP     AX,2048
  669.                JAE     FAIL_FORMAT
  670. GET_ENTRIES:   MOV     CL,5                          ;32 bytes/entry.
  671.                SHL     AX,CL                         ;Total bytes for directory.
  672.                MOV     CX,AX                         ;Save.
  673.                XOR     DX,DX                         ;Zero in high half.
  674.                DIV     READ_BUFFER.BYTES_PER_SECTOR  ;Sectors for dirs.
  675.                MOV     BX,AX                         ;Save this.
  676.                XOR     AX,AX                         ;Zeros in directory.
  677.                SHR     CX,1                          ;Divide by two for words.
  678.                MOV     DI,OFFSET WRITE_BUFFER
  679.                REP     STOSW                         ;Zeros in directory.
  680.                MOV     AL,DRIVE
  681.                MOV     CX,BX
  682.                MOV     DX,SECTOR
  683.                MOV     BX,OFFSET WRITE_BUFFER        ;Write directory sectors.
  684.                INT     26H
  685.                POP     AX
  686.                JC      FAIL_FORMAT
  687.                XOR     AL,AL                   ;ErrorLevel = 0.
  688.                JMP     SHORT FORMAT_END
  689.  
  690. FAIL_FORMAT:   MOV     AL,1                    ;ErrorLevel = 1.
  691. FORMAT_END:    RET
  692.  
  693. ;----------------------------------------------;
  694. WEEKDAY:       CALL    GET_DATE                ;ErrorLevel = (0 - 6).
  695.                RET
  696.  
  697. DAY:           CALL    GET_DATE                ;ErrorLevel = (1 - 31).
  698.                MOV     AL,DL
  699.                RET
  700.  
  701. MONTH:         CALL    GET_DATE                ;ErrorLevel = (1- 12).
  702.                MOV     AL,DH
  703.                RET
  704.  
  705. YEAR:          CALL    GET_DATE
  706.                SUB     CX,1980                 ;Normalize.
  707.                MOV     AL,CL                   ;ErrorLevel = (0 - 199).
  708.                RET
  709.  
  710. HOUR:          CALL    GET_TIME                ;ErrorLevel = (0 - 23).
  711.                MOV     AL,CH
  712.                RET
  713.  
  714. MINUTE:        CALL    GET_TIME                ;ErrorLevel = (0- 59).
  715.                MOV     AL,CL
  716.                RET
  717.  
  718. SECOND:        CALL    GET_TIME                ;ErrorLevel = (0 - 59).
  719.                MOV     AL,DH
  720.                RET
  721.  
  722. ;----------------------------------------------;
  723. BREAK:         MOV     AX,3300H                ;ErrorLevel = 1 if Break ON.
  724.                INT     21H
  725.                MOV     AL,DL
  726.                RET
  727.  
  728. ;----------------------------------------------;
  729. ROWS:          MOV     AL,CRT_ROWS             ;ErrorLevel = displayable rows.
  730.                INC     AL
  731.                RET
  732.  
  733. COLS:          MOV     AL,BYTE PTR CRT_COLS    ;ErrorLevel = displayable cols.
  734.                RET
  735.  
  736. VIDEOMODE:     MOV     AL,CRT_MODE             ;ErrorLevel = current video mode.
  737.                RET
  738.  
  739. SETCURSOR:     CALL    DECIMAL_INPUT           ;Get cursor row position request.
  740.                JZ      SETCURSOR_END           ;If none, exit.
  741.                DEC     AL                      ;Else, logical base zero.
  742.                JS      SETCURSOR_END           ;If zero, exit.
  743.                PUSH    AX                      ;Else, save request.
  744.                CALL    PARSE_DELIMIT           ;Parse delimiters.
  745.                CALL    DECIMAL_INPUT           ;Get columns.
  746.                JZ      SETCURSOR_END           ;If none, exit.
  747.                DEC     AL                      ;Else, logical base zero.
  748.                JS      SETCURSOR_END           ;if zero, exit.
  749.                POP     DX                      ;Retrieve, row.
  750.                MOV     DH,DL
  751.                MOV     DL,AL
  752.                CALL    SET_CURSOR              ;Set cursor position.
  753.                XOR     AL,AL                   ;ErrorLevel = 0.
  754. SETCURSOR_END: RET
  755.  
  756. ;----------------------------------------------;
  757. COLDBOOT:      MOV     AX,40H                  ;Point to BIOS data.
  758.                MOV     DS,AX
  759.                MOV     WORD PTR DS:[72H],0     ;Reset flag = 0.
  760.                JMP     FAR PTR RESET           ;BIOS reset.
  761.  
  762. ;----------------------------------------------;
  763. WARMBOOT:      MOV     AX,40H                  ;Point to BIOS data.
  764.                MOV     DS,AX
  765.                MOV     WORD PTR DS:[72H],1234H ;Reset flag = 1234h.
  766.                JMP     FAR PTR RESET           ;BIOS reset.
  767.  
  768. ;----------------------------------------------;
  769. BEEP:          CALL    PARSE_DELIMIT
  770.                MOV     BX,C_NOTE               ;Default tone frequency divisor.
  771.                CMP     BYTE PTR [SI - 1],COMMA ;Any tone request?
  772.                JZ      BEEP_LENGTH             ;If no, done here.
  773.                CALL    DECIMAL_INPUT           ;Else, get it.
  774.                JZ      BEEP_LENGTH             ;If none, get length.
  775.                MOV     BX,AX                   ;Else, store tone.
  776. BEEP_LENGTH:   CALL    PARSE_DELIMIT           ;Parse delimiters.
  777.                CALL    DECIMAL_INPUT           ;Get length.
  778.                JNZ     DO_BEEP                 ;If go it, beep.
  779.                MOV     AX,1                    ;Else, default of one second.
  780.  
  781. DO_BEEP:       MOV     BP,AX                   ;Save beep time.
  782.                OR      BP,BP                   ;Zero time?
  783.                JZ      NEXT_BEEP               ;If yes, skip.
  784.                XOR     AX,AX
  785.                MOV     DX,12H                  ;120000h dividend constant.
  786.                CMP     BX,12H
  787.                JBE     NO_BEEP                 ;Avoid division by zero error.
  788.                DIV     BX                      ; Divide to get
  789.                MOV     BX,AX                   ; 8253 countdown.
  790.  
  791.                MOV     CX,1                    ;One timer tick.
  792.                CALL    DELAY                   ;Wait till clock rolls over.
  793.  
  794.                MOV     AL,0B6H                 ;Channel 2 speaker functions.
  795.                OUT     43H,AL                  ;8253 Mode Control.
  796.                JMP     $+2                     ;IO delay.
  797.                MOV     AX,BX                   ;Retrieve countdown.
  798.                OUT     42H,AL                  ;Channel 2 LSB.
  799.                JMP     $+2
  800.                MOV     AL,AH                   ;Channel 2 MSB.
  801.                OUT     42H,AL
  802.                IN      AL,61H                  ;Port B.
  803.                OR      AL,3                    ;Turn on speaker.
  804.                JMP     $+2
  805.                OUT     61H,AL
  806.  
  807. NO_BEEP:       MOV     CX,BP                   ;Number of seconds.
  808.                CALL    DELAY                   ;Delay seconds.
  809.                IN      AL,61H                  ;Get Port B again.
  810.                AND     AL,NOT 3                ;Turn speaker off.
  811.                JMP     $+2
  812.                OUT     61H,AL
  813. NEXT_BEEP:     CMP     BYTE PTR [SI],";"       ;Semicolon delimiter?
  814.                JZ      BEEP                    ;If yes, next tone.
  815.                XOR     AL,AL                   ;ErrorLevel = 0
  816.                RET
  817.  
  818. ;----------------------------------------------;
  819. ANSI:          MOV     AX,3529H                ;Get undocumented INT 29 vector.
  820.                INT     21H
  821.                MOV     SI,OFFSET CON           ;Does it point to ANSI.SYS?
  822.                MOV     DI,CON_OFFSET           ;Check by looking for "CON"
  823.                MOV     CX,3                    ; as device name.
  824.                REPZ    CMPSB
  825.                JZ      FOUND_ANSI              ;If yes, found it.
  826.  
  827.                MOV     BX,OFFSET ANSICOM       ;Else, look for ANSI.COM.
  828.                XOR     DX,DX                   ;Start at segment zero.
  829.                MOV     AX,CS                   ;Store our segment in AX.
  830. NEXT_SEARCH:   INC     DX                      ;Next paragraph.
  831.                MOV     ES,DX
  832.                CMP     DX,AX                   ;Is it our segment?
  833.                JZ      NOT_FOUND               ;If yes, search is done.
  834.                XOR     DI,DI
  835.                MOV     AL,NOT 0E9H             ;NOT the Long JMP instruction.
  836.                SCASB
  837.                JNZ     NEXT_SEARCH             ;Match?
  838.                MOV     SI,BX                   ;Else, point to ANSI.COM sign.
  839.                INC     DI
  840.                INC     DI
  841.                MOV     CX,ANSICOM_LENGTH       ;Check 10 bytes for match.
  842.                REPZ    CMPSB
  843.                JNZ     NEXT_SEARCH             ;If no match, keep looking.
  844.  
  845. FOUND_ANSI:    XOR     AL,AL                   ;ErrorLevel = 0.
  846. ANSI_END:      RET
  847.  
  848. NOT_FOUND:     MOV     AL,1                    ;ErrorLevel = 1.
  849.                RET
  850.  
  851. ;----------------------------------------------;
  852. CPU:           MOV     AL,1                    ;8086/8088
  853.                PUSH    SP
  854.                POP     BX
  855.                CMP     BX,SP                   ;88/86/186 will push SP-2
  856.                JZ      CK_286                  ;286/386 will push SP
  857.                MOV     CL,32                   ;186 uses CL MOD 32.
  858.                SHL     BX,CL
  859.                JZ      CPU_END
  860.                INC     AL                      ;80186
  861.                JMP     SHORT CPU_END
  862.  
  863. CK_286:        MOV     AL,4                    ;80386
  864.                PUSHF
  865.                MOV     BX,SP
  866.                POPF
  867.                INC     BX
  868.                INC     BX
  869.                CMP     BX,SP                   ;32 or 16 bit push?
  870.                JNZ     CPU_END
  871.  
  872.                SUB     SP,6
  873.                MOV     BP,SP
  874. DB             0FH,01H,46H,00H                 ;SGDT    QWORD PTR [BP]
  875.                ADD     SP,4                    ;Get Global Descriptor Table.      
  876.                POP     BX
  877.                INC     BH                      ;Third word of GDT = -1
  878.                JNZ     CPU_END                 ; for 286.
  879.                DEC     AL                      ;80286
  880. CPU_END:       RET
  881.  
  882. ;----------------------------------------------;
  883. DISPLAYS:      MOV     AX,1A00H                ;Read Display Combination.
  884.                INT     10H
  885.                CMP     AL,1AH                  ;Supported?
  886.                JNZ     CK_EGA                  ;If no, done here.
  887.                MOV     AL,BL                   ;Else, return display type.
  888.                JMP     SHORT DISPLAY_END
  889.  
  890. CK_EGA:        MOV     BL,10H                  ;Return EGA information.
  891.                MOV     AH,12H
  892.                INT     10H
  893.                CMP     BL,10H                  ;Supported?
  894.                JZ      CK_CGA                  ;If no, done here.
  895.                MOV     CX,40H
  896.                MOV     ES,CX
  897.                MOV     AL,1                    ;Assume MDA
  898.                TEST    ES:BYTE PTR [87H],8     ;Else, EGA_info; Is it active?
  899.                JNZ     DISPLAY_END             ;If no, assumed right.
  900.                MOV     AL,4                    ;Else, assume EGAcolor.
  901.                OR      BH,BH                   ;Assume right?
  902.                JZ      DISPLAY_END             ;If yes, exit.
  903.                INC     AL                      ;Else, it's gotta be EGAmono.
  904.                JMP     SHORT DISPLAY_END
  905.  
  906. CK_CGA:        PUSH    DS
  907.                MOV     AX,40H
  908.                MOV     DS,AX                   ;BIOS data area
  909.                MOV     AL,2                    ;Assume CGA.
  910.                CMP     WORD PTR DS:[63H],3D4H  ;ADDR_6845.
  911.                JZ      GOT_ADAPTOR             ;If CGA, guessed right.
  912.                DEC     AL                      ;Else, MDA
  913. GOT_ADAPTOR:   POP     DS
  914. DISPLAY_END:   RET
  915.  
  916. ;----------------------------------------------;
  917. MAINMEM:       XOR     BX,BX                   ;Shrink allocated memory to
  918.                MOV     AH,4AH                  ; zero for current program
  919.                INT     21H                     ; via DOS.
  920.                MOV     AH,48H                  ;Request FFFFh paragraphs of
  921.                MOV     BX,0FFFFH               ; memory. This will fail with
  922.                INT     21H                     ; available paragraphs in BX.
  923.                MOV     CL,6                    ;Divide by 64 to get memory.
  924.                SHR     BX,CL
  925.                CALL    CAPITALIZE
  926.                CMP     BYTE PTR [SI],"R"       ;Was there an argument of "R"?
  927.                JZ      MEMORY_REPORT           ;If yes, display available mem.
  928.                CALL    DECIMAL_INPUT           ;Else, get requested memory.
  929.                JZ      NOT_ENOUGH              ;If zero, fail.
  930.                CMP     BX,AX                   ;Else, compare available with
  931.                JAE     ENOUGH                  ; requested.
  932.                JMP     SHORT NOT_ENOUGH
  933.  
  934. EXTMEM:        MOV     AX,0FFFFH
  935.                MOV     ES,AX
  936.                MOV     AL,ES:[0EH]             ;Get System ID.
  937.                XOR     BX,BX                   ;Assume none available.
  938.                CMP     AL,0FCH
  939.                JA      EXTMEM_REPORT           ;If PC, XT or PCjr, not supported
  940.                JZ      GET_EXT                 ;If AT, get extended.
  941.                CMP     AL,0F9H                 ;If PC Convert., Mod 30, ignore.
  942.                JAE     EXTMEM_REPORT
  943. GET_EXT:       MOV     AH,88H                  ;Retrieve extended.
  944.                INT     15H
  945.                MOV     BX,AX                   ;Store in BX.
  946. EXTMEM_REPORT: CALL    CAPITALIZE
  947.                CMP     BYTE PTR [SI],"R"       ;If R argument, display available
  948.                JZ      MEMORY_REPORT
  949.                CALL    DECIMAL_INPUT           ;Else, get requested.
  950.                JZ      NOT_ENOUGH              ;If zero, not enough.
  951.                CMP     BX,AX                   ;Else, compare requested with
  952.                JAE     ENOUGH                  ; available.
  953.                JMP     SHORT NOT_ENOUGH
  954.  
  955. EXPMEM:        MOV     AX,3567H                ;Retrieve EMM interrupt vector.
  956.                INT     21H
  957.                XOR     BX,BX                   ;Assume no memory.
  958.                MOV     DI,0AH                  ;See if offset 10 of vector
  959.                PUSH    SI
  960.                MOV     SI,OFFSET EMM           ; points to EMMXXXX0.
  961.                MOV     CX,EMM_LENGTH
  962.                REPZ    CMPSB
  963.                POP     SI
  964.                JNZ     EXPMEM_REPORT           ;If no, then no memory manager.
  965.                MOV     AH,42H                  ;Else, retrieve free expanded.
  966.                INT     67H
  967. EXPMEM_REPORT: MOV     CL,4                    ;Convert to K.
  968.                SHL     BX,CL
  969.                CALL    CAPITALIZE
  970.                CMP     BYTE PTR [SI],"R"       ;If report switch, display avail.
  971.                JZ      MEMORY_REPORT
  972.                CALL    DECIMAL_INPUT           ;Else, get requested and compare
  973.                JZ      NOT_ENOUGH              ; with available.
  974.                CMP     BX,AX
  975.                JB      NOT_ENOUGH
  976.  
  977. ENOUGH:        XOR     AL,AL                   ;ErrorLevel = 0.
  978.                RET
  979.  
  980. NOT_ENOUGH:    MOV     AL,1                    ;ErrorLevel = 1.
  981.                RET
  982.  
  983. MEMORY_REPORT: MOV     AX,BX
  984.                CALL    DEC_OUTPUT              ;Display available memory.
  985.                PUSH    CS
  986.                POP     DS                      ;Restore data segment.
  987.                MOV     DX,OFFSET BYTES_FREE    ;Display "K Bytes free".
  988.                CALL    PRINT_STRING
  989.                XOR     AL,AL                   ;ErrorLevel = 0.
  990.                RET
  991.  
  992. ;----------------------------------------------;
  993. WAITFOR:       XOR     DI,DI                   ;Assume no minutes.
  994.                CALL    DECIMAL_INPUT           ;Get argument.
  995.                MOV     BX,AX                   ;Else, BX = seconds.
  996.                CMP     BYTE PTR [SI],":"       ;Delimiter colon?
  997.                JNZ     GO_DELAY                ;If no, delay seconds.
  998.                MOV     DI,AX                   ;Else, save parameter in DI.
  999.                CALL    DECIMAL_INPUT           ;Get second parameter.
  1000.                MOV     BX,AX                   ;Save in BX.
  1001.  
  1002. GO_DELAY:      MOV     AX,60                   ;Multiply minutes by 60.
  1003.                MUL     DI
  1004.                ADD     BX,AX                   ;Add to second parameter.
  1005.  
  1006. NEXT_WAIT:     MOV     AH,1                    ;Key pressed?
  1007.                INT     16H
  1008.                JNZ     WAITFOR_BREAK           ;If yes, abort.
  1009.                XOR     AL,AL                   ;EL=0.
  1010.                OR      BX,BX                   ;Zero seconds?
  1011.                JZ      WAITFOR_END             ;If yes, done.
  1012.                MOV     CX,18                   ;If no, delay one second.
  1013.                CALL    DELAY
  1014.                DEC     BX                      ;Decrement second count
  1015.                JMP     NEXT_WAIT
  1016.  
  1017. WAITFOR_BREAK: CALL    KEYPRESS                ;Rid KBD buffer of keypress.
  1018. NO_WAITFOR:    MOV     AL,1                    ;ErrorLevel =  1.
  1019. WAITFOR_END:   RET
  1020.  
  1021. ;----------------------------------------------;
  1022. WAITTIL:       CALL    DECIMAL_INPUT           ;Get argument.
  1023.                JZ      NO_WAITTIL              ;If none, exit.
  1024.                MOV     BH,AL                   ;Else, save in BH.
  1025.                INC     SI                      ;Pointer past colon.
  1026.                CALL    DECIMAL_INPUT           ;Assume minutes.
  1027.                JZ      NO_WAITTIL              ;If none, exit.
  1028.                MOV     BL,AL                   ;Else, save in BL.
  1029.                MOV     DI,BX                   ;Save in DI.
  1030.                INC     SI                      ;Pointer past colon.
  1031.                CALL    DECIMAL_INPUT           ;Get argument.
  1032.                MOV     BL,AL                   ;Seconds in BL.
  1033.  
  1034. NEXT_WAITTIL:  MOV     AH,1                    ;Check for keypress.
  1035.                INT     16H
  1036.                JNZ     WAITTIL_BREAK           ;If yes, abort.
  1037.                MOV     AH,2CH                  ;Else, get time.
  1038.                INT     21H
  1039.                CMP     DI,CX                   ;Requested hours:minutes?
  1040.                JNZ     NEXT_WAITTIL            ;If no, wait.
  1041.                CMP     BL,DH                   ;Else, requested = seconds?
  1042.                JNZ     NEXT_WAITTIL            ;If no, wait.
  1043.                XOR     AL,AL                   ;Else, ErrorLevel = 0.
  1044.                JMP     SHORT WAITTIL_END       ;Exit.
  1045.  
  1046. WAITTIL_BREAK: CALL    KEYPRESS                ;Eat keypress.
  1047. NO_WAITTIL:    MOV     AL,1                    ;ErrorLevel = 1.
  1048. WAITTIL_END:   RET
  1049.  
  1050. ;----------------------------------------------;
  1051. GETKEY:        MOV     DI,OFFSET CHARS         ;Storage for alphanumeric keys.
  1052.                MOV     BX,OFFSET FUNCS         ;Storage for function keys.
  1053.                MOV     CL,1                    ;Position.
  1054. NEXT_GET:      LODSB                           ;Get a byte.
  1055.                CMP     AL,CR                   ;End of arguments?
  1056.                JZ      DO_KEY                  ;If yes, wait for keypress.
  1057.                CMP     AL,SPACE                ;Else, if delimiter, next byte.
  1058.                JBE     NEXT_GET
  1059.                CMP     AL,SINGLE_QUOTE         ;If single or double quotes
  1060.                JZ      GET_CHARS               ; then start of literals.
  1061.                CMP     AL,DOUBLE_QUOTE
  1062.                JZ      GET_CHARS
  1063.                DEC     SI                      ;Else, adjust pointer and
  1064.                CALL    DECIMAL_INPUT           ; get the function key number.
  1065.                JNZ     CK_FUNC                 ;If a number found, continue.
  1066.                INC     SI                      ;Else, adjust pointer.
  1067.                JMP     NEXT_GET                ;Get next byte.
  1068. CK_FUNC:       CMP     AL,1                    ;If number between 1 and 12
  1069.                JB      NEXT_GET                ; it's legal.
  1070.                CMP     AL,12
  1071.                JA      NEXT_GET
  1072.                MOV     CH,AL                   ;Save number in CH.
  1073.                ADD     AL,3AH                  ;Convert number to scan code.
  1074.                CMP     CH,10
  1075.                JBE     STORE_FUNC
  1076.                ADD     AL,40H                  ;Adjust if function keys 10 or 11
  1077.                MOV     KBD_TYPE,10H            ; and use extended KBD read.
  1078. STORE_FUNC:    MOV     BYTE PTR [BX],AL        ;Store the scan code.
  1079.                INC     BX                      ;Next storage.
  1080.                MOV     BYTE PTR [BX],CL        ;Store the position.
  1081.                INC     BX                      ;Next storage.
  1082.                INC     FUNC_CNT                ;Increment function key count.
  1083.                INC     CL                      ;Next position.
  1084.                JMP     NEXT_GET                ;Next byte.
  1085.  
  1086. GET_CHARS:     MOV     DL,AL                   ;Save quote type.
  1087. NEXT_CHARS:    LODSB                           ;Get a byte.
  1088.                CMP     AL,CR                   ;End of input?
  1089.                JZ      DO_KEY                  ;If yes, done here.
  1090.                CMP     AL,DL                   ;Else, ending quote?
  1091.                JZ      NEXT_GET                ;If yes, done here.
  1092.                CMP     AL,"a"                  ;Else, capitalize.
  1093.                JB      STORE_CHAR
  1094.                CMP     AL,"z"
  1095.                JA      STORE_CHAR
  1096.                AND     AL,5FH
  1097. STORE_CHAR:    STOSB                           ;Store the character.
  1098.                MOV     AL,CL
  1099.                STOSB                           ;Store the position.
  1100.                INC     CHAR_CNT                ;Increment character count.
  1101.                INC     CL                      ;Next position.
  1102.                JMP     NEXT_CHARS              ;Next literal.
  1103.  
  1104. DO_KEY:        CMP     CHAR_CNT,0              ;If no arguments found,
  1105.                JNZ     KEY                     ; then return scan code of
  1106.                CMP     FUNC_CNT,0              ; first keypress.
  1107.                JZ      SCAN_KEY
  1108.  
  1109. KEY:           MOV     AH,KBD_TYPE             ;Else, get a key.
  1110.                INT     16H
  1111.                CMP     AL,"a"                  ;Capitalize.
  1112.                JB      CHAR_KEYS
  1113.                CMP     AL,"z"
  1114.                JA      CHAR_KEYS
  1115.                AND     AL,5FH
  1116. CHAR_KEYS:     MOV     CX,CHAR_CNT             ;Does it match one of the
  1117.                JCXZ    FUNC_KEYS               ; literals?
  1118.                MOV     DI,OFFSET CHARS
  1119. CHAR_SCAN:     SCASB
  1120.                JZ      KEY_END                 ;If yes, return EL = position.
  1121.                INC     DI
  1122.                LOOP    CHAR_SCAN
  1123.  
  1124. FUNC_KEYS:     XCHG    AL,AH                   ;Else, scan code in AL.
  1125.                MOV     CX,FUNC_CNT
  1126.                JCXZ    CK_BREAK
  1127.                MOV     DI,OFFSET FUNCS         ;Match any function key requests?
  1128. FUNC_SCAN:     SCASB
  1129.                JZ      KEY_END                 ;If yes, done here.
  1130.                INC     DI
  1131.                LOOP    FUNC_SCAN
  1132. CK_BREAK:      OR      AX,AX                   ;Else, was it Ctrl break?
  1133.                MOV     AL,-1                   ;If yes, ErrorLevel = -1.
  1134.                JZ      KEY_DONE
  1135.                CMP     AH,3                    ;Same for Ctrl C.
  1136.                JZ      KEY_DONE
  1137.                JMP     KEY                     ;Else, get another keypress.
  1138.  
  1139. KEY_END:       MOV     AL,BYTE PTR [DI]        ;ErrorLevel = position.
  1140. KEY_DONE:      RET
  1141.  
  1142. SCAN_KEY:      CALL    KEYPRESS                ;If no arguments
  1143.                MOV     AL,AH                   ; ErrorLevel = scan code.
  1144.                RET
  1145.  
  1146. ;----------------------------------------------;
  1147. SHIFT:         CALL    CAPITALIZE              ;Capitalize argument.
  1148.                XOR     AL,AL                   ;Assume not depressed.  EL = 0.
  1149.                MOV     BP,SI                   ;Save argument start.
  1150.                MOV     DI,OFFSET ALT           ;Is it ALT?
  1151.                MOV     CX,3
  1152.                REPZ    CMPSB
  1153.                MOV     BL,ALT_SHIFT            ;If yes, test for ALT.
  1154.                JZ      TEST_SHIFT
  1155.  
  1156.                MOV     SI,BP                   ;Else, argument start again.
  1157.                MOV     DI,OFFSET CTRL          ;Is it CTRL.
  1158.                MOV     CX,4
  1159.                REPZ    CMPSB
  1160.                MOV     BL,CTRL_SHIFT           ;If yes, test for Ctrl.
  1161.                JNZ     SHIFT_END
  1162.  
  1163. TEST_SHIFT:    MOV     CX,40H
  1164.                MOV     DS,CX
  1165.                TEST    DS:KBD_FLAG,BL          ;Is request shift key depressed?
  1166.                JZ      SHIFT_END               ;If no, ErrorLevel = 0.
  1167.                INC     AL                      ;Else, ErrorLevel = 1.
  1168. SHIFT_END:     RET
  1169.  
  1170. ;----------------------------------------------;
  1171. SCROLLOCK:     MOV     BL,SCROLL_STATE         ;ScrollLock.
  1172.                JMP     SHORT TOGGLE_STATE
  1173.  
  1174. NUMLOCK:       MOV     BL,NUM_STATE            ;NumLock.
  1175.                JMP     SHORT TOGGLE_STATE
  1176.  
  1177. CAPSLOCK:      MOV     BL,CAPS_STATE           ;CapsLock.
  1178.  
  1179. TOGGLE_STATE:  CALL    CAPITALIZE
  1180.                MOV     BP,SI                   ;Save parameter start.
  1181.                XOR     DL,DL                   ;Toggle/set flag.
  1182.                MOV     DI,OFFSET ON            ;Point to on.
  1183.                MOV     CX,2
  1184.                REP     CMPSB
  1185.                JZ      DO_TOGGLE
  1186.                INC     DL
  1187.                MOV     SI,BP
  1188.                MOV     DI,OFFSET OFF
  1189.                MOV     CX,3
  1190.                REP     CMPSB
  1191.                JNZ     DO_TOGGLE
  1192.                INC     DL
  1193.  
  1194. DO_TOGGLE:     MOV     AX,40H
  1195.                MOV     DS,AX
  1196.                DEC     DL
  1197.                JS      SET_STATE
  1198.                DEC     DL
  1199.                JNS     OFF_STATE
  1200.                XOR     DS:KBD_FLAG,BL          ;Toggle the requested shift
  1201.                JMP     SHORT STATE_END
  1202. OFF_STATE:     NOT     BL
  1203.                AND     DS:KBD_FLAG,BL
  1204.                JMP     SHORT STATE_END
  1205. SET_STATE:     OR      DS:KBD_FLAG,BL
  1206. STATE_END:     XOR     AL,AL                   ; key BIOS KBD flag.
  1207.                RET
  1208.  
  1209. ;----------------------------------------------;
  1210. DRIVEEXIST:    CALL    ASCIIZ
  1211.                MOV     DI,OFFSET FCB
  1212.                MOV     AX,2900H                ;Parse filename.
  1213.                INT     21H
  1214.                INC     AL                      ;EL=1 if exist.
  1215.                RET
  1216.  
  1217. ;----------------------------------------------;
  1218. ISVOL:         MOV     DX,SI                   ;Save parameter.
  1219. NEXT_ISVOL:    LODSB
  1220.                CMP     AL,"/"                  ;Convert to ASCII zero.
  1221.                JZ      ISVOLZ
  1222.                CMP     AL,CR
  1223.                JNZ     NEXT_ISVOL
  1224. ISVOLZ:        MOV     BYTE PTR [SI - 1],0
  1225.                MOV     CX,08H                  ;Volume attribute.
  1226.                CALL    FIND_FIRST              ;Find first matching.
  1227.                MOV     AL,0                    ;Assume it doesn't exist; EL = 0.
  1228.                JC      ISVOL_END               ;If no find match, guessed right.
  1229.                TEST    DS:DTA.ATTRIBUTE,CL     ;Else, attribute match?
  1230.                JZ      ISVOL_END               ;If no, ErrorLevel = 0.
  1231.                INC     AL                      ;Else, ErrorLevel = 1.
  1232. ISVOL_END:     RET
  1233.  
  1234. ;----------------------------------------------;
  1235. DIREXIST:      CALL    ASCIIZ                  ;Convert to ASCIIZ.
  1236.                MOV     BP,SI                   ;Save argument in BP.
  1237.                MOV     AH,19H                  ;Get drive default
  1238.                INT     21H
  1239.                MOV     CX,AX                   ; and save in CX.
  1240.                CMP     BYTE PTR [SI + 1],":"   ;Is there a drive request?
  1241.                JNZ     SAVE_DIR                ;If no, skip this.
  1242.                MOV     DI,OFFSET READ_BUFFER   ;Else, see if drive exists
  1243.                MOV     AX,2900H                ; via parse filename.
  1244.                INT     21H
  1245.                INC     AL                      ;Drive exist?
  1246.                JZ      DIREXIST_END            ;If no, exit with EL=0.
  1247.                MOV     AL,1                    ;Else, EL = 1.
  1248.                CMP     BYTE PTR [BP + 2],SPACE ;Drive only request?
  1249.                JBE     DIREXIST_END            ;If yes, exists; exit.
  1250.                MOV     DL,BYTE PTR [BP]        ;Else, change to that drive
  1251.                AND     DL,5FH                  ; so can restore default on
  1252.                SUB     DL,"A"                  ; exit.
  1253.                MOV     AH,0EH
  1254.                INT     21H
  1255.  
  1256. SAVE_DIR:      MOV     DI,OFFSET READ_BUFFER   ;Save default directory.
  1257.                MOV     AL,"\"                  ;DOS doesn't preface with
  1258.                STOSB                           ;slash delimiter so we must.
  1259.                MOV     SI,DI
  1260.                XOR     DL,DL
  1261.                MOV     AH,47H
  1262.                INT     21H
  1263.  
  1264.                MOV     DX,BP                   ;Point to argument again and
  1265.                MOV     AH,3BH                  ; attempt to change dir.
  1266.                INT     21H
  1267.                MOV     AL,0                    ;Assume failed.
  1268.                JC      DIREXIST_END            ;If failed, exit EL = 0.
  1269.                INC     AL                      ;Else, EL = 1.
  1270.  
  1271. DIREXIST_END:  PUSH    AX                      ;Save EL.
  1272.                MOV     DX,OFFSET READ_BUFFER   ;Restore default dir.
  1273.                MOV     AH,3BH
  1274.                INT     21H
  1275.                MOV     DX,CX                   ;Restore default drive.
  1276.                MOV     AH,0EH
  1277.                INT     21H
  1278.                POP     AX                      ;Retrieve EL and return.
  1279.                RET
  1280.  
  1281. ;----------------------------------------------;
  1282. TYPEMATIC:     MOV     BL,REPEAT_NORMAL        ;Assume normal parameters.
  1283.                MOV     BH,INIT_NORMAL
  1284.                MOV     AL,BYTE PTR [SI]
  1285.                AND     AL,5FH
  1286.                CMP     AL,"N"                  ;Is it (N)ormal request?
  1287.                JZ      SET_TYPE                ;If yes, assumed right.
  1288.                CALL    DECIMAL_INPUT           ;Get requested typematic rate.
  1289.                MOV     BL,REPEAT_DEFAULT       ;Assume no parameter.
  1290.                JZ      STORE_REPEAT            ;If none, use default.
  1291.                MOV     BL,AL                   ;Else, rate in BL.
  1292.                MOV     AL,1                    ;ErrorLevel = 1.
  1293.                CMP     BL,REPEAT_MAX           ;Is it greater than max rate?
  1294.                JA      TYPEMATIC_END           ;If yes, exit with error.
  1295. STORE_REPEAT:  NEG     BL                      ;Inverse rate by subtracting
  1296.                ADD     BL,REPEAT_MAX           ; from maximum rate.
  1297.                CALL    DECIMAL_INPUT           ;Get requested initial delay.
  1298.                MOV     BH,INIT_DEFAULT         ;Assume no parameter.
  1299.                JZ      SET_TYPE                ;If none, use default.
  1300.                MOV     BH,AL                   ;Else, delay in BL.
  1301.                MOV     AL,1                    ;ErrorLevel = 1.
  1302.                CMP     BH,INIT_MAX             ;Is it greater than max delay?
  1303.                JA      TYPEMATIC_END           ;If yes, exit with error.
  1304. SET_TYPE:      MOV     AX,305H                 ;Set typematic rate and delay
  1305.                INT     16H                     ; via BIOS.
  1306.                XOR     AL,AL                   ;ErrorLevel = 0.
  1307. TYPEMATIC_END: RET
  1308.  
  1309. ;----------------------------------------------;
  1310. CURSORTYPE:    CALL    DISPLAYS                ;Get display type.
  1311.                MOV     BX,0607H                ;Assume CGA default cursor.
  1312.                CMP     AL,2
  1313.                JZ      GET_CURSOR
  1314.                MOV     BX,0B0CH                ;Assume mono/EGA cursor.
  1315.                CMP     AL,5
  1316.                JBE     GET_CURSOR
  1317.                MOV     BX,0D0EH                ;Else, VGA cursor.
  1318. GET_CURSOR:    CALL    FIND_HEX                ;Get row argument.
  1319.                JZ      DO_CURSOR               ;If none, use default.
  1320.                MOV     BH,AL                   ;Else, save.
  1321.                CALL    PARSE_DELIMIT
  1322.                CALL    FIND_HEX                ;Get column argument.
  1323.                MOV     BL,AL
  1324. DO_CURSOR:     MOV     CX,BX
  1325.                MOV     AX,40H
  1326.                MOV     DS,AX
  1327.                PUSH    DS:[87H]                ;Preserve EGA info.
  1328.                OR      BYTE PTR DS:[87H],1     ;Emulation off.
  1329.                MOV     AH,1                    ;Set cursor type via BIOS.
  1330.                INT     10H
  1331.                POP     DS:[87H]                ;Restore EGA info.
  1332.                XOR     AL,AL                   ;ErrorLevel = 0.
  1333. CURSOR_END:    RET
  1334.  
  1335. ;----------------------------------------------;
  1336. PRTSC:         INT     5                       ;Print screen via BIOS.
  1337.                LODSB
  1338.                AND     AL,5FH
  1339.                CMP     AL,"F"                  ;If "F" found, add formfeed.
  1340.                JNZ     PRTSC_END
  1341.                MOV     DL,FF
  1342.                MOV     AH,5
  1343.                INT     21H
  1344. PRTSC_END:     XOR     AL,AL                   ;ErrorLevel = 0.
  1345.                RET
  1346.  
  1347. ;----------------------------------------------;
  1348. DOSVER:        MOV     AX,DOS_VERSION          ;ErrorLevel = DOS major * 32
  1349.                MOV     CL,5                    ; + DOS minor.
  1350.                SHL     AL,CL
  1351.                OR      AL,AH
  1352.                RET
  1353.  
  1354. ;----------------------------------------------;
  1355. ROMDATE:       MOV     AX,SEG BOOT_SEG         ;Point to ROM system date.
  1356.                MOV     DS,AX
  1357.                MOV     SI,OFFSET DATE_STAMP
  1358.                MOV     CX,8                    ;8 bytes to date.
  1359. NEXT_DATE:     LODSB
  1360.                CMP     AL,"/"
  1361.                JB      ROMDATE_END
  1362.                CMP     AL,"9"
  1363.                JA      ROMDATE_END
  1364.                MOV     DL,AL
  1365.                CALL    PRINT_CHAR              ;Display date.
  1366.                LOOP    NEXT_DATE
  1367. ROMDATE_END:   MOV     DL,CR
  1368.                CALL    PRINT_CHAR
  1369.                MOV     DL,LF
  1370.                CALL    PRINT_CHAR
  1371.                XOR     AL,AL                   ;ErrorLevel = 0.
  1372.                RET
  1373.  
  1374. ;----------------------------------------------;
  1375. RENDIR:        CMP     DOS_MAJOR,3             ;Must be DOS 3.x or better
  1376.                JB      RENDIR_ERR              ; to rename a directory.
  1377.                CALL    ASCIIZ                  ;ASCII zero target name.
  1378.                MOV     DX,SI
  1379.                MOV     CX,10H                  ;Directory attribute.
  1380.                CALL    FIND_FIRST              ;Does it exist?
  1381.                JC      RENDIR_ERR                 ;If no, exit.
  1382.                TEST    BYTE PTR DTA.ATTRIBUTE,10H ;Else, is it a directory?
  1383.                JZ      RENDIR_END                 ;If no, exit.
  1384.                CALL    FIND_END                ;Else, point to new name.
  1385.                CALL    PARSE_DELIMIT
  1386.                CALL    ASCIIZ                  ;ASCII zero it also.
  1387.                MOV     DI,SI
  1388.                MOV     AH,56H                  ;Rename the directory.
  1389.                INT     21H
  1390.                MOV     AL,0                    ;ErrorLevel = 0 if successful.
  1391.                JNC     RENDIR_END
  1392. RENDIR_ERR:    MOV     AL,1                    ;ErrorLevel = 1 if unsuccessful.
  1393. RENDIR_END:    RET
  1394.  
  1395. ;----------------------------------------------;
  1396. E43V50:        CALL    DISPLAYS                ;Get display type.
  1397.                CMP     AL,4                    ;Is it EGA or VGA?
  1398.                JB      EGAVGA_ERR              ;If no, exit.
  1399.                MOV     DL,AL                   ;Save display type in DL.
  1400.                XOR     BL,BL                   ;Load block zero of ROM
  1401.                MOV     AX,1112H                ; 8x8 double dot font.
  1402.                INT     10H
  1403.                CMP     DL,5                    ;Is it an EGA.
  1404.                JA      EGAVGA_DONE             ;If no, done here.
  1405.                MOV     AX,40
  1406.                MOV     DS,AX
  1407.                PUSH    DS:[87H]                ;Else, turn EGA cursor emulation
  1408.                OR      BYTE PTR DS:[87H],1     ; off and set cursor type to
  1409.                MOV     DX,600H                 ; underline.
  1410.                CALL    SETCURSOR
  1411.                POP     DS:[87H]
  1412.                MOV     DX,3B4H                 ;Set cursor type port.
  1413.                MOV     AX,714H
  1414.                OUT     DX,AX
  1415. EGAVGA_DONE:   XOR     AL,AL                   ;ErrorLevel = 0.
  1416.                JMP     SHORT EGAVGA_END
  1417. EGAVGA_ERR:    MOV     AL,1                    ;ErrorLevel = 1.
  1418. EGAVGA_END:    RET
  1419.  
  1420. ;----------------------------------------------;
  1421. COMPARE:       CMP     BYTE PTR [SI],CR        ;If no argument, error.
  1422.                JZ      COMPARE_ERR
  1423.                CALL    CAPITALIZE              ;Else, capitalize argument.
  1424.                MOV     DI,SI                   ;Save in DI.
  1425. STRING_END:    LODSB                           ;Find end of first argument.
  1426.                CMP     AL,SPACE
  1427.                JA      STRING_END
  1428.                CALL    PARSE_DELIMIT           ;Parse delimiters.
  1429.                CALL    CAPITALIZE              ;Capitalize second argument.
  1430.                CALL    ASCIIZ                  ;Eliminate any "/" character.
  1431. NEXT_COMPARE:  LODSB                           ;Get a byte.
  1432.                CMP     AL,SPACE                ;End of string?
  1433.                JBE     CK_MATCH                ;If yes, see if end of other.
  1434.                SCASB                           ;Else, see if matching bytes.
  1435.                JZ      NEXT_COMPARE            ;If yes, continue.
  1436.                JMP     SHORT COMPARE_ERR       ;Else, no match.
  1437. CK_MATCH:      XOR     AL,AL                   ;EL = 0 if strings match.
  1438.                CMP     BYTE PTR [DI],SPACE
  1439.                JBE     COMPARE_END
  1440. COMPARE_ERR:   MOV     AL,1                    ;EL = 1 if non-matching.
  1441. COMPARE_END:   RET
  1442.  
  1443. ;----------------------------------------------;
  1444. CANCOPY:       XOR     DL,DL                   ;Assume source default drive.
  1445.                CMP     BYTE PTR [SI + 1],":"   ;Drive parameter?
  1446.                JNZ     GET_CLUSTER             ;If no, assumed right.
  1447.                MOV     DL,[SI]                 ;Else, get drive.
  1448.                AND     DL,5FH                  ;Capitalize.
  1449.                SUB     DL,"A" - 1              ;Logical.
  1450. GET_CLUSTER:   MOV     AH,36H
  1451.                INT     21H                     ;Get cluster size.
  1452.                CMP     AX,0FFFFH               ;Valid drive.
  1453.                JZ      CANCOPY_FAIL            ;If no, exit.
  1454.                MUL     CX                      ;Else, get bytes/cluster.
  1455.                MOV     BP,AX                   ;Save it.
  1456.  
  1457.                CALL    ASCIIZ                  ;ASCII zero filespec.
  1458.                MOV     DX,SI                   ;Point to filespec.
  1459.                MOV     CX,1                    ;Normal and read-only files.
  1460.                CALL    FIND_FIRST
  1461.                JC      CANCOPY_FAIL            ;If no matching, fail.
  1462.                XOR     DI,DI                   ;Else, start with zero.
  1463. NEXT_CANCOPY:  MOV     AX,DTA.SIZE_LOW         ;Get file size.
  1464.                MOV     DX,DTA.SIZE_HIGH
  1465.                DIV     BP                      ;Convert to clusters.
  1466.                OR      DX,DX                   ;Round up.
  1467.                JZ      ACCUMULATE
  1468.                INC     AX
  1469. ACCUMULATE:    ADD     DI,AX                   ;Accumulate.
  1470.                MOV     AH,4FH                  ;Find next matching.
  1471.                INT     21H
  1472.                JNC     NEXT_CANCOPY
  1473.  
  1474.                CALL    FIND_END                ;Find end of filespec.
  1475.                CALL    PARSE_DELIMIT
  1476.                XOR     DL,DL                   ;Assume no destination drive.
  1477.                CMP     BYTE PTR [SI - 1],"/"
  1478.                JZ      GET_DEST
  1479.                LODSB                           ;Get drive request.
  1480.                CMP     AL,SPACE                ;Is there one?
  1481.                JBE     GET_DEST                ;If no, use default.
  1482.                AND     AL,5FH                  ;Else, capitalize.
  1483.                SUB     AL,"A" - 1
  1484.                MOV     DL,AL
  1485. GET_DEST:      MOV     AH,36H                  ;Get target free space.
  1486.                INT     21H
  1487.                CMP     AX,0FFFFH               ;If invalid drive, fail
  1488.                JZ      CANCOPY_FAIL
  1489.                CMP     BX,DI                   ;Else, compare available clusters
  1490.                JB      CANCOPY_FAIL            ; with filespec clusters.
  1491.                XOR     AL,AL                   ;EL = 0.
  1492.                JMP     SHORT CANCOPY_END
  1493. CANCOPY_FAIL:  MOV     AL,1                    ;EL = 1.
  1494. CANCOPY_END:   RET
  1495.  
  1496. ;----------------------------------------------;
  1497. WINDOW:        PUSH    CURSOR_POSN             ;Preserve cursor position.
  1498.                MOV     DI,OFFSET SPACES        ;Assume no border chars.
  1499.                CALL    DECIMAL_INPUT           ;Get starting row.
  1500.                JZ      WIN_ERR                 ;If none, exit.
  1501.                MOV     BH,AL                   ;Else, save in BH.
  1502.                DEC     BH                      ;Adjust.
  1503.                CALL    PARSE_DELIMIT
  1504.                CALL    DECIMAL_INPUT           ;Get starting column.
  1505.                JZ      WIN_ERR                 ;If none, exit.
  1506.                MOV     BL,AL                   ;Else, save in BL.
  1507.                DEC     BL                      ;Adjust.
  1508.                MOV     DX,BX
  1509.                MOV     WIN_CURSOR,DX           ;Store upper left corner.
  1510.                CALL    DO_WIN_CURSOR           ;Set cursor.
  1511.                CALL    PARSE_DELIMIT
  1512.                CALL    DECIMAL_INPUT           ;Get width.
  1513.                SUB     AX,2                    ;At least 2 wide.
  1514.                JB      WIN_ERR
  1515.                MOV     WIN_WIDTH,AX
  1516.                CALL    PARSE_DELIMIT
  1517.                CALL    DECIMAL_INPUT           ;Get height.
  1518.                SUB     AX,2                    ;At least 2 wide.
  1519.                JAE     SAVE_HEIGHT
  1520. WIN_ERR:       JMP     SHORT WINDOW_ERROR
  1521. SAVE_HEIGHT:   MOV     WIN_HEIGHT,AX
  1522.  
  1523.                CALL    PARSE_DELIMIT
  1524.                CALL    GET_COLOR               ;Get screen color.
  1525.                CALL    FIND_HEX                ;Get requested color.
  1526.                JZ      DO_WINDOW               ;If none, use screen color.
  1527.                MOV     BL,AL                   ;Else, requested.
  1528.                CALL    PARSE_DELIMIT
  1529.                LODSB
  1530.                CMP     AL,"-"                  ;Use single box chars?
  1531.                JNZ     CK_DOUBLE
  1532.                MOV     DI,OFFSET SINGLE_BOX
  1533.                JMP     SHORT DO_WINDOW
  1534. CK_DOUBLE:     CMP     AL,"="                  ;Use double box char?
  1535.                JNZ     DO_WINDOW
  1536.                MOV     DI,OFFSET DOUBLE_BOX
  1537.  
  1538. DO_WINDOW:     MOV     CURSOR_FLAG,1           ;No line wraps.
  1539.                MOV     SI,DI
  1540.                MOV     DX,CURSOR_POSN
  1541.                CALL    LINE                    ;Top line of box.
  1542.  
  1543.                LODSB
  1544.                MOV     BP,AX                   ;Border character.
  1545.                CMP     WIN_HEIGHT,0
  1546.                JZ      BOTTOM
  1547. SIDES:         MOV     AX,BP
  1548.                CALL    ATTRIB_CHAR
  1549.                MOV     CX,WIN_WIDTH
  1550.                JCXZ    RIGHT_SIDE
  1551. CENTER:        MOV     AL,SPACE                ;Spaces for center.
  1552.                CALL    ATTRIB_CHAR
  1553.                LOOP    CENTER
  1554. RIGHT_SIDE:    MOV     AX,BP                   ;Right side of box.
  1555.                CALL    ATTRIB_CHAR
  1556.                CALL    DO_WIN_CURSOR
  1557.                DEC     WIN_HEIGHT
  1558.                JNZ     SIDES
  1559.  
  1560. BOTTOM:        CALL    LINE                    ;Bottom line.
  1561.                XOR     AL,AL                   ;EL = 0.
  1562.                JMP     SHORT WINDOW_END
  1563. WINDOW_ERROR:  MOV     AL,1                    ;EL = 1.
  1564. WINDOW_END:    POP     DX                      ;Restore cursor position.
  1565.                PUSH    AX
  1566.                CALL    SET_CURSOR
  1567.                POP     AX
  1568.                RET
  1569.  
  1570. LINE:          LODSB                           ;Left char. of line.
  1571.                CALL    ATTRIB_CHAR
  1572.                LODSB                           ;Center repeat char of line.
  1573.                MOV     CX,WIN_WIDTH
  1574.                JCXZ    CORNER
  1575.                MOV     BP,AX
  1576. NEXT_LINE:     MOV     AX,BP
  1577.                CALL    ATTRIB_CHAR
  1578.                LOOP    NEXT_LINE
  1579. CORNER:        LODSB                           ;Right char. of line.
  1580.                CALL    ATTRIB_CHAR
  1581.                CALL    DO_WIN_CURSOR
  1582.                RET
  1583.  
  1584. DO_WIN_CURSOR: MOV     DX,WIN_CURSOR
  1585.                CALL    SET_CURSOR
  1586.                ADD     WIN_CURSOR,100H         ;Cursor to next line.
  1587.                RET
  1588.  
  1589. ;*************************;
  1590. ;*  SUPPORT SUBROUTINES  *;
  1591. ;*************************;
  1592.  
  1593. DISPLAY_HELP:  MOV     AL,WHITE_ON_BLUE        ;Clear the screen with W/B.
  1594.                CMP     CRT_MODE,7              ;Else, is it MONO mode?
  1595.                JNZ     DO_HELP                 ;If no, use requested color.
  1596.                MOV     AL,WHITE_ON_BLACK       ;Else, use white on black.
  1597. DO_HELP:       CALL    DO_CLS                  ; blue background.
  1598.                MOV     BL,BLUE_ON_WHITE        ;Use inverse video blue on
  1599.                CMP     CRT_MODE,7              ; white for the header.
  1600.                JNZ     DISPLAY_COPY
  1601.                MOV     BL,BLACK_ON_WHITE       ;Or inverse video black on
  1602. DISPLAY_COPY:  MOV     SI,OFFSET COPYRIGHT     ; white if mono display.
  1603.                CALL    DO_CECHO
  1604.                MOV     DI,WHITE_ON_BLUE        ;For the rest of the help menu
  1605.                CMP     CRT_MODE,7              ; use white on blue for color
  1606.                JNZ     NEXT_COPY               ; display and white on black
  1607.                MOV     DI,WHITE_ON_BLACK       ; for mono display.
  1608.  
  1609. NEXT_COPY:     MOV     BX,DI                   ;Retrieve the color.
  1610.                CALL    DISP_STRING             ;Display top part of help.
  1611.                MOV     BP,201H                 ;Display PC Magazine logo
  1612.                MOV     SI,OFFSET PCMAG_LOGO    ; starting at row 2, column 1
  1613.                MOV     BL,INTENSE_ON_RED       ; for the top left corner.
  1614.                CMP     CRT_MODE,7              ;Use high intensity white on
  1615.                JNZ     NEXT_LOGO               ; red if color, else on black
  1616.                MOV     BL,INTENSE              ; if mono.
  1617. NEXT_LOGO:     MOV     DX,BP
  1618.                CALL    SET_CURSOR              ;Set the cursor.
  1619.                CALL    DO_CECHO                ;Print a line of logo.
  1620.                ADD     BP,100H                 ;Next line.
  1621.                CMP     BYTE PTR [SI],0         ;Until null marking end of logo.
  1622.                JNZ     NEXT_LOGO
  1623.  
  1624.                MOV     BX,DI                   ;Retrieve text color.
  1625.                MOV     SI,OFFSET HELP1         ;Display all pages of help
  1626.                CALL    DISP_PAGE               ; pause for a keystroke
  1627.                JZ      HELP_END                ; between pages and as long
  1628.                MOV     SI,OFFSET HELP2         ; as ESC not pressed.
  1629.                CALL    DISP_PAGE
  1630.                JZ      HELP_END
  1631.                MOV     SI,OFFSET HELP3
  1632.                CALL    DISP_PAGE
  1633.                JZ      HELP_END
  1634.                MOV     SI,OFFSET HELP4
  1635.                CALL    DISP_PAGE
  1636.                JZ      HELP_END
  1637.                MOV     SI,OFFSET HELP5
  1638.                CALL    DISP_PAGE
  1639.  
  1640. HELP_END:      MOV     DX,1600H                ;Set cursor on line 22 on exit
  1641.                CALL    SET_CURSOR              ; so DOS won't scroll screen.
  1642.                RET
  1643.  
  1644. ;----------------------------------------------;
  1645. DISP_STRING:   CALL    DO_CECHO                ;Display lines of text
  1646.                CMP     BYTE PTR [SI],0         ; until terminating null found.
  1647.                JNZ     DISP_STRING
  1648.                RET
  1649.  
  1650. DISP_PAGE:     MOV     CX,0F00H                ;Clear last help page (row 15
  1651.                MOV     DX,164FH                ; through row 22) via BIOS
  1652.                MOV     BH,BL                   ; scroll active page.
  1653.                MOV     AX,600H
  1654.                INT     10H
  1655.                MOV     DX,0F00H                ;Set cursor to first line of
  1656.                CALL    SET_CURSOR              ; help.
  1657.                CALL    DISP_STRING
  1658.                MOV     DX,1800H                ;Cursor on line 24 for "press
  1659.                CALL    SET_CURSOR              ; any key" message.
  1660.                MOV     SI,OFFSET MORE
  1661.                MOV     CURSOR_FLAG,1           ;Suppress CR.
  1662.                CALL    DO_CECHO
  1663.                MOV     CURSOR_FLAG,0           ;CR back on.
  1664.                CALL    KEYPRESS                ;Pause for a keystroke.
  1665.                CMP     AH,ESC_SCAN             ;Is it ESC?
  1666.                JZ      PAGE_END
  1667.                CMP     AL,3                    ;Ctrl-C?
  1668.                JZ      PAGE_END
  1669.                OR      AX,AX                   ;Check for Ctrl Break.
  1670. PAGE_END:      RET                             ;Return for next page.
  1671.  
  1672. ;-----------------------------------------------------------------;
  1673. ; INPUT: SI -> string;  OUTPUT SI -> first non-white space or CR. ;
  1674. ;-----------------------------------------------------------------;
  1675. PARSE_DELIMIT: LODSB                           ;Get a byte.
  1676.                CMP     AL,CR
  1677.                JZ      LEADING_END
  1678.                CMP     AL,SPACE                ;Is it a space char or below?
  1679.                JBE     PARSE_DELIMIT
  1680.                CMP     AL,COMMA                ;Or comma?
  1681.                JZ      PARSE_DELIMIT
  1682.                CMP     AL,"/"                  ;Or forward slash?
  1683.                JZ      PARSE_DELIMIT
  1684.                CMP     AL,";"                  ;Or semicolon?
  1685.                JZ      PARSE_DELIMIT           ;If yes, parse.
  1686. LEADING_END:   DEC     SI                      ;Else, adjust pointer to
  1687.                RET                             ; string start.
  1688.  
  1689. ;----------------------------------------------;
  1690. ; INPUT:  SI -> string;  SI preserved.         ;
  1691. ;----------------------------------------------;
  1692. CAPITALIZE:    PUSH    SI
  1693. NEXT_CAP:      LODSB
  1694.                CMP     AL,SPACE                ;Capitalize until first
  1695.                JBE     CAP_END                 ; white space encountered.
  1696.                CMP     AL,"a"
  1697.                JB      NEXT_CAP
  1698.                CMP     AL,"z"
  1699.                JA      NEXT_CAP
  1700.                AND     BYTE PTR [SI - 1],5FH
  1701.                JMP     NEXT_CAP
  1702. CAP_END:       POP     SI
  1703.                RET
  1704.  
  1705. ;----------------------------------------------;
  1706. FIND_END:      LODSB
  1707.                OR      AL,AL                   ;Search string until terminating
  1708.                JNZ     FIND_END                ; ASCIIZ found.
  1709.                RET
  1710.  
  1711. ;-----------------------------------------------------;
  1712. FIND_HEX:      PUSH    SI
  1713. NEXT_H:        LODSB
  1714.                CMP     AL,SPACE
  1715.                JBE     CK_HEX
  1716.                CMP     AL,COMMA
  1717.                JNZ     NEXT_H
  1718. CK_HEX:        MOV     AL,[SI - 2]
  1719.                POP     SI
  1720.                AND     AL,5FH
  1721.                CMP     AL,"H"
  1722.                JNZ     DECIMAL
  1723.                CALL    HEX_INPUT
  1724.                RET
  1725. DECIMAL:       CALL    DECIMAL_INPUT
  1726.                RET
  1727.  
  1728. ;----------------------------------------------------------------------;
  1729. ; INPUT:  SI -> string;                                                ;
  1730. ; OUTPUT: SI -> end of string; AX = number; ZF = 1 if no number found. ;
  1731. ;----------------------------------------------------------------------;
  1732. DECIMAL_INPUT: PUSH    BX
  1733.                PUSH    CX
  1734.                XOR     BX,BX                   ;Start with zero as number.
  1735.                XOR     BP,BP                   ;Number found flag.
  1736. NEXT_DECIMAL:  LODSB                           ;Get a character.
  1737.                SUB     AL,"0"                  ;ASCII to binary.
  1738.                JC      DECIMAL_END             ;If not between 0 and 9, skip.
  1739.                CMP     AL,9
  1740.                JA      DECIMAL_END
  1741.                CBW                             ;Convert byte to word.
  1742.                XCHG    AX,BX                   ;Swap old and new number.
  1743.                MOV     CX,10                   ;Shift to left by multiplying
  1744.                MUL     CX                      ; last entry by ten.
  1745.                ADD     BX,AX                   ;Add new number and store in BX.
  1746.                INC     BP
  1747.                JMP     NEXT_DECIMAL
  1748. DECIMAL_END:   DEC     SI                      ;SI -> string end.
  1749.                MOV     AX,BX
  1750.                OR      BP,BP                   ;Number found flag.
  1751.                POP     CX
  1752.                POP     BX
  1753.                RET
  1754.  
  1755. ;----------------------------------------------;
  1756. HEX_INPUT:     PUSH    BX
  1757.                PUSH    CX
  1758.                XOR     BX,BX
  1759.                XOR     BP,BP
  1760. NEXT_HEX:      LODSB                           ;Get a byte.
  1761.                CMP     AL,"a"
  1762.                JB      GET_HEX
  1763.                AND     AL,5FH
  1764. GET_HEX:       SUB     AL,"0"                  ;ASCII to binary.
  1765.                JC      HEX_END                 ;If not 0 to 9, skip.
  1766.                CMP     AL,9                    ;Is it A - F ?
  1767.                JLE     NOT_ALPHA               ;If no, OK.
  1768.                SUB     AL,7                    ;Else, adjust for alpha.
  1769.                CMP     AL,10                   ;Is it punctuation?
  1770.                JB      HEX_END                 ;If yes, skip.
  1771.                CMP     AL,15                   ;Is it valid?
  1772.                JA      HEX_END                 ;If no, skip.
  1773. NOT_ALPHA:     MOV     CL,4                    ;Shift old number four bits left.
  1774.                SHL     BX,CL
  1775.                OR      BL,AL                   ;Add to number.
  1776.                INC     BP
  1777.                JMP     NEXT_HEX
  1778. HEX_END:       MOV     AX,BX
  1779.                OR      BP,BP
  1780.                POP     CX
  1781.                POP     BX
  1782.                RET
  1783.  
  1784. ;----------------------------------------------;
  1785. ASCIIZ:        PUSH    SI
  1786. NEXT_ASCII:    LODSB                           ;Place a terminating null
  1787.                CMP     AL,"/"                  ; at the end of string.
  1788.                JZ      ASCII
  1789.                CMP     AL,SPACE
  1790.                JA      NEXT_ASCII
  1791. ASCII:         MOV     BYTE PTR [SI - 1],0
  1792.                POP     SI
  1793.                RET
  1794.  
  1795. ;-------------------------------------;
  1796. ;  INPUT: AX = number.  AX preserved  ;
  1797. ;-------------------------------------;
  1798. DEC_OUTPUT:    PUSH    AX
  1799.                MOV     BX,10                   ;Convert to decimal.
  1800.                XOR     CX,CX                   ;Zero in counter.
  1801. NEXT_COUNT:    XOR     DX,DX
  1802.                DIV     BX
  1803.                ADD     DL,'0'                  ;Convert to ASCII.
  1804.                PUSH    DX                      ;Save results.
  1805.                INC     CX                      ;Also increment count.
  1806.                CMP     AX,0                    ;Are we done?
  1807.                JNZ     NEXT_COUNT
  1808.  
  1809. NEXT_NUMBER:   POP     DX                      ;Retrieve numbers.
  1810.                CALL    PRINT_CHAR              ;And write them.
  1811.                LOOP    NEXT_NUMBER
  1812.                POP     AX
  1813.                RET
  1814.  
  1815. ;----------------------------------------------;
  1816. GET_BIOS_DATA: PUSH    DS
  1817.                PUSH    ES
  1818.                MOV     AX,40H                  ;BIOS data area.
  1819.                MOV     DS,AX
  1820.                MOV     SI,BIOS_ACTIVE_PAGE     ;Start with active page.
  1821.                MOV     DI,OFFSET ACTIVE_PAGE
  1822.                MOVSB                           ;Retrieve active page
  1823.                MOV     SI,BIOS_CRT_MODE        ;Retrieve CRT mode, CRT columns,
  1824.                MOV     CX,CRT_DATA_LENGTH      ; CRT length, CRT start.
  1825.                REP     MOVSB
  1826.                MOV     BL,ES:ACTIVE_PAGE       ;Use active page as index
  1827.                XOR     BH,BH                   ; of active cursor position.
  1828.                SHL     BX,1
  1829.                ADD     SI,BX
  1830.                MOVSW
  1831.                XOR     BH,BH
  1832.                MOV     AX,1130H                ;Font information.
  1833.                MOV     DL,24                   ;Assume 25 lines.
  1834.                INT     10H
  1835.                POP     ES
  1836.                MOV     AL,DL
  1837.                STOSB
  1838.                POP     DS
  1839.                RET
  1840.  
  1841. ;----------------------------------------------;
  1842. ; OUTPUT: ES = segment of BATCHMAN data.       ;
  1843. ;----------------------------------------------;
  1844. CK_BAT_DATA:   MOV     BYTE PTR LOOP_COUNT,0   ;Initialize variables to zero.
  1845.                MOV     WORD PTR CURRENT_DIR,0
  1846.                MOV     BX,OFFSET START         ;Point to start of code.
  1847.                NOT     BYTE PTR [BX]           ;Change a byte so no false match.
  1848.                XOR     DX,DX                   ;Start at segment zero.
  1849.                MOV     AX,CS                   ;Store our segment in AX.
  1850. NEXT_PARAG:    INC     DX                      ;Next paragraph.
  1851.                MOV     ES,DX
  1852.                CMP     DX,AX                   ;Is it our segment?
  1853.                JZ      BAT_DATA_END            ;If yes, search is done.
  1854.                MOV     SI,BX                   ;Else, point to our signature.
  1855.                MOV     DI,BX                   ; and offset of possible match.
  1856.                MOV     CX,16                   ;Check 16 bytes for match.
  1857.                REPZ    CMPSB
  1858.                JNZ     NEXT_PARAG              ;If no match, keep looking.
  1859.                MOV     DATA_SEG,ES             ;Save segment of resident data.
  1860. BAT_DATA_END:  RET
  1861.  
  1862. ;----------------------------------------------;
  1863. ; INPUT: CX = 1/18 seconds.                    ;
  1864. ;----------------------------------------------;
  1865. DELAY:         PUSH    DS                      ;Preserve data segment.
  1866.                MOV     AX,40H                  ;Point to BIOS data segment.
  1867.                MOV     DS,AX
  1868. NEXT_TICK:     MOV     AX,DS:[6CH]             ;Retrieve timer low.
  1869. NEXT_DELAY:    MOV     DX,DS:[6CH]             ;Retrieve timer low.
  1870.                CMP     DX,AX                   ;Have we timed out?
  1871.                JZ      NEXT_DELAY              ;If not, wait until timer tick.
  1872.                LOOP    NEXT_TICK
  1873.                POP     DS                      ;Restore data segment.
  1874.                RET
  1875.  
  1876. ;----------------------------------------------;
  1877. ; INPUT: DX = cursor position.                 ;
  1878. ;----------------------------------------------;
  1879. SET_CURSOR:    MOV     CURSOR_POSN,DX          ;Save locally cursor position.
  1880.                MOV     BH,ACTIVE_PAGE
  1881.                MOV     AH,2                    ;Set cursor on active video page.
  1882.                INT     10H
  1883.                RET
  1884.  
  1885. ;----------------------------------------------;
  1886. FIND_FIRST:    MOV     AH,4EH
  1887.                INT     21H
  1888.                RET
  1889.  
  1890. ;----------------------------------------------;
  1891. KEYPRESS:      XOR     AH,AH
  1892.                INT     16H
  1893.                RET
  1894.  
  1895. ;----------------------------------------------;
  1896. GET_DATE:      MOV     AH,2AH
  1897.                INT     21H
  1898.                RET
  1899.  
  1900. GET_TIME:      MOV     AH,2CH
  1901.                INT     21H
  1902.                RET
  1903.  
  1904. ;----------------------------------------------;
  1905. PRINT_STRING:  MOV     AH,9
  1906.                INT     21H
  1907.                RET
  1908.  
  1909. ;----------------------------------------------;
  1910. PRINT_CHAR:    MOV     AH,2
  1911.                INT     21H
  1912.                RET
  1913.  
  1914. DTA            LABEL   BYTE
  1915. READ_BUFFER    =       DTA + SIZE MATCHING
  1916. CHARS          =       READ_BUFFER
  1917. FUNCS          =       READ_BUFFER
  1918. FCB            =       READ_BUFFER
  1919. WRITE_BUFFER   =       READ_BUFFER + SIZE BOOT_SECTOR
  1920.  
  1921. _TEXT          ENDS
  1922.                END     START
  1923.