home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / fileutil / s15.arj / S.ASM next >
Encoding:
Assembly Source File  |  1992-01-17  |  30.9 KB  |  1,222 lines

  1. ;TASM source for S.COM v.1.5.  Copyright (C) DA Nye, 1992, all rights reserved.
  2.  
  3. ideal
  4. model tiny
  5.  
  6. NFILES          EQU 200         ;Max # files in display
  7.  
  8. FILESPEC        EQU 82h         ;Address of filespec in command tail
  9. PATH            EQU 81h         ;Count of chars in path part of filespec here
  10. NORMAL_FILE     EQU 11100001b   ;Attribute for a normal file
  11. TAGGED          EQU 80h         ;Bit to set in attrib to indicate file tagged
  12. DTA_ATTRIB      EQU DTA+21      ;Offsets of elements in DTA
  13. DTA_TIME        EQU DTA+22
  14. DTA_DATE        EQU DTA+24
  15. DTA_SIZE        EQU DTA+26
  16. DTA_NAME        EQU DTA+30
  17. FILE_TIME       EQU bp          ;Addrs of elements in 'fileRecords' after name
  18. FILE_DATE       EQU bp+2
  19. FILE_SIZE       EQU bp+4
  20. COPYVAL         EQU 1           ;Keys for Copy/Move/Delete/Rename operations
  21. DELETEVAL       EQU 2
  22. MOVEVAL         EQU 3
  23. RENAMEVAL       EQU 4
  24.  
  25. codeseg
  26. BOF:
  27. org 100h
  28.  
  29. Start:
  30.     mov sp, OFFSET stackEnd     ;Initialize stack
  31.     sub ax, ax
  32.     push ax
  33.     mov [programSeg], cs        ;Save current value of CS -> program segment
  34.     mov ah, 0Fh                 ;Find display memory
  35.     int 10h
  36.     mov di, 0B000h              ;If mode 7 (= MDA or Herc), video seg=B000h,
  37.     cmp al, 7
  38.     je @@L1
  39.     mov di, 0B800h              ;Otherwise video seg=B800h,
  40. @@L1:
  41.     mov es, di                  ;Unless running under DesqView
  42.     mov cx, 'DE'
  43.     mov dx, 'SQ'
  44.     mov ax, 2B01h
  45.     int 21h
  46.     cmp al, 0FFh
  47.     je @@L2
  48.     mov ah, 0FEh
  49.     int 10h
  50.     mov di, es
  51. @@L2:
  52.     mov [displaySegment], di
  53.     mov es, di                  ;Remember original screen color at lower right
  54.     mov al, [es:3999]
  55.     mov [original], al
  56.     mov es, [2Ch]               ;Find COMSPEC
  57.     sub di, di
  58. @@L3:
  59.     mov si, OFFSET comspec
  60.     mov cx, 8
  61.     repe cmpsb
  62.     jne @@L3
  63.     mov [comspecOff], di
  64.     mov [comspecSeg], es
  65.     mov es, [programSeg]
  66. NewSpec:
  67.     mov si, FILESPEC            ;Examine command line filespec
  68.     mov di, si
  69.     cmp [byte FILESPEC-2], 1    ;If none, use "*.*"
  70.     jbe @@L3
  71. @@L1:
  72.     lodsb
  73.     cmp al, '*'                 ;Look for wildcard chars
  74.     je GetPathLength
  75.     cmp al, '?'
  76.     je GetPathLength
  77.     cmp al, 0Dh
  78.     jne @@L1
  79.     cmp [byte si-2], '\'        ;If none, assume spec is a directory
  80.     jne @@L2
  81.     dec si                      ;If last char was not '\', add it
  82.     jmp SHORT @@L3
  83. @@L2:
  84.     mov [byte si-1], '\'
  85. @@L3:
  86.     mov ax, si                  ;Compute length of path minus filename
  87.     sub ax, di
  88.     mov [PATH], al
  89.     mov di, si
  90. NewDir:
  91.     mov [keepSorted], 0         ;Directory is initially unsorted
  92.     mov si, OFFSET starDotStar  ;Append '*.*',0
  93.     call CopyString
  94.     jmp SHORT Restart
  95. NoRoomError:
  96.     mov dx, OFFSET noRoomMsg    ;Not enough room to run S
  97. Abort:
  98.     call BlankScreen
  99.     mov ah, 9
  100.     int 21h
  101.     mov ax, 4C00h               ;Bye
  102.     int 21h
  103.  
  104. GetPathLength:
  105.     mov al, [byte FILESPEC-2]   ;Null-terminate other filespecs
  106.     sub ah, ah
  107.     mov si, ax
  108.     add si, FILESPEC-1
  109.     mov [si], ah
  110.     mov cx, ax                  ;Count chars in path excluding file name:
  111.     std                         ;Search backward for last '\' or ':'
  112. @@L1:
  113.     lodsb
  114.     cmp al, '\'
  115.     je @@L2
  116.     cmp al, ':'
  117.     loopne @@L1
  118. @@L2:
  119.     cld
  120.     mov [PATH], cl
  121.  
  122. Restart:
  123.     mov ax, OFFSET files        ;Cursor -> first file
  124.     mov [cursor], ax
  125.     mov [top], ax
  126. Restart0:
  127.     mov bx, (EOF-BOF)/16+1      ;Resize memory to amount needed
  128.     mov ah, 4Ah
  129.     int 21h
  130.     jc NoRoomError
  131.     mov ah, 19h                 ;Get default drive for path/spec display
  132.     int 21h
  133.     add al, 'A'
  134.     mov di, OFFSET pathNSpec    ;Store letter, ':\'
  135.     stosb
  136.     mov ax, '\:'
  137.     stosw
  138.     mov si, di                  ;Get path and name of current directory
  139.     sub dl, dl
  140.     mov ah, 47h
  141.     int 21h
  142.     sub al, al                  ;Find end
  143.     mov cx, -1
  144.     repne scasb
  145.     dec di
  146.     mov ax, ' '+100h*' '        ;Add two spaces
  147.     stosw
  148.     mov si, FILESPEC            ;Append filespec
  149.     call CopyString
  150.  
  151. ;Get all file names matching filespec and set up tables
  152. GetFileRecords:
  153.     mov dx, OFFSET DTA          ;Set up DTA
  154.     mov ah, 1Ah
  155.     int 21h
  156.     sub ax, ax                  ;Initialize file size total
  157.     mov [totalSize], ax
  158.     mov [totalSize+2], ax
  159.     mov dx, FILESPEC            ;Get first file name
  160.     mov cl, 37h
  161.     mov ah, 4Eh
  162.     int 21h
  163.     jnc FileFound               ;No files.  Try a different filespec.
  164.     mov si, OFFSET NoFilesMsg
  165.     call Error
  166.     jmp NewFilespec
  167. FileFound:
  168.     mov di, OFFSET fileRecords  ;DI -> storage for file names
  169.     mov bx, OFFSET files        ;BX -> array of files
  170.     sub bx, 2
  171. StoreFileName:
  172.     add bx, 2                   ;For all files that will fit,
  173.     cmp bx, (OFFSET files) + NFILES*2
  174.     jb @@L1
  175.     sub bx, 2
  176.     mov [last], bx
  177.     mov si, OFFSET tooManyMsg
  178.     jmp DoError
  179. @@L1:
  180.     mov [bx], di                ;Store pointer to status/filename in files[]
  181.     mov al, [DTA_ATTRIB]        ;Store status byte
  182.     and al, 3Fh                 ;Top bit is used to indicate file is marked
  183.     stosb
  184.     mov si, OFFSET DTA_NAME     ;Copy file name from DTA to filename storage
  185.     call CopyString
  186.     inc di
  187.     mov si, OFFSET DTA_TIME     ;Copy time, date and size
  188.     mov cx, 4
  189.     rep movsw
  190.     test [DTA_ATTRIB], 10h      ;If not a subdirectory,
  191.     jnz @@L2
  192.     mov si, OFFSET DTA_SIZE     ;Add size to total
  193.     lodsw
  194.     add [totalSize], ax
  195.     lodsw
  196.     adc [totalSize+2], ax
  197. @@L2:
  198.     mov ah, 4Fh                 ;Next filename
  199.     int 21h
  200.     jnc StoreFileName
  201.     mov [last], bx              ;Save pointer to last file entry
  202.     mov al, [keepSorted]        ;If returning from EXEC, need to resort files?
  203.     or al, al
  204.     jz DisplayFiles
  205.     jmp Sort0
  206.  
  207. ;Main loop.  Display files and wait for command.
  208. DisplayFiles:
  209.     call BlankStatus            ;Clear status line
  210.     mov si, OFFSET helpF1       ;Display help key
  211.     call DisplayString
  212.     mov bx, [cursor]
  213.     mov si, [bx]
  214.     lodsb                       ;Get attributes of file at cursor
  215.     mov [attrib], al            ;Save attribute byte
  216.     call DisplayString          ;Display name of highlighted file
  217.     mov bp, si                  ;Save pointer to time, date, size
  218.     test [attrib], 10h          ;If a directory,
  219.     jz @@L7
  220.     mov si, OFFSET dirMsg       ; show '<DIR>' instead of file size
  221.     add di, 2
  222.     call DisplayString
  223.     jmp SHORT @@L9
  224. @@L7:
  225.     lea si, [FILE_SIZE]         ;File size, right justified
  226.     add di, 14
  227.     call WriteLongDecimal
  228. @@L9:
  229.     add di, 18                  ;File date:
  230.     push di
  231.     sub dx, dx
  232.     mov bx, [FILE_DATE]         ;Year
  233.     mov al, bh
  234.     shr al, 1
  235.     add al, 80
  236.     sub ah, ah
  237.     mov cl, 2
  238.     call WriteDecimal
  239.     mov al, '/'
  240.     stosw
  241.     mov ax, bx                  ;Day
  242.     and ax, 1Fh
  243.     mov cx, 2
  244.     call WriteDecimal
  245.     mov al, '/'
  246.     stosw
  247.     mov ax, bx                  ;Month
  248.     mov cl, 5
  249.     shr ax, cl
  250.     and ax, 0Fh
  251.     mov cl, 2
  252.     call WriteDecimal
  253.     pop di
  254.     add di, 12
  255.     push di
  256.     mov bx, [FILE_TIME]         ;File time:
  257.     mov ax, bx                  ;Minutes
  258.     mov cl, 5
  259.     shr ax, cl
  260.     and ax, 3Fh
  261.     mov cx, 2
  262.     call WriteDecimal
  263.     mov al, ':'
  264.     stosw
  265.     mov al, bh                  ;Hours
  266.     mov cl, 3
  267.     shr al, cl
  268.     sub ah, ah
  269.     sub cl, cl
  270.     call WriteDecimal
  271.     cld
  272.     pop di
  273.     add di, 4
  274.     mov dl, [attrib]            ;Display attribute letters
  275.     test dl, 1                  ;Read-only
  276.     jz @@L3
  277.     mov al, 'R'
  278.     stosw
  279. @@L3:
  280.     test dl, 2                  ;Hidden
  281.     jz @@L4
  282.     mov al, 'H'
  283.     stosw
  284. @@L4:
  285.     test dl, 4                  ;System
  286.     jz @@L5
  287.     mov al, 'S'
  288.     stosw
  289. @@L5:
  290.     test dl, 20h                ;Archive
  291.     jz @@L6
  292.     mov al, 'A'
  293.     stosw
  294. @@L6:
  295.     mov al, 186                 ;Display divider
  296.     stosw
  297.     mov si, OFFSET pathNSpec    ;Display path and filespec
  298.     call DisplayString
  299.     mov di, 158                 ;Display total size of displayed files
  300.     mov si, OFFSET totalSize
  301.     call WriteLongDecimal
  302.     mov bx, [top]
  303.     mov di, 160
  304.     cld
  305. DisplayNext:
  306.     mov ah, [normal]            ;Set to inverse video if cursor line
  307.     cmp bx, [cursor]
  308.     jne @@L0
  309.     mov ah, [inverse]
  310. @@L0:
  311.     cmp bx, [last]              ;If done with files,
  312.     jle @@L0a
  313.     mov cx, 16                  ;Blank out name area
  314.     jmp SHORT @@L7
  315. @@L0a:
  316.     mov si, [bx]                ;Get table entry for a file
  317.     lodsb                       ;Get status byte
  318.     test al, TAGGED             ;If file has been tagged, display '>'
  319.     mov al, '>'
  320.     jnz @@L1
  321.     mov al, ' '
  322. @@L1:
  323.     stosw
  324.     mov cx, 9                   ;In field of 9,
  325. @@L2:
  326.     lodsb                       ;Display filename up to extension
  327.     cmp al, '.'
  328.     je @@L3
  329.     or al, al
  330.     jz @@L6
  331. @@L2a:
  332.     stosw
  333.     loop @@L2
  334. @@L3:
  335.     cmp cx, 9                   ;Check for special cases of '.', '..'
  336.     je @@L2a
  337.     cmp [byte si-2], '.'
  338.     je @@L2a
  339.     mov al, ' '                 ;Else pad with spaces out to 9 chars
  340.     rep stosw
  341. @@L4:
  342.     mov cx, 6                   ;Display extension in field of 6
  343. @@L5:
  344.     lodsb
  345.     or al, al
  346.     jz @@L7
  347.     stosw
  348.     loop @@L5
  349.     jmp SHORT @@L7
  350. @@L6:
  351.     add cx, 6                   ;Just pad with blanks if no extension
  352. @@L7:
  353.     mov al, ' '
  354.     rep stosw
  355.     cmp di, 4000                ;Stop at screenful
  356.     je GetCommand
  357.     cmp di, 3872
  358.     jb @@L8
  359.     sub di, 3808                ;Next column
  360. @@L8:
  361.     add di, 128                 ;Next row
  362.     add bx, 2
  363.     jmp DisplayNext
  364.  
  365. ;Get command
  366. GetCommand:
  367.     call HideCursor
  368.     mov es, [programSeg]
  369.     mov ah, 8                   ;Get keypress
  370.     sub ch, ch
  371. @@L1:
  372.     inc ch
  373.     int 21h
  374.     or al, al
  375.     jz @@L1
  376.     mov ah, ch                  ;AH = 2 if aux code, 1 if plain ASCII
  377.     call ToUpper
  378.     mov di, OFFSET CommandKeys  ;Look it up, get pointer to routine
  379.     mov cx, NCOMMANDS
  380.     repne scasw
  381.     jne InvalidCommand
  382.     add di, CommandAddrs-CommandKeys-2
  383.     mov bx, [cursor]            ;SI -> file record for highlighted file
  384.     mov si, [bx]
  385.     call ShowCursor
  386.     jmp [word di]               ;Jump to routine
  387. InvalidCommand:
  388.     call Beep
  389.     jmp GetCommand
  390.  
  391. ;********************************* Commands ***********************************
  392.  
  393. Up:
  394.     sub bx, 2
  395.     jmp SHORT NewLine
  396.  
  397. Down:
  398.     add bx, 2
  399.     jmp SHORT NewLine
  400.  
  401. Left:
  402.     sub bx, 48
  403.     jmp SHORT NewLine
  404.  
  405. Right:
  406.     add bx, 48
  407.     jmp SHORT NewLine
  408.  
  409. PageUp:
  410.     sub bx, 238
  411.     jmp SHORT NewLine
  412.  
  413. PageDown:
  414.     add bx, 238
  415.  
  416. NewLine:
  417.     cmp bx, [last]              ;Make sure cursor is still within bounds
  418.     jbe @@L1
  419.     mov bx, [last]
  420. @@L1:
  421.     cmp bx, OFFSET files
  422.     jae @@L2
  423.     mov bx, OFFSET files
  424. @@L2:
  425.     cmp bx, [top]               ;Slide window if off screen
  426.     jae @@L3
  427.     mov [top], bx
  428.     jmp SHORT @@L4
  429. @@L3:
  430.     mov ax, bx
  431.     sub ax, 238
  432.     cmp ax, [top]
  433.     jb @@L4
  434.     mov [top], ax
  435. @@L4:
  436.     mov [cursor], bx
  437.     jmp DisplayFiles
  438.  
  439. Tag:
  440.     test [byte si], NOT NORMAL_FILE  ;Only allow marking of normal files
  441.     jnz @@L1
  442.     xor [byte si], TAGGED
  443. @@L1:
  444.     jmp DisplayFiles
  445.  
  446. Go:
  447.     mov [byte inputString], 0   ;Initialize to no user-entered command tail
  448. Go0:
  449.     lodsb                       ;Get attribute byte
  450.     mov dx, si                  ;Join path and file name
  451.     mov si, PATH
  452.     mov di, OFFSET buffer
  453.     call Join
  454.     test al, 10h                ;If a directory,
  455.     jz DoExec
  456. ChangeDir:
  457.     mov dx, OFFSET buffer
  458.     mov ah, 3Bh                 ;Change to it
  459.     int 21h
  460.     mov [byte PATH], 0          ;No path now
  461.     mov di, FILESPEC            ;Read in contents of new directory
  462.     jmp NewDir
  463. DoExec:
  464.     mov si, dx                  ;Find extension
  465. @@L1:
  466.     lodsb
  467.     cmp al, '.'
  468.     je @@L2
  469.     or al, al
  470.     jne @@L1
  471.     jmp SHORT @@L4
  472. @@L2:
  473.     mov di, OFFSET extensions   ;If .EXE, .COM or .BAT, execute it
  474.     mov dx, si
  475. @@L3:
  476.     mov si, dx
  477.     mov cx, 3
  478.     repe cmpsb
  479.     je @@L5
  480.     sub al, al
  481.     repne scasb
  482.     cmp [byte di], 0
  483.     jne @@L3
  484. @@L4:
  485.     mov si, OFFSET notExecMsg   ;Else error, not an executable file
  486.     jmp DoError
  487. @@L5:
  488.     call BlankScreen
  489.     mov dx, OFFSET buffer       ;If .BAT, need COMMAND.COM, otherwise don't
  490.     cmp [byte di], 0
  491.     jne @@L6
  492.     mov si, OFFSET CC
  493. label DoEdit near               ;Edit function enters here
  494.     mov di, OFFSET EXECCmdLine+1
  495.     call CopyString             ;Store '/c ' or '/c <editor> ' to command tail
  496.     mov dl, cl
  497.     mov si, OFFSET buffer       ;Append path\file
  498.     call CopyString
  499.     add dl, cl
  500.     mov [EXECCmdLine], dl       ;Store length, append CR
  501.     mov [byte di], 13
  502.     mov dx, [comspecOff]
  503.     mov ds, [comspecSeg]
  504. @@L6:
  505.     cmp [byte cs:inputString], 0    ;If a command tail was entered,
  506.     jz DoDOS
  507.     push ds
  508.     mov ds, [cs:programSeg]
  509.     mov di, OFFSET EXECCmdLine+1
  510.     mov al, ' '                 ;Add a space
  511.     stosb
  512.     mov si, OFFSET inputString  ;Add command tail
  513.     call CopyString
  514.     inc cl
  515.     add [EXECCmdLine], cl
  516.     mov [byte di], 13
  517.     pop ds
  518. DoDOS:
  519.     mov cx, bx
  520.     mov bx, (inputString-BOF)/16+1  ;Release unneeded memory
  521.     mov ah, 4Ah
  522.     int 21h
  523.     jc SysErr
  524.     call BlankScreen
  525.     push cx
  526.     mov [cs:temp], sp           ;Do EXEC
  527.     mov bx, OFFSET EXECParams
  528.     mov ax, 4B00h
  529.     int 21h
  530.     mov bx, cs                  ;Restore critical registers
  531.     mov ds, bx
  532.     mov es, bx
  533.     mov ss, bx
  534.     mov sp, [temp]
  535.     pop bx
  536.     jnc @@L2
  537.     mov si, OFFSET sysErrMsg    ;EXEC error
  538.     cmp al, 8                   ;If return code = 8, no room
  539.     jne @@L1
  540.     mov si, OFFSET noExecRoomMsg
  541. @@L1:
  542.     jmp DoError
  543. @@L2:
  544.     mov [byte EXECCmdLine], 0   ;Tidy up
  545.     jmp Restart0
  546.  
  547. SysErr:
  548.     mov si, OFFSET sysErrMsg
  549. DoError:
  550.     call Error
  551.     jmp DisplayFiles
  552.  
  553. GoCL:
  554.     mov si, OFFSET commandTailMsg   ;Prompt for command tail
  555.     call Query
  556.     mov si, [bx]
  557.     jmp Go0
  558.  
  559. DOS:
  560.     mov dx, [comspecOff]
  561.     mov ds, [comspecSeg]
  562.     jmp DoDOS
  563.  
  564. Edit:
  565.     lea dx, [si+1]              ;Join path and file name
  566.     mov si, PATH
  567.     mov di, OFFSET buffer
  568.     call Join
  569.     mov si, OFFSET editor       ;Invoke editor
  570.     mov [byte inputString],0
  571.     jmp DoEdit
  572.  
  573. Copy:
  574.     mov [byte CMDR], COPYVAL    ;Set Copy/Move/Delete/Remove key to Copy
  575.     jmp SHORT DoCopy
  576.  
  577. Delete:
  578.     mov [byte CMDR], DELETEVAL  ;Set Copy/Move/Delete/Remove key to Delete
  579.     jmp SHORT DoCMDR
  580.  
  581. Move:
  582.     mov [byte CMDR], MOVEVAL    ;Set Copy/Move/Delete/Rename key to Move
  583.  
  584. DoCopy:
  585.     mov si, OFFSET DestMsg      ;If Copy or Move, prompt for destination
  586.     call Query
  587.     mov di, si
  588.     mov [byte si], '\'          ;Append '\'
  589.     inc si
  590.     mov [temp], si
  591.     cmp [byte CMDR], MOVEVAL
  592.     jne DoCMDR
  593.     mov ax, [word FILESPEC]     ;If Move to same drive,
  594.     mov dx, [word inputString]
  595.     cmp ah, ':'
  596.     je @@L1
  597.     cmp dh, ':'
  598.     jne @@L2
  599. @@L1:
  600.     cmp ax, dx
  601.     jne DoCMDR
  602. @@L2:
  603.     mov [byte CMDR], RENAMEVAL  ; do Rename instead (much faster)
  604. DoCMDR:
  605.     mov bp, OFFSET files - 2    ;For each file
  606. CMDRNext:
  607.     add bp, 2
  608.     cmp bp, [last]
  609.     jbe @@L0
  610.     jmp GetFileRecords
  611. @@L0:
  612.     mov si, [bp]                ;Skip if not tagged
  613.     lodsb
  614.     test al, TAGGED
  615.     jz CMDRNext
  616.     xor [byte si-1], TAGGED     ;Else untag
  617.     mov dx, si
  618.     mov si, PATH                ;Source path\filename -> sourceFileSpec
  619.     mov di, OFFSET sourceFileSpec
  620.     call Join
  621.     cmp [byte CMDR], DELETEVAL  ;If not Deleting
  622.     je @@L4
  623.     mov si, dx                  ;Append current file's name to destination path
  624.     mov di, [temp]
  625.     call CopyString
  626.     cmp [byte CMDR], RENAMEVAL  ;If Rename, do it
  627.     jne @@L2
  628. label DoRename near
  629.     mov dx, OFFSET sourceFileSpec
  630.     mov di, OFFSET inputString
  631.     mov ah, 56h
  632.     int 21h
  633.     jnc @@L1
  634.     mov dx, di                  ;If rename failed, try deleting target name
  635.     mov ah, 41h
  636.     int 21h
  637.     jnc DoRename                ; and try again
  638.     jmp SHORT CantOpen          ;If delete failed, abort
  639. @@L1:
  640.     jmp CMDRNext
  641. @@L2:
  642.     mov dx, OFFSET sourceFileSpec   ;Copy or Move: open source, dest files
  643.     mov ax, 3D00h
  644.     int 21h
  645.     jc CantOpen
  646.     mov [sourceHandle], ax
  647.     sub cx, cx
  648.     mov dx, OFFSET inputString
  649.     mov ax, 3C00h
  650.     int 21h
  651.     jc CantOpen
  652.     mov [destHandle], ax
  653. @@L3:
  654.     mov bx, [sourceHandle]      ;Read a bufferful
  655.     mov cx, 512
  656.     mov dx, OFFSET buffer
  657.     mov ah, 3Fh
  658.     int 21h
  659.     jc ReadError
  660.     mov bx, [destHandle]        ;Write it
  661.     mov cx, ax
  662.     mov ah, 40h
  663.     int 21h
  664.     jc WriteError
  665.     cmp ax, cx
  666.     jb FullError
  667.     cmp cx, 512                 ;Loop until done
  668.     je @@L3
  669.     mov ah, 3Eh                 ;Close files
  670.     mov bx, [sourceHandle]
  671.     int 21h
  672.     mov bx, [destHandle]
  673.     int 21h
  674.     cmp [CMDR], MOVEVAL         ;If Move, now do Delete
  675.     jne @@L1
  676. @@L4:
  677.     mov bx, OFFSET sourceFileSpec   ;Delete file
  678.     mov ah, 41h
  679.     int 21h
  680.     jnc @@L1
  681.  
  682. CantOpen:
  683.     mov si, OFFSET cantOpenMsg
  684.     jmp DoError
  685. ReadError:
  686.     mov si, OFFSET readMsg
  687.     jmp DoError
  688. WriteError:
  689.     mov si, OFFSET writeMsg
  690.     jmp DoError
  691. FullError:
  692.     mov si, OFFSET fullMsg
  693.     jmp DoError
  694.  
  695. NewFilespec:
  696.     mov si, OFFSET newSpecMsg   ;Prompt for new filespec
  697.     call Query
  698.     inc al
  699.     mov [FILESPEC-2], al        ;Store count of chars
  700.     mov si, OFFSET inputString
  701.     mov di, FILESPEC
  702.     call CopyString             ;Copy new filespec to command tail area
  703.     mov [byte di], 0Dh          ;Append CR
  704.     jmp NewSpec                 ;Process new filespec
  705.  
  706. Rename:
  707.     lea dx, [si + 1]            ;Join path and current name
  708.     mov si, PATH
  709.     mov di, OFFSET sourceFileSpec
  710.     call Join
  711.     mov si, OFFSET newNameMsg   ;Prompt for new name of file or directory
  712.     call Query
  713.     jmp DoRename
  714.  
  715. Drive:
  716.     mov si, OFFSET newDriveMsg  ;Prompt for letter of drive to change to
  717.     call QueryChar
  718.     sub al, 'A'
  719.     mov dl, al
  720.     mov ah, 0Eh
  721.     int 21h
  722.     cmp dl, al                  ;Ask again if that drive doesn't exist
  723.     jb @@L1
  724.     mov si, OFFSET badDriveMsg
  725.     jmp DoError
  726. @@L1:
  727.     mov [byte FILESPEC-2], 0    ;If successful, start with default filespec
  728.     jmp NewSpec
  729.  
  730. Sort:
  731. ;
  732. ;Sort algorithm:
  733. ; 1) make up array of records {pointer to field to sort | tag}, one for each
  734. ;    file, in 'buffer'.
  735. ; 2) bubble-sort these records
  736. ; 3) copy file record pointers in 'files' to 'buffer' in order of sorted tags
  737. ; 4) copy file record pointers back to 'files' in new order
  738. ;
  739.     mov si, OFFSET sortMsg      ;Prompt for sort field
  740.     call QueryChar
  741.     cmp al, 'N'                 ;Check for legal sort field option
  742.     je Sort0
  743.     cmp al, 'E'
  744.     je Sort0
  745.     cmp al, 'D'
  746.     je Sort0
  747.     mov si, OFFSET genErrorMsg
  748.     jmp DoError
  749. Sort0:
  750.     mov [keepSorted], al        ;Remember for later resorting after EXEC
  751.     mov dl, al
  752.     sub dh, dh                  ;DH = tag (position of file in current order)
  753.     mov di, OFFSET buffer
  754.     mov bx, OFFSET files
  755. @@L1:
  756.     mov si, [bx]                ;Find field to sort:  get pointer to record
  757. @@L1a:
  758.     inc si
  759.     mov cx, si
  760.     cmp dl, 'N'                 ;If Name, already pointing at it
  761.     je @@L4
  762.     sub ah, ah                  ;If Date, find null at end of name
  763.     cmp dl, 'E'                 ;If Ext find '.' or end of name
  764.     jne @@L2
  765.     cmp [byte si], '.'          ;'.' and '..' are special cases
  766.     je @@L1a
  767.     mov ah, '.'
  768. @@L2:
  769.     lodsb
  770.     or al, al
  771.     je @@L3
  772.     cmp al, ah
  773.     jne @@L2
  774. @@L3:
  775.     dec si                      ;If Ext, back up to '.' or null
  776.     cmp dl, 'E'
  777.     je @@L4
  778.     add si, 3                   ;If Date, advance to date field
  779. @@L4:
  780.     mov ax, si                  ;Store pointer to field to sort
  781.     stosw
  782.     mov al, dh                  ;Store tag
  783.     stosb
  784.     inc dh                      ;Bump tag
  785.     add bx, 2                   ;Loop until no more files
  786.     cmp bx, [last]
  787.     jbe @@L1
  788. DoSort:
  789.     lea bp, [di-3]              ;BP -> last
  790.     push bp
  791. @@L0:
  792.     mov bx, OFFSET buffer       ;Do bubble sort
  793. @@L1:
  794.     mov si, [bx]
  795.     cmp [word si-1], '.'        ;Leave '.' and '..' alone
  796.     je @@L4
  797.     mov di, [bx+3]
  798.     cmp dl, 'D'                 ;If sorting Dates, compare one word
  799.     jne @@L2
  800.     cmpsw
  801.     jmp SHORT @@L3
  802. @@L2:
  803.     mov cx, -1                  ;Else compare bytes until not equal
  804.     repe cmpsb
  805. @@L3:
  806.     jbe @@L4                    ;If first field > second
  807.     mov ax, [bx]                ;Exchange field pointers and tags
  808.     xchg ax, [bx+3]
  809.     mov [bx], ax
  810.     mov al, [bx+2]
  811.     xchg al, [bx+5]
  812.     mov [bx+2], al
  813. @@L4:
  814.     add bx, 3                   ;Loop until no more files this pass
  815.     cmp bx, bp
  816.     jb @@L1
  817.     sub bp, 3
  818.     cmp bp, OFFSET buffer       ;Loop until no more passes
  819.     jne @@L0
  820. OrderByTags:
  821.     mov di, OFFSET buffer       ;Arrange file pointers in order of tags
  822.     mov si, OFFSET files
  823.     pop bp
  824. @@L1:
  825.     mov bl, [di+2]              ;Get tag
  826.     sub bh, bh
  827.     add bx, bx
  828.     mov ax, [bx+si]             ;Get file ptr associated with that tag
  829.     stosw                       ;Store in place of field pointer in sort buffer
  830.     inc di
  831.     cmp di, bp
  832.     jbe @@L1
  833.     mov si, OFFSET buffer
  834.     mov di, OFFSET files
  835. @@L2:
  836.     movsw                       ;Copy file pointers back in new order
  837.     inc si
  838.     cmp di, [last]
  839.     jbe @@L2
  840.     jmp DisplayFiles
  841.  
  842. Help:
  843.     call BlankStatus            ;Display help on status line
  844.     mov si, OFFSET helpMsg
  845.     call DisplayString
  846.     mov ah, 8
  847.     int 21h
  848.     jmp DisplayFiles
  849.  
  850. Exit:
  851.     call BlankScreen
  852.     mov ax, 4C00h               ;Bye
  853.     int 21h
  854.  
  855. ;******************************* Subroutines **********************************
  856.  
  857. WriteLongDecimal:
  858. ;
  859. ;Display 4-byte integer
  860. ;  IN: SI -> number, DI -> display address of least significant digit
  861. ; OUT: none
  862. ;USED: AX CX DX
  863.     push cx
  864.     push dx
  865.     push di
  866.     mov ax, [si]
  867.     mov dx, [si + 2]
  868.     mov cx, 10000               ;Divide by 10000, show quotient|remainder
  869.     div cx
  870.     xchg ax, dx
  871.     or dx, dx
  872.     jz @@L1
  873.     mov cl, 4
  874.     call WriteDecimal
  875.     mov ax, dx
  876. @@L1:
  877.     sub cl, cl
  878.     call WriteDecimal
  879.     pop di
  880.     pop dx
  881.     pop cx
  882.     ret
  883.  
  884. WriteDecimal:
  885. ;
  886. ;Display a decimal number in inverse video, writing digits backwards from right.
  887. ;  IN: AX = number, CL = field width with leading 0s (no leading 0s if CL = 0)
  888. ;      ES:DI -> Video RAM where rightmost digit will go
  889. ; OUT: AH = [inverse], DI -> left of first digit
  890. ;USED: AL, CX
  891.     push bx
  892.     push dx
  893.     std
  894.     mov bx, 10
  895.     sub ch, ch
  896. @@L1:
  897.     sub dx, dx                  ;Get a digit
  898.     div bx
  899.     xchg ax, dx                 ;Write it to display, right to left
  900.     add al, '0'
  901.     mov ah, [inverse]
  902.     stosw
  903.     xchg ax, dx
  904.     cmp cx, 0                   ;If CX > 0, loop even if AX = 0
  905.     jg @@L2
  906.     or ax, ax                   ;Else loop only if AX > 0 (more digits left)
  907.     jz @@L3
  908. @@L2:
  909.     loop @@L1
  910. @@L3:
  911.     pop dx
  912.     pop bx
  913.     mov ah, [inverse]
  914. Ret1:
  915.     ret
  916.  
  917. DisplayString:
  918. ;
  919. ;Display string on status line.
  920. ;  IN: SI -> string (null-terminated), ES:DI -> status line, AH = attribute
  921. ; OUT: DI is advanced past end of string
  922. ;USES: AL SI
  923. @@L1:
  924.     lodsb
  925.     or al, al
  926.     jz Ret1
  927.     stosw
  928.     jmp @@L1
  929.  
  930. DisplayInputString:
  931. ;
  932. ;Same as above but show cursor at end of line
  933. ;  IN, OUT, USES -- see above
  934.     push bx
  935.     push dx
  936.     call DisplayString
  937.     mov dx, di                  ;Set cursor to end of printed string
  938.     add dx, 2
  939.     shr dl, 1
  940.     sub dh, dh
  941.     mov ah, 2
  942.     sub bx, bx
  943.     int 10h
  944.     pop dx
  945.     pop bx
  946.     ret
  947.  
  948.  
  949. Error:
  950. ;
  951. ;Beep, display string on status line and wait for keypress (any key)
  952. ;  IN: SI -> string
  953. ; OUT: ES -> program segment
  954. ;USED: AX
  955.     call Beep
  956.     call BlankStatus
  957.     call DisplayString          ;Display error string
  958.     mov si, OFFSET ErrorMsg     ;Display 'Press any key' message
  959.     call DisplayString
  960.     mov ah, 8
  961.     int 21h
  962.     mov es, [programSeg]
  963.     ret
  964.  
  965. Query:
  966. ;
  967. ;Prompt for string input on status line
  968. ;  IN: SI -> message
  969. ; OUT: SI -> null at end of ASCIIZ string input, AX = length (excluding null)
  970. ;USED: none
  971.     push bx
  972.     push cx
  973.     push dx
  974.     push di
  975.     push es
  976.     call BlankStatus
  977.     call DisplayInputString
  978.     mov cx, 80                  ;Get input
  979.     mov dx, OFFSET inputString
  980.     mov si, dx
  981.     sub bx, bx
  982.     mov ah, 3Fh
  983.     int 21h
  984.     sub ax, 2
  985.     add si, ax
  986.     mov [byte si], 0            ;Null-terminate it
  987.     pop es
  988.     pop di
  989.     pop dx
  990.     pop cx
  991.     pop bx
  992.     ret
  993.  
  994. QueryChar:
  995. ;
  996. ;Prompt for single character input
  997. ;  IN: SI -> message
  998. ; OUT: AL = character (lower case converted to upper)
  999. ;USED: AH
  1000.     push es
  1001.     call BlankStatus
  1002.     call DisplayInputString     ;Display string
  1003.     mov ah, 8                   ;Get char
  1004.     int 21h
  1005.     call ToUpper
  1006.     pop es
  1007.     ret
  1008.  
  1009. BlankStatus:
  1010. ;
  1011. ;Clear top line of display to inverse video
  1012. ;  IN: none
  1013. ; OUT: ES = video segment, DI = 0
  1014. ;USED: AX CX
  1015.     mov ax, [displaySegment]
  1016.     mov es, ax
  1017.     sub di, di
  1018.     mov al, ' '
  1019.     mov ah, [inverse]
  1020.     mov cx, 80
  1021.     rep stosw
  1022.     sub di, di
  1023.     ret
  1024.  
  1025. Join:
  1026. ;
  1027. ;Copy counted path, then ASCIIZ file name to buffer and null-terminate
  1028. ;  IN: SI -> path, DX -> file, DI -> destination
  1029. ; OUT: DI -> null at end of copied string, CX = total chars excluding null
  1030. ;USED: none
  1031.     push ax
  1032.     push si
  1033.     lodsb                       ;Get count of chars in path (a counted string)
  1034.     sub ah, ah
  1035.     mov cx, ax
  1036.     rep movsb                   ;Copy it to destination
  1037.     mov cx, ax
  1038.     mov si, dx                  ;Now copy file name (null-terminated)
  1039.     call CopyString
  1040.     add cx, ax                  ;Sum string counts -> CX
  1041.     mov [byte di], 0            ;Null-terminate the result
  1042.     pop si
  1043.     pop ax
  1044.     ret
  1045.  
  1046. CopyString:
  1047. ;
  1048. ;Copy null-terminated string.
  1049. ;  IN: SI -> string, DI -> destination
  1050. ; OUT: CX = length (excluding null), DI -> terminating null of copied string
  1051. ;USED: AL
  1052.     mov cx, -1
  1053. @@L1:
  1054.     lodsb                       ;Copy string
  1055.     stosb
  1056.     or al, al                   ;Until null at end is encountered
  1057.     loopnz @@L1                 ;Accumulate count of chars
  1058.     neg cx                      ;Adjust count
  1059.     sub cx, 2
  1060.     dec di                      ;DI -> terminating null
  1061.     ret
  1062.  
  1063. HideCursor:
  1064. ;
  1065. ;Move cursor off bottom of screen
  1066. ;  IN: none
  1067. ; OUT: none
  1068. ;USED: AH
  1069.     push bx
  1070.     push dx
  1071.     mov dx, 1900h
  1072. DoCursor:
  1073.     sub bh, bh
  1074.     mov ah, 2
  1075.     int 10h
  1076.     pop dx
  1077.     pop bx
  1078.     ret
  1079.  
  1080. ShowCursor:
  1081. ;
  1082. ;Put cursor back at 0,0
  1083. ;  IN: none
  1084. ; OUT: none
  1085. ;USED: AH
  1086.     push bx
  1087.     push dx
  1088.     sub dx, dx
  1089.     jmp DoCursor
  1090.  
  1091. Beep:
  1092. ;
  1093. ;Output a bell char
  1094. ;  IN: none
  1095. ; OUT: none
  1096. ;USED: AH DL
  1097.     mov dl, 7
  1098.     mov ah, 2
  1099.     int 21h
  1100.     ret
  1101.  
  1102. BlankScreen:
  1103. ;
  1104. ;Clear screen to original color and home cursor
  1105. ;  IN: none
  1106. ; OUT: none
  1107. ;USED: AX
  1108.     push cx
  1109.     push es
  1110.     push di
  1111.     mov es, [cs:displaySegment] ;Blank screen
  1112.     sub di, di
  1113.     mov al, ' '
  1114.     mov ah, [original]
  1115.     mov cx, 25 * 80
  1116.     rep stosw
  1117.     pop di
  1118.     pop es
  1119.     pop cx
  1120.     ret
  1121.  
  1122. ToUpper:
  1123. ;
  1124. ;Convert lower to upper case
  1125. ;  IN: AL = char
  1126. ; OUT: AL = char
  1127. ;USED: none
  1128.     cmp al, 'a'
  1129.     jb @@L1
  1130.     cmp al, 'z'
  1131.     ja @@L1
  1132.     add al, 'A'-'a'
  1133. @@L1:
  1134.     ret
  1135.  
  1136. ;********************************** Data **************************************
  1137.  
  1138.  
  1139. ;Command dispatch table:  aux,2 or ASCII,1 paired to command routine addresses
  1140.  
  1141. commandKeys     db 72,2, 80,2, 75,2, 77,2, 73,2, 81,2, 'T',1, 13,1, 'C',1
  1142.                 db 'D',1, 'M',1, 'R',1, 'E',1, 'F',1, 'V',1, 'S',1
  1143.                 db 60,2, 10,1, 59,2, 27,1
  1144. commandAddrs    dw Up, Down, Left, Right, PageUp, PageDown, Tag, Go, Copy
  1145.                 dw Delete, Move, Rename, Edit, NewFilespec, Drive, Sort
  1146.                 dw DOS, GoCL, Help, Exit
  1147. NCOMMANDS       EQU (commandAddrs-commandKeys)/2
  1148.  
  1149.  
  1150. ;Strings
  1151.  
  1152. helpMsg         db 'Copy Delete Edit Filespec Move Ren Sort '
  1153.                 db 'Tag driVe Enter=cd/run F2=DOS Esc=exit',0
  1154. helpF1          db 'Help F1',186,0
  1155. noFilesMsg      db 'No matching files or invalid path',0
  1156. noRoomMsg       db 'Out of room$'
  1157. badPathMsg      db 'Bad path',0
  1158. sysErrMsg       db 'System error',0
  1159. cantOpenMsg     db "Can't open file",0
  1160. writeMsg        db 'Write error',0
  1161. fullMsg         db 'Disk full',0
  1162. readMsg         db 'Read error',0
  1163. notExecMsg      db 'Not a directory or executable file',0
  1164. badDriveMsg     db "Drive doesn't exist",0
  1165. genErrorMsg     db 'Error',0
  1166. tooManyMsg      db 'Too many files',0
  1167. noExecRoomMsg   db 'Not enough memory',0
  1168. ErrorMsg        db '.  Press any key.',0
  1169. destMsg         db 'Where to?',0
  1170. newSpecMsg      db 'New filespec:',0
  1171. newDriveMsg     db 'New drive:',0
  1172. sortMsg         db 'Sort on:  Name Ext Date',0
  1173. newNameMsg      db 'New name:',0
  1174. commandTailMsg  db 'Command tail:',0
  1175. dirMsg          db '<DIR>',0
  1176. extensions      db 'EXECOMBAT',0
  1177. starDotStar     db '*.*',0
  1178. original        db ?                ;Original screen color to restore on exit
  1179. inverse         db 70h              ;Black on white
  1180. normal          db 17h              ;White on blue
  1181. editor          db '/C E ', 12 dup (0)
  1182. cc              db '/C ',0
  1183. comspec         db  'COMSPEC='
  1184.  
  1185.  
  1186. ;EXEC function parameter block
  1187.  
  1188. EXECParams      dw 0
  1189. EXECCmdLineOff  dw OFFSET EXECCmdLine
  1190. programSeg      dw 0
  1191.                 dw -1, -1 , -1 , -1
  1192.  
  1193. EXECCmdLine     db 0, 80 dup (?)
  1194. sourceFileSpec  EQU EXECCmdLine     ;Second use for this space during Copy etc.
  1195.  
  1196.  
  1197. ;Variables, buffers
  1198.  
  1199.                 dw 128 dup (?)  ;Stack (here for protection during EXEC)
  1200. stackEnd:
  1201. keepSorted      db ?            ;Holds sort subcommand char if this dir sorted
  1202. CMDR            db ?            ;Key for Copy/Move/Delete/Rename actions
  1203. attrib          db ?            ;File attribute byte
  1204. totalSize       dw ?, ?         ;Sum of file sizes
  1205. sourceHandle    dw ?            ;Source handle for Copy, etc.
  1206. destHandle      dw ?            ;Destination handle for Copy etc.
  1207. cursor          dw ?            ;Position in 'files' of highlighted file
  1208. last            dw ?            ;Position in 'files' of last file in directory
  1209. top             dw ?            ;Position in 'files' of file at top of screen
  1210. temp            dw ?            ;Holds SP during EXEC, other uses
  1211. comspecSeg      dw ?            ;Segment of environment
  1212. comspecOff      dw ?            ;Offset of 'C:\COMMAND.COM' in environment
  1213. displaySegment  dw ?            ;Segment of display RAM
  1214. pathNSpec       db 80 dup (?)   ;Default path and current filespec for display
  1215. DTA             db 64 dup (?)   ;Disk Transfer Area
  1216. inputString     db 80 dup (?)   ;String returned by 'Query', other uses
  1217. files           dw NFILES dup (?)   ;Array of pointers to file records
  1218. fileRecords     db 80*NFILES dup (?)    ;File records: attrib/name/time/date
  1219. buffer          db 512 dup (?)  ;Buffer for Copy, etc.
  1220.  
  1221. EOF:
  1222. end Start