home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / ENTERPRS / CPM / UTILS / F / PETASCII.ARK / PETASCII.MAC < prev   
Text File  |  1987-09-17  |  23KB  |  893 lines

  1. ;
  2. ; PETASCII.MAC  --  Version 1.0
  3. ;
  4. ; USAGE:
  5. ;
  6. ;    PETASCII {d:}<fn.ft> {d:}{<fn.ft>}
  7. ;
  8. ; Copies the input file (first file reference) to the output file
  9. ; (second file reference), converting PETASCII characters to ASCII
  10. ; characters.  Also inserts linefeeds after carriage returns where
  11. ; they do not already exist.
  12. ;
  13. ; If the drivecode d: of the input file is omitted the current drive
  14. ; is assumed.  An input filename is required.
  15. ;
  16. ; If the output file reference is omitted, it will be given the same
  17. ; name as the input file.  Any file of the same name on the destination
  18. ; drive will be renamed to filetype .BAK, which will preserve the
  19. ; input file if both input and output use the same drive.  If the
  20. ; output filename does not include a filetype, the output file will
  21. ; be given the same filetype as the input file.  If a drive speci-
  22. ; fication is not given, output will default to the current drive.
  23. ;
  24. ; Version 1.0, 9/17/87, Gene Pizzetta -- for the I/O routines I have
  25. ; relied heavily on David E. Cortesi, "A Programmer's Notebook:
  26. ; Utilities for CP/M-80"
  27. ;
  28. ; This program was developed using SLRMAC from SLR Systems.  I will
  29. ; also assemble with MAC, but you will need to have Z80.LIB on your
  30. ; default disk.
  31. ;
  32. WarmStart    equ    0000h    ; vector to BIOS re-boot
  33. CpmBasePage    equ    high WarmStart
  34. Bdos        equ    0005h    ; vector to CP/M BDOS
  35. BdosAddress    equ    0006h    ; address in the Bdos jump
  36. CpmFcb        equ    005Ch    ; default FCB, file operand 1
  37. CpmFcb2        equ    CpmFcb+10h    ; file operand 2
  38. CpmBuffer    equ    0080h    ; default file buffer 80..FFh
  39. CpmTail        equ    CpmBuffer    ; command tail
  40. TransientArea    equ    0100h    ; start of the .COM file
  41. SuccessCode    equ    0000h    ; program success code
  42. FailCode    equ    0FF00h    ; program failure code
  43. ;
  44. ; BDOS functions
  45. BdosKeyin    equ    01h    ; one console input byte
  46. BdosType    equ    02h    ; type byte at console
  47. BdosPrint    equ    05h    ; print one byte
  48. BdosString    equ    09h    ; type "string$" at console
  49. BdosLine    equ    0Ah    ; read a buffered console line
  50. BdosTestCon    equ    0Bh    ; test console, A > 0 if key hit
  51. BdosSetDrive    equ    0Eh    ; select default drivecode
  52. BdosOpen    equ    0Fh    ; open a disk file
  53. BdosClose    equ    10h    ; close (output) file
  54. BdosSrch1    equ    11h    ; initial directory search
  55. BdosSrchn    equ    12h    ; successive directory searches
  56. BdosErase    equ    13h    ; erase a file
  57. BdosRead    equ    14h    ; read disk sequential
  58. BdosWrite    equ    15h    ; write disk sequential
  59. BdosMake    equ    16h    ; create a file directory entry
  60. BdosRename    equ    17h    ; rename file
  61. BdosDrive    equ    19h    ; get default drive number
  62. BdosSetBuffer    equ    1Ah    ; set file buffer address
  63. BdosGetAlloc    equ    1Bh    ; get address of allocation vector
  64. BdosGetDPB    equ    1Fh    ; get address of DPB
  65. BdosFreeSpace    equ    2Eh    ; get disk free space
  66. BdosReturn    equ    6Ch    ; program return code
  67. BdosParse    equ    98h    ; parse filename
  68. ;
  69. ; ASCII values
  70. AsciiLF        equ    0Ah    ; LineFeed
  71. AsciiCR        equ    0Dh    ; CarriageReturn
  72. CpmEof        equ    1Ah    ; ^Z, end of ascii file
  73. AsciiBlank    equ    20h    ; blank
  74. AsciiDEL    equ    7Fh    ; DEL, highest valid byte
  75. ;
  76.     MACLIB    Z80
  77. ;
  78. ; Program entry macro:  find bottom of Bdos and set the stack
  79. ; there.  Call label Main with HL --> end of storage.  Provide
  80. ; code to support the ABORT macro and the command tail.
  81. ;
  82. PROLOG    MACRO
  83.     ORG    TransientArea    ;; start at TPA
  84.     LHLD    BdosAddress    ;; HL --> BDOS base
  85.     MVI    L,255
  86.     INX    H        ;; HL --> page above BDOS
  87.     DCR    H        ;; HL --> page below BDOS
  88.     SPHL            ;; SP --> high storage
  89.     DCR    H        ;; Allow 256 byte stack
  90.     DCX    H        ;; HL --> last usable byte
  91.     CALL    Main        ;; call mainline code
  92. ;
  93. ; On normal exit, the main routine will return here ...
  94. EPILOG:
  95.     JMP    WarmStart    ;; do a warm start
  96. ;
  97. ; If a fatal error is detected the ABORT macro will come
  98. ; here, with the address of a "message$" string on top of
  99. ; the stack and all registers as they were.  When debugging
  100. ; with DDT, set a breakpoint here to trap an abort.  When
  101. ; using SID, set a pass-counter at label ERROREXIT.
  102. ;
  103. ERROREXIT:
  104.     POP    D        ;; type the string on
  105.     MVI    C,BdosString    ;; ..the console
  106.     CALL    Bdos
  107.     LXI    D,FailCode    ;; kill any active
  108.     MVI    C,BdosReturn    ;; ..submit file
  109.     CALL    Bdos
  110.     JMP    WarmStart    ;; and terminate
  111.     ENDM
  112. ;
  113. ; Bdos-service macro:  call Bdos for a service, saving all
  114. ; registers except A and sometimes HL.  Load DE with the
  115. ; service parameter if one was specified.
  116. ;
  117. SERVICE    MACRO    ?S,?DE
  118.     PUSH    B
  119.     PUSH    D
  120.   IF (?S NE 12) AND (?S NE 24) AND (?S NE 27)
  121.     IF (?S NE 29) AND (?S NE 31)
  122.     PUSH    H
  123.     ENDIF
  124.   ENDIF
  125.   IF NOT NUL ?DE
  126.     LXI    D,?DE
  127.   ENDIF
  128.     MVI    C,?S
  129.     CALL    Bdos
  130.   IF (?S NE 12) AND (?S NE 24) AND (?S NE 27)
  131.     IF (?S NE 29) AND (?S NE 31)
  132.     POP    H
  133.     ENDIF
  134.   ENDIF
  135.     POP    D
  136.     POP    B
  137.     ENDM
  138. ;
  139. ; Abort-the-program-with-a-message macro:  The operand
  140. ; is the address of a message in storage.
  141. ;
  142. ; The macro puts the address of the message on the
  143. ; stack, preserving all registers for use in debugging.  It
  144. ; branches to the ERROREXIT assembled by PROLOG, where the
  145. ; message will be printed.
  146. ;
  147. ABORT    MACRO    ?MSG
  148.     PUSH    H
  149.     LXI    H,?MSG
  150.     XTHL
  151.     JMP    ERROREXIT
  152.     ENDM
  153. ;
  154. ; Macro to make it easy to origin the assembler to the next
  155. ; page boundary.
  156. ;
  157. ORGPAGE    MACRO
  158.     ORG    ($+255) AND 0FF00h
  159.     ENDM
  160. ;
  161. ; Macro to test the console and, if a key has been hit, to
  162. ; jump to a named location.  No registers are changed, so
  163. ; the macro can be inserted anywhere in the code.
  164. ;
  165. TESTCON    MACRO    WHERE
  166.     local    skippit
  167.     local    endtest
  168.     push    h
  169.     push    d
  170.     push    b
  171.     push    psw
  172.     mvi    c,BdosTestCon
  173.     call    Bdos        ;; test the console
  174.     lxi    h,endtest    ;; assume no key hit
  175.     ora    a        ;; was one?
  176.     JRZ    skippit        ;; jump if not
  177.     lxi    h,WHERE        ;; yes, prepare to exit
  178. skippit    pop    psw        ;; restore A, flags,
  179.     pop    b        ;; regs BC,
  180.     pop    d        ;; ..and DE
  181.     xthl            ;; restore H, stack address
  182.     ret            ;; go to 'endtest' or WHERE
  183. endtest    equ    $
  184.     ENDM
  185. ;
  186. ; Program begins here ...
  187. ;
  188.     PROLOG
  189. ;
  190. ; Input Variables:  used by SetUpInput and GetChar.
  191. ;
  192. PFCB        dw    CpmBuffer+1    ; FCB addresses for BdosParse
  193.         dw    0        ; (service function 152)
  194. InFCB        ds    36    ; Input FCB
  195. InIndex        db    0    ; Index over CpmBuffer
  196. ;
  197. ; Output Variables:  used by SetUpOutput, PutChar, DumpOut.
  198. ;
  199. OutFCB        ds    36    ; Output FCB
  200. OutType        ds    3    ; hold for real output filetype
  201. OutBuffer    dw    0    ; address of OutBuffer
  202. OutIndex    dw    0    ; index over OutBuffer
  203. OutLimit    dw    0    ; negative of size of OutBuffer
  204. DollarType    db    '$$$'    ; type of an output workfile
  205. BAKType        db    'BAK'    ; type of BAK file
  206. ;
  207. ; Messages:
  208. ;
  209. MsgNoName    db    'An input filename is required.$'
  210. MsgNoFile    db    'Input file not found.$'
  211. MsgNoBuffer    db    'Not enough room for an output buffer.$'
  212. MsgNoParseIn    db    'Illegal input filename.$'
  213. MsgNoParseOut    db    'Illegal output filename.$'
  214. MsgAmbig    db    'The output file may not be ambiguous.$'
  215. MsgNoMake    db    'Can''t create the work file.$'
  216. MsgWrite    db    'Error writing the work file.$'
  217. MsgNoClose    db    'Error closing the work file.$'
  218. MsgRename    db    'Error renaming work file to proper name.$'
  219. MsgRenameEx    db    'Error renaming existing file to .BAK.$'
  220. MsgSignOn:    db    'PETASCII      Version 1.0',AsciiCR,AsciiLF,'$'
  221. ;
  222. ; the main program -- initialization
  223. ;
  224. Main:    lxi    d,MsgSignOn    ; print sign on message
  225.     mvi    c,BdosString
  226.     call    Bdos
  227. ;
  228.     call    SetUpInput    ; open the input file
  229.     lxi    b,InFCB        ; BC --> default fileref
  230.     lxi    d,OBspace    ; DE --> start of buffer space
  231. ;            prolog sets HL --> end of buffer space
  232.     call    SetUpOutput    ; prepare the output stuff
  233. ;
  234. ; the main program -- main loop
  235. ;
  236.     call    GetChar        ; get first character
  237. ;
  238. MainWhile:
  239.     cpi    CpmEof        ; while not EOF ...
  240.     JRZ    MainEnd
  241. ;
  242.     cpi    AsciiCR        ; is it a carriage return?
  243.     JRZ    IsReturn    ; (yes, handle it)
  244. ;
  245.     cpi    041h        ; is it control, number, symbol?
  246.     JRC    MainPut        ; (yes, print it)
  247. ;
  248.     cpi    05Bh        ; is it PETASCII lowercase?
  249.     JRC    MakeLower    ; (yes, make ASCII lower)
  250. ;
  251.     cpi    061h        ; is it a symbol?
  252.     JRC    MainPut        ; (yes, print it)
  253. ;
  254.     cpi    07Bh        ; is it PETASCII low uppercase?
  255.     JRC    MakeUpper1    ; (yes, make ASCII upper)
  256. ;
  257.     cpi    0C1h        ; is it a symbol?
  258.     JRC    ClearHigh    ; (yes, handle it)
  259. ;
  260.     cpi    0DBh        ; is it PETASCII high uppercase?
  261.     JRC    MakeUpper2    ; (yes, make ASCII upper)
  262. ;
  263.     JR    ClearHigh    ; handle any leftovers
  264. ;
  265. ; MakeLower:  it's a PETASCII lowercase character which we make
  266. ; into ASCII lowercase by adding 32 to it ...
  267. ;
  268. MakeLower:
  269.     adi    20h
  270.     JR    MainPut
  271. ;
  272. ; MakeUpper1:  it's a PETASCII uppercase character in the 61h-7Ah
  273. ; range which we make into ASCII uppercase by subtracting 32 ...
  274. ;
  275. MakeUpper1:
  276.     sui    20h
  277.     JR    MainPut
  278. ;
  279. ; MakeUpper2:  it's a PETASCII uppercase character in the C1h-DAh
  280. ; range which we make into ASCII uppercase by substracting 128 ...
  281. ;
  282. MakeUpper2:
  283.     sui    80h
  284.     JR    MainPut
  285. ;
  286. ; IsReturn:  it's a carriage return.  We send it and then determine
  287. ; if the next character is a linefeed.  If it's not, we send one.
  288. ;
  289. IsReturn:
  290.     call    PutChar        ; send the carriage return
  291.     call    GetChar        ; get the next character
  292.     cpi    AsciiLF        ; is it a linefeed?
  293.     JRZ    MainPut        ; (yes, send it)
  294.     push    psw        ; no, save the character
  295.     mvi    a,AsciiLF
  296.     call    PutChar        ; send a linefeed
  297.     pop    psw        ; recover character
  298.     jmp    MainWhile    ; ..and loop
  299. ;
  300. ; ClearHigh:  we have a character with the high bit set so we must
  301. ; reset it and fall through to send it ...
  302. ;
  303. ClearHigh:
  304.     ani    AsciiDEL
  305. ;
  306. ; MainPut:  we come here with the adjusted character in A.  We
  307. ; must send it, get the next character, and loop ...
  308. ;
  309. MainPut:
  310.     call    PutChar
  311.     call    GetChar
  312.     jmp    MainWhile    ; ..end while
  313. ;
  314. MainEnd:
  315.     call    FinishOutput    ; complete output file
  316.     ret            ; end main
  317. ;
  318. ; I/O subroutines --
  319. ;
  320. ; GetChar:  return the next byte of input in A.  The default
  321. ; buffer at 80h..FFh is used.  Since we are dealing with ASCII
  322. ; files, CpmEof (^Z) is the end of file mark.  When it appears,
  323. ; don't advance the index past it, so further call will keep on
  324. ; returning CpmEof indefinitely.
  325. ; Preserves:  BC, DE, HL
  326. ;
  327. GetChar:
  328.     push    h        ; save a work register
  329.     lda    InIndex
  330.     inr    a        ; InIndex = InIndex + 1
  331.     JRNZ    GetChar2
  332. ;
  333. ; InIndex was FFh, the buffer is empty.  Put EOF in the buffer
  334. ; so that if physical end of file occurs, we will see it as
  335. ; logical end of file instead.
  336. ;
  337.     push    d
  338.     push    b
  339.     lxi    h,CpmBuffer
  340.     mvi    m,CpmEof    ; put CpmEof in buffer
  341.     xchg            ; DE --> buffer
  342.     mvi    c,BdosSetBuffer
  343.     call    Bdos        ; set buffer address
  344.     lxi    d,InFCB
  345.     mvi    c,BdosRead
  346.     call    Bdos        ; read a record
  347.     pop    b
  348.     pop    d
  349.     mvi    a,80h        ; InIndex = 80h
  350. ;
  351. ; At this point there is either data or CpmEof at the
  352. ; location addressed by InIndex = A.
  353. ;
  354. GetChar2:
  355.     sta    InIndex
  356.     mvi    h,CpmBasePage    ; form an address
  357.     mov    l,a
  358.     mov    a,m        ; A = data
  359.     cpi    CpmEof        ; if it's EOF,
  360.     JRNZ    GetChar3
  361.     lxi    h,InIndex
  362.     dcr    m        ; ..InIndex = InIndex - 1
  363. GetChar3:
  364.     pop    h
  365.     ret
  366. ;
  367. ; PutChar:  write the byte in A into the output buffer.  If the
  368. ; buffer is full, (OutIndex=OutLimit), then dump it first.
  369. ; Preserves:  All
  370. ;
  371. PutChar:
  372.     push    h
  373.     push    d
  374.     lhld    OutIndex
  375.     xchg            ; DE = OutIndex
  376.     lhld    OutLimit
  377.     dad    d        ; carry = OutIndex=OutLimit
  378.     JRNC    PutChar2
  379. ;
  380.     call    DumpOut        ; dump the buffer to the file
  381.     lxi    d,0        ; OutIndex = 0
  382. ;
  383. PutChar2:
  384.     lhld    OutBuffer
  385.     dad    d        ; HL --> next output byte spot
  386.     mov    m,a
  387.     inx    d
  388.     xchg
  389.     shld    OutIndex    ; OutIndex = OutIndex +1
  390.     pop    d
  391.     pop    h
  392.     ret
  393. ;
  394. ; DumpOut:  write buffered output data to the work file.  Called
  395. ; in two cases:  (1) when the buffer is full (OutIndex=OutLimit)
  396. ; and (2) at the end of the program when OutIndex is
  397. ; unpredictable and might even be zero.  The buffer must be a
  398. ; multiple of 128 bytes, but needn't be on any special boundary.
  399. ; Preserves:  All
  400. ;
  401. DumpOut:
  402.     push    psw
  403.     push    h
  404.     push    d
  405.     push    b
  406.     lhld    OutIndex
  407.     mov    a,h
  408.     ora    l        ; is there any data at all?
  409.     JRZ    DumpOut9    ; ..no data? do nothing.
  410. ;
  411. ; When the buffer is full, OutIndex mod 128 is zero.  At the
  412. ; end of the run, though, OutIndex could be in mid-record.  In
  413. ; that case, fill the last record with EOF marks.
  414. ;
  415.     xchg            ; DE = OutIndex
  416.     lhld    OutBuffer
  417.     dad    d        ; HL --> next output byte
  418. DumpOut2:
  419.     mov    a,e
  420.     ani    128-1        ; test OutIndex mod 128
  421.     JRZ    DumpOut3    ; (it was zero)
  422.     mvi    m,CpmEof    ; ..stick in an EOF
  423.     inx    d        ; ..step OutIndex
  424.     inx    h        ; .. and step storage pointer
  425.     
  426.     JR    DumpOut2
  427. ;
  428. ; The buffer now contains a whole number of 128-byte records.
  429. ; Write them all.  Keep the limit index in BC, and the current
  430. ; record's index in DE.
  431. ;
  432. DumpOut3:
  433.     mov    b,d        ; BC = index of last record
  434.     mov    c,e
  435.     dcx    b        ; ..less one
  436.     lxi    d,0        ; DE = starting index of 0
  437. DumpOut4:
  438.     lhld    OutBuffer
  439.     dad    d
  440.     xchg            ; DE --> record, HL has index
  441.     SERVICE    BdosSetBuffer
  442.     SERVICE    BdosWrite,OutFCB
  443.     ora    a        ; check for disk full, etc.
  444.     JRNZ    DumpOutError
  445. ;
  446.     lxi    d,128
  447.     dad    d        ; step index (in HL)
  448.     xchg            ; DE = index to next record
  449.     call    CmpBD        ; set flags fro BC vs DE
  450.     JRNC    DumpOut4    ; continue if limit <= index
  451. ;
  452. DumpOut9:
  453.     pop    b
  454.     pop    d
  455.     pop    h
  456.     pop    psw
  457.     ret
  458. ;
  459. DumpOutError:
  460.     ABORT    MsgWrite
  461. ;
  462. ; CmpBD:  compare BC to DE as a 16-bit unsigned integer, setting
  463. ; the machine flags as for a CMP instruction.
  464. ; Preserves:  All except the flags
  465. ;
  466. CmpBD:
  467.     push    h
  468.     mov    h,a
  469.     mov    a,b
  470.     cmp    d
  471.     JRNZ    CmpBDZ
  472.     mov    a,c
  473.     cmp    e
  474. CmpBDZ:    mov    a,h
  475.     pop    h
  476.     ret
  477. ;
  478. ; SetUpInput:  initialize the input file for utility I/O with
  479. ; GetChar.  Requires definition of InFCB, InIndex (InVars).
  480. ; Preserves:  All
  481. ;
  482. SetUpInput:
  483.     push    psw
  484.     push    b
  485.     push    d
  486.     push    h
  487. ;
  488. ; Prepare the input file for reading
  489. ;
  490.     lxi    h,InFCB        ; put addr of InFCB into PFCB
  491.     shld    PFCB+2
  492.     lxi    d,PFCB        ; parse filename to InFCB
  493.     mvi    c,BdosParse
  494.     call    Bdos
  495. ;
  496. ; If input filename omitted, abort
  497. ;
  498.     shld    PFCB        ; save return code
  499.     mvi    a,0FFh        ; check return code
  500.     cmp    h
  501.     JRNZ    SUI1        ; parsed okay
  502.     cmp    l
  503.     JRNZ    SUI1
  504.     ABORT    MsgNoParseIn
  505. SUI1:    lxi    h,InFCB+1
  506.     mvi    a,AsciiBlank
  507.     cmp    m
  508.     JRNZ    SUI2        ; parse successful, continue
  509.     ABORT    MsgNoName
  510. ;
  511. ; get an input drivecode for the FCB
  512. ;
  513. SUI2:    dcx    h
  514.     mov    a,m        ; A = given drivecode
  515.     ora    a        ; was it omitted?
  516.     JRNZ    SUI3
  517.     SERVICE    BdosDrive    ; get, e.g., A=00, B=01
  518.     inr    a        ; make it A=01, B=02, etc.
  519. SUI3:
  520.     sta    InFCB        ; explicit drivecode in FCB
  521. ;
  522. ; open the input FCB
  523. ;
  524.     SERVICE    BdosOpen,InFCB
  525.     inr    a        ; if no file exists, abort
  526.     JRNZ    SUI4
  527.     ABORT    MsgNoFile
  528. ;
  529. ;    InIndex = FFh
  530. ;
  531. SUI4:
  532.     mvi    a,0FFh
  533.     sta    InIndex
  534. ;
  535.     pop    h
  536.     pop    d
  537.     pop    b
  538.     pop    psw
  539.     ret
  540. ;
  541. ; FillZero:  replicate 00h, beginning at DE, for BC bytes
  542. ;
  543. FillZero:
  544.     push    psw
  545.     xra    a
  546.     JR    FillX
  547. ;
  548. ; FillA:  replicate the byte in A, beginning at DE, for BC bytes.
  549. ; If the length is 2 or greater, use MoveHtoD to do the work.  If
  550. ; the length is 1, that won't work.
  551. ; Preserves:  A, BC, HL
  552. ; Returns:  DE advanced by BC
  553. ;
  554. FillA:
  555.     push    psw
  556. FillX:                ; FillZero joins here
  557.     push    h
  558.     stax    d        ; fill one byte
  559.     inx    d        ; ..step,
  560.     dcx    b        ; ..count the one
  561.     mov    h,a        ; save fill byte
  562.     mov    a,b        ; guard against case of BC=0001
  563.     ora    c
  564.     JRZ    FillZ
  565.     mov    a,h        ; recover fill byte
  566.     mov    h,d        ; create address of fill data
  567.     mov    l,e        ; ..in storage
  568.     dcx    h        ; HL --> first byte, DE --> second
  569.     call    MoveHtoD    ; do propogation-move
  570. FillZ:    inx    b        ; restore entry value of b
  571.     pop    h
  572.     pop    psw
  573.     ret
  574. ;
  575. ; MoveHtoD:  Z80 LDIR instruction.  It does [HL++] --> [DE++] for
  576. ; BC times.  BC must be 1 or more.  BC is preserved.  That is handy for
  577. ; repetitive use of the routine.
  578. ;
  579. MoveHtoD:
  580.     push    b
  581.     ldir
  582.     pop    b
  583.     ret
  584. ;
  585. ; Delimiter:  HL addresses a byte.  Find out if it is one of the
  586. ; standard CP/M token delimiters.
  587. ; Preserves:  BC, DE, HL
  588. ; Returns:  Z flag true if byte is a delimiter
  589. ;        A = the byte itself
  590. ; Note:  The tests are made in descending order by ASCII value.
  591. ; This "rnc" returns if the byte is equal, or if it is greater
  592. ; and hence cannot possibly be one of the bytes tested later.
  593. ;
  594. Delimiter:
  595.     mov    a,m
  596.     cpi    ']'        ; delimiters for option lists
  597.     rnc
  598.     cpi    '['
  599.     rnc
  600.     cpi    '='        ; delimits filerefs in, e.g., ren
  601.     rnc
  602.     cpi    ';'        ; delimits passwords (BDOS 3)
  603.     rnc
  604.     cpi    '/'        ; delimits optional parameters
  605.     rnc
  606.     cpi    '.'        ; delimits filetypes
  607.     rnc
  608.     cpi    ','        ; delimits tokens (supposedly)
  609.     rnc
  610.     cpi    ' '        ; only token delimiter that works
  611.     rnc
  612.     cmp    a        ; control character -- force Z true
  613.     ret
  614. ;
  615. ; SetUpOutput:  prepare the utility output mechanism.
  616. ; A missing filetype from the command line will be filled in with
  617. ; the filetype of the input file (from InFCB).
  618. ; Input:  DE --> start of output buffer
  619. ;      HL --> end of output buffer (as PROLOG gives it)
  620. ;      CpmFcb2 contains output fileref
  621. ; Preserves:  All
  622. ; Note:  this code assumes that the OutVars and FileMessages
  623. ; units from this file have been included.
  624. ; It aborts the program under these conditions:
  625. ;    MsgAmbigOut, if the output fileref is ambiguous
  626. ;    MsgNoBuffer, if the output buffer is less than 256 bytes
  627. ;    MsgNoMake, if "outname.$$$" can't be defined
  628. ;    MsgNoClose, if "outname.$$$" doesn't close successfully
  629. ;    MsgNoRename, if the rename to change the type doesn't work
  630. ;    MsgNoParseIn, if input filename is illegal
  631. ;    MsgNoParseOut, if output filename is illegal
  632. ;
  633. SetUpOutput:
  634.     push    psw
  635.     push    b
  636.     push    d
  637.     push    h
  638. ;
  639. ; prepare the buffer address, index, and index limit.  The limit
  640. ; is the 2s complement of the buffer-size -- see DumpOut for use
  641. ;
  642.     xchg            ; HL --> start of buffer
  643.     mov    a,l        ; ensure buffer starts on 128-multiple
  644.     dcr    a
  645.     ori    128-1
  646.     mov    l,a
  647.     inx    h
  648.     shld    OutBuffer
  649.     xchg            ; DE --> start, HL --> end
  650.     mov    a,d
  651.     cma
  652.     mov    d,a
  653.     mov    a,e
  654.     cma
  655.     mov    e,a
  656.     inx    d        ; DE = 2s complement of buffer start
  657.     dad    d        ; HL = buffer size
  658.     mov    a,h        ; ..which we want to ensure is at
  659.     ora    a        ; least 256 bytes ...
  660.     JRNZ    SUO2        ; (it is)
  661.     ABORT    MsgNoBuffer
  662. SUO2:
  663.     mov    a,l        ; ensure buffer size is a multiple
  664.     ani    128        ; ..of 128 bytes, too
  665.     cma
  666.     mov    l,a
  667.     mov    a,h
  668.     cma
  669.     mov    h,a
  670.     inx    h
  671.     shld    OutLimit    ; OutLimit = 2s complement of size
  672.     lxi    h,0
  673.     shld    OutIndex    ; OutIndex = 0
  674. ;
  675. ; fill filename and type in OutFCB with spaces
  676. ;
  677.     mvi    a,AsciiBlank
  678.     lxi    b,11
  679.     lxi    d,OutFCB+1
  680.     call    FillA
  681. ;
  682. ; make the rest of the FCB zeroes
  683. ;
  684.     lxi    b,36-12
  685.     call    FillZero
  686. ;
  687. ; see if there's an output fileref in the command tail and, if so,
  688. ; parse it into the OutFCB
  689. ;
  690.     lda    PFCB        ; is there more in the tail
  691.     cpi    0        ; if the return code = 0,
  692.     JRNZ    SUO2a        ; ..there's no more
  693.     lda    PFCB+1
  694.     cpi    0
  695.     JRNZ    SUO2a
  696.     JR    SUO2b        ; (no more, get default)
  697. SUO2a:    lxi    h,OutFCB    ; there's more ... try parsing
  698.     shld    PFCB+2        ; ..it to see if it's a fileref
  699.     lxi    d,PFCB
  700.     mvi    c,BdosParse    ; parse second fileref
  701.     call    Bdos
  702.     mvi    a,0FFh        ; check return code
  703.     cmp    h
  704.     JRNZ    SUO2b        ; parsed okay
  705.     cmp    l
  706.     JRNZ    SUO2b
  707.     ABORT    MsgNoParseOut
  708. ;
  709. ; Get an output drivecode for the FCB
  710. ;
  711. SUO2b:    lxi    h,OutFCB
  712.     mov    a,m        ; A = given drivecode
  713.     ora    a        ; was it omitted?
  714.     JRNZ    SUO2c        ; (no, store it)
  715.     SERVICE    BdosDrive    ; get, e.g., A=00, B=01
  716.     inr    a        ; make it A=01, B=02, etc.
  717. ;
  718. SUO2c:    sta    OutFCB
  719. ;
  720. ; get an output filename for the FCB, and make sure that
  721. ; the name given is explicit (has no question marks).
  722. ;
  723. SUO3:
  724.     lxi    b,8
  725.     lxi    d,OutFCB+1    ; DE --> destination
  726.     lxi    h,InFCB+1    ; HL --> 2nd operand filename
  727.     lda    OutFCB+1    ; was it omitted?
  728.     cpi    AsciiBlank
  729.     cz    MoveHtoD    ; if so, copy over default
  730.     lxi    h,OutFCB+1    ; HL --> the final filename
  731.     mvi    a,'?'        ; BC still has the length
  732.     call    ScanForA    ; look for "?" in name
  733.     JRNZ    SUO4        ; (there are none)
  734.     ABORT    MsgAmbig
  735. ;
  736. ; get an output filetype and save it.  Make sure it isn't
  737. ; ambiguous, either.
  738. ;
  739. SUO4:
  740.     lxi    b,3        ; BC = length of filetype
  741.     lxi    d,OutType    ; DE --> destination (not FCB)
  742.     lxi    h,OutFCB+9    ; HL --> 2nd operand filetype
  743.     mov    a,m
  744.     cpi    AsciiBlank    ; was one given?
  745.     JRNZ    SUO5        ; (yes, move it)
  746.     lxi    h,InFCB+9    ; ..no, use default filetype
  747. SUO5:
  748.     call    MoveHtoD    ; move filetype to OutType
  749.     lxi    h,OutType    ; ..then check the chosen type
  750.     mvi    a,'?'
  751.     call    ScanForA    ; look for "?" in type
  752.     JRNZ    SUO6        ; (none found)
  753.     ABORT    MsgAmbig
  754. ;
  755. ; Prepare a workfile for output
  756. ; move '$$$' to the output FCB filetype
  757. ;
  758. SUO6:
  759.     lxi    h,DollarType    ; "$$$" for use in work file
  760.     lxi    d,OutFCB+9
  761.     lxi    b,3        ; length of filetype
  762.     call    MoveHtoD
  763. ;
  764. ; erase any existing workfile of that name
  765. ; (note: if drive is R/O, we die here)
  766. ;
  767.     SERVICE    BdosErase,OutFCB
  768. ;
  769. ; make a file of the workfile's name
  770. ;
  771.     SERVICE    BdosMake,OutFCB
  772.     inr    a        ; if the make fails, abort
  773.     JRNZ    SUO7
  774.     ABORT    MsgNoMake
  775. ;
  776. SUO7:
  777.     pop    h
  778.     pop    d
  779.     pop    b
  780.     pop    psw
  781.     ret
  782. ;
  783. ; FinishOutput:  close the work file and rename to proper type.
  784. ; Preserves:  All
  785. ;
  786. FinishOutput:
  787.     push    psw
  788.     push    b
  789.     push    d
  790.     push    h
  791. ;
  792.     call    DumpOut        ; write any buffered output
  793. ;
  794.     lxi    d,OutFCB    ; then close the work file
  795.     mvi    c,BdosClose
  796.     call    Bdos
  797.     inr    a        ; FFh signals failure
  798.     JRNZ    FinO2        ; (wasn't that)
  799.     ABORT    MsgNoClose
  800. ;
  801. ; OutFCB presently named "y:outname.$$$".  Stuff in the type
  802. ; of the real output file, and erase that file.  This is where,
  803. ; if the output fileref had any question marks, we could do a
  804. ; lot of damage!
  805. ;
  806. FinO2:
  807.     lhld    OutType
  808.     lda    OutType+2
  809.     shld    OutFCB+9
  810.     sta    OutFCB+9+2
  811.     lxi    d,OutFCB
  812.     mvi    c,BdosOpen
  813.     call    Bdos
  814.     ora    a        ; does the file exist?
  815.     JRNZ    FinO2a        ; (no, continue)
  816. ;
  817.     lhld    BAKType
  818.     lda    BAKType+2
  819.     shld    OutFCB+9
  820.     sta    OutFCB+9+2
  821.     lxi    d,OutFCB
  822.     mvi    c,BdosErase    ; erase any .BAK file existing
  823.     call    Bdos
  824. ;
  825.     lxi    h,OutFCB    ; move .BAK filename to new name
  826.     lxi    d,OutFCB+16
  827.     lxi    b,16
  828.     call    MoveHtoD
  829.     lhld    OutType        ; put output filename in old name
  830.     lda    OutType+2
  831.     shld    OutFCB+9
  832.     sta    OutFCB+9+2
  833.     lxi    d,OutFCB
  834.     mvi    c,BdosRename    ; rename existing file to filename.BAK
  835.     call    Bdos
  836.     inr    a        ; FFh means failure
  837.     JRNZ    FinO2a        ; (not the case)
  838.     ABORT    MsgRenameEx
  839. ;
  840. ; Now set the FCB up so that it reads:
  841. ;    OutFCB+0: y outname $$$
  842. ;    OutFCB+16: - outname typ
  843. ; and do the rename function to rename the work file.
  844. ;
  845. FinO2a:    lxi    h,OutFCB
  846.     lxi    d,OutFCB+16
  847.     lxi    b,16
  848.     call    MoveHtoD
  849.     lhld    DollarType
  850.     mov    a,h
  851.     shld    OutFCB+9
  852.     sta    OutFCB+9+2
  853.     lxi    d,OutFCB
  854.     mvi    c,BdosRename
  855.     call    Bdos
  856.     inr    a        ; FFh means failure
  857.     JRNZ    FinO3        ; (not the case)
  858.     ABORT    MsgRename
  859. ;
  860. FinO3:
  861.     pop    h
  862.     pop    d
  863.     pop    b
  864.     pop    psw
  865.     ret
  866. ;
  867. ; ScanForA:  scan BC bytes of HL --> text, looking for a match to
  868. ; the byte in A.  Return BC and HL updated, and the Z flag true
  869. ; if a match is found or false if it is not.
  870. ; Input:  HL --> text
  871. ;      BC = count of bytes to scan (0000h means 65536)
  872. ;      A = the byte to search for
  873. ; Preserves:  A, DE
  874. ; Returns:  HL incremented from 1 to BC bytes (HL points to
  875. ;        the byte after the match, or to the byte after
  876. ;        the scanned field if no match was found).
  877. ;        BC decremented by at least 1 (BC decremented to
  878. ;        zero if no match was found).
  879. ;        Z flag true if a match was found.
  880. ; Note:  this is the Z80 CPIR instruction.  Note also that input
  881. ; of BC=0000 will cause a scan of all of storage.
  882. ;
  883. ScanForA:
  884.     CCIR
  885.     ret
  886. ;
  887. ; The end of the program, and the start of the buffer space.
  888. ;
  889. OBSpace    equ    $
  890. ;
  891.     end
  892. ;
  893.