home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / cpm / list / ep-src.ark / RUN.MAC < prev    next >
Text File  |  1988-05-21  |  26KB  |  1,227 lines

  1.  
  2.     .comment    `
  3. This version of CCC.C is incompatible with the code generated
  4. by the standard version of cc.  It is missing sdei, etc., and
  5. routines are at different addresses.
  6.  
  7.             `
  8.     .xlist
  9.  
  10. FALSE    equ    0
  11. TRUE    equ    NOT FALSE
  12. MPM2    equ    FALSE
  13.  
  14. USERST    equ    FALSE    ;True to use a restart vector for CDB interfacing
  15. RSTNUM    equ    6    ;Use "RST n" as default debugger vector. Has no
  16.             ;effect if USERST is false.
  17. rstloc    equ  RSTNUM*8    ;Memory address where "RST n" vector falls
  18.  
  19. nfcbs    equ    9    ;maximum # of files open at one time
  20. base    equ    0    ;start of ram in system (either 0 or 4200h)
  21. .bdos    equ    base+5    ;the rest of these do not vary between CP/M systems.
  22. tpa    equ    base+100h
  23. tbuff    equ    base+80h
  24. fcb    equ    base+5ch
  25. origin    equ    tpa
  26. exitad    equ    base    ;warm boot location
  27.  
  28. cntrlc    equ    3
  29. cr    equ    0dh
  30. lf    equ    0ah
  31. newlin    equ    0ah
  32. errorv    equ    255
  33.  
  34.  
  35. conin    equ 1        ;get a character from console
  36. conout    equ 2        ;write a character to console
  37. lstout    equ 5        ;write a character to list device
  38. dconio    equ 6        ;direct console I/O (only for CP/M 2.0)
  39. pstrng    equ 9        ;print string (terminated by '$')
  40. getlin    equ 10        ;get buffered line from console
  41. cstat    equ 11        ;get console status
  42. select    equ 14        ;select disk
  43. openc    equ 15        ;open a file
  44. closec    equ 16        ;close a file
  45. delc    equ 19        ;delete a file
  46. reads    equ 20        ;read a sector (sequential)
  47. ;writs    equ 21        ;write a sector (sequential)
  48. creatc    equ 22        ;make a file
  49. renc    equ 23        ;rename file
  50. sdma    equ 26        ;set dma
  51. gsuser    equ 32        ;get/set user code
  52. readr    equ 33        ;read random sector
  53. writr    equ 34        ;write random sector
  54. cfsizc    equ 35        ;compute file size
  55. srrecc    equ 36        ;set random record
  56.  
  57.     aseg
  58.     org    origin
  59.  
  60. ;
  61. ; The "lxi sp,0" instruction at the start of the code is changed by
  62. ; CLINK, if the "-t" option is NOT used, into:
  63. ;        lhld    base+6
  64. ;        sphl
  65. ;
  66. ; If "-t <addr>" is used, then the sequence becomes:
  67. ;        lxi    sp,<addr>
  68. ;        nop
  69. ;
  70. ; If "-n" is used, to indicate no-warm-boot, then the the sequence becomes:
  71. ;        jmp    snobsp
  72. ;        nop
  73. ;
  74.  
  75.     lxi    sp,0    ;These two instructions change depending on whether
  76.     nop        ;or not the CLINK "-t" or "-n" options are given.
  77.  
  78.     nop
  79.     nop
  80.  
  81.     jmp    skpfex    ;skip over the following vector (don't ask...)
  82.  
  83. fexitv:    jmp    exitad    ;final exit vector. If "-n" used, this
  84.             ;becomes address of the "nobret" routine.
  85.  
  86. skpfex:    call    init    ;do ARGC & ARGV processing, plus misc. initializations
  87.     call    _main    ;go crunch!!!!
  88.     jmp    vexit    ;close open files and reboot
  89.  
  90. extrns:    ds    2        ;set by CLINK to external data base address
  91. cccsiz:    dw    _main-origin    ;size of this code (for use by CLINK)
  92. codend:    ds    2        ;set by CLINK to (last addr of code + 1)
  93. freram:    ds    2        ;set by CLINK to (last addr of externals + 1)
  94.  
  95. ;
  96. ; Jump vectors to some file i/o utility routines:
  97. ;
  98.  
  99. error:    jmp    verror    ;loads -1 into HL and returns
  100. .exit:    jmp    vexit    ;close all open files and reboot
  101.  
  102. .close:    jmp    vclose    ;close a file
  103. setfcb:    jmp    vsetfcb    ;set up fcb at HL given filename at DE
  104. fgfd:    jmp    vfgfd    ;return C set if file fd in A not open
  105. fgfcb:    jmp    vfgfcb    ;compute address of internal fcb for fd in A
  106. setfcu:    jmp    vsetfcu    ;set up FCB and process user number prefix
  107. setusr:    jmp    vsetusr ;set user area to upper 5 bits of A, save previous
  108. rstusr:    jmp    vrstusr    ;restore user area to what it was before setusr call
  109. snobsp: jmp    vsnobsp    ;set up SP for non-boot ("-tn") CLINK option
  110. nobret:    jmp    vnobret    ;return to CCP when non-boot ("-tn") in effect.
  111.  
  112. ;khack:    jmp    vkhack    ;Kirkland interrupt vector initialization
  113.     ds    3
  114.  
  115. clrex:    jmp    vclrex    ;routine to clear external data area
  116.  
  117. ;no    ds    9    ;reserved
  118.  
  119. ;
  120. ; The following routines fetch a variable value from either
  121. ; the local stack frame or the external area, given the relative
  122. ; offset of the datum required immediately following the call;
  123. ; for the "long displacement" routines, the offset must be 16 bits,
  124. ; for the "short displacement" routines, the offset must be 8 bits.
  125. ;(DELETED!!)
  126. ;ldei:
  127. ;sdei:
  128. ;lsei:
  129. ;ssei:
  130. ;ldli:
  131. ;sdli:
  132.  
  133. ;
  134. ; Flag conversion routines:
  135. ;
  136. ;(DELETED!)
  137. ;pzinh:
  138. ;pnzinh:
  139. ;pcinh:
  140. ;pncinh:
  141. ;ppinh:
  142. ;pminh:
  143. ;pzind:
  144. ;pnzind:
  145. ;pcind:
  146. ;pncind:
  147. ;ppind:
  148. ;pmind:
  149.  
  150. ;    
  151. ; Relational operator routines: take args in DE and HL,
  152. ; and return a flag bit either set or reset.
  153. ;
  154. ; ==, >, < :
  155. ;
  156.  
  157. eqwel:    mov    a,l    ;return Z if HL == DE, else NZ
  158.     cmp    e
  159.     rnz        ;if L <> E, then HL <> DE
  160.     mov    a,h    ;else HL == DE only if H == D
  161.     cmp    d
  162.     ret
  163.  
  164. blau:    xchg        ;return C if HL < DE, unsigned
  165. albu:    mov    a,d    ;return C if DE < HL, unsigned
  166.     cmp    h
  167.     rnz        ;if D <> H, C is set correctly
  168.     mov    a,e    ;else compare E with L
  169.     cmp    l
  170.     ret
  171.  
  172. bgau:    xchg        ;return C if HL > DE, unsigned
  173. agbu:    mov    a,h    ;return C if DE > HL, unsigned
  174.     cmp    d
  175.     rnz        ;if H <> D, C is set correctly
  176.     mov    a,l    ;else compare L with E
  177.     cmp    e
  178.     ret
  179.  
  180. blas:    xchg        ;return C if HL < DE, signed
  181. albs:    mov    a,h    ;return C if DE < HL, signed
  182.     xra    d
  183.     jp    albu    ;if same sign, do unsigned compare
  184.     mov    a,d
  185.     ora    a
  186.     rp        ;else return NC if DE is positive and HL is negative
  187.     stc        ;else set carry, since DE is negative and HL is pos.
  188.     ret
  189.  
  190. bgas:    xchg        ;return C if HL > DE, signed
  191. agbs:    mov    a,h    ;return C if DE > HL, signed
  192.     xra    d
  193.     jp    agbu    ;if same sign, go do unsigned compare
  194.     mov    a,h
  195.     ora    a
  196.     rp        ;else return NC is HL is positive and DE is negative
  197.     stc
  198.     ret        ;else return C, since HL is neg and DE is pos
  199.  
  200.  
  201. ;
  202. ; Multiplicative operators: *, /, and %:
  203. ;
  204.  
  205. smod:    mov    a,d    ;signed MOD routine: return (DE % HL) in HL
  206.     push    psw    ;save high bit of DE as sign of result
  207.     call    tstn    ;get absolute value of args
  208.     xchg
  209.      call    tstn
  210.     xchg
  211.     call    usmod    ;do unsigned mod
  212.     pop    psw    ;was DE negative?
  213.     ora    a    ;if not,
  214.     rp        ;    all done
  215.     mov    a,h    ;else make result negative
  216.     cma
  217.     mov    h,a
  218.     mov    a,l
  219.     cma
  220.     mov    l,a
  221.     inx    h
  222.     ret
  223.  
  224. ;    nop        ;maintain address compatibility with some
  225. ;    nop        ; pre-release v1.4's.
  226.  
  227. usmod:    mov    a,h    ;unsigned MOD: return (DE % HL) in HL
  228.     ora    l
  229.     rz
  230.     push    d
  231.     push    h
  232.     call    usdiv
  233.     pop    d
  234.     call    usmul
  235.     mov    a,h
  236.     cma
  237.     mov    h,a
  238.     mov    a,l
  239.     cma 
  240.     mov    l,a
  241.     inx    h
  242.     pop    d
  243.     dad    d
  244.     ret
  245.  
  246. smul:    xra    a    ;signed multiply: return (DE * HL) in HL
  247.     sta    tmp
  248.     call    tstn
  249.     xchg
  250.     call    tstn
  251.     call    usmul
  252. smul2:    lda    tmp
  253.     rar
  254.     rnc
  255.     mov    a,h
  256.     cma
  257.     mov    h,a
  258.     mov    a,l
  259.     cma
  260.     mov    l,a
  261.     inx    h
  262.     ret
  263.  
  264. tstn:    mov    a,h
  265.     ora    a
  266.     rp
  267.     cma
  268.     mov    h,a
  269.     mov    a,l
  270.     cma
  271.     mov    l,a
  272.     inx    h
  273.     lda    tmp
  274.     inr    a
  275.     sta    tmp
  276.     ret
  277.  
  278. usmul:    push    b    ;unsigned multiply: return (DE * HL) in HL
  279.     call    usm2
  280.     pop    b
  281.     ret
  282.  
  283. usm2:    mov    b,h
  284.     mov    c,l
  285.     lxi    h,0
  286. usm3:    mov    a,b
  287.     ora    c
  288.     rz
  289.     mov    a,b
  290.     rar
  291.     mov    b,a
  292.     mov    a,c
  293.     rar
  294.     mov    c,a
  295.     jnc    usm4
  296.     dad    d
  297. usm4:    xchg
  298.     dad    h
  299.     xchg
  300.     jmp    usm3
  301.  
  302. usdiv:    mov    a,h    ;unsigned divide: return (DE / HL) in HL
  303.     ora    l    ;return 0 if HL is 0
  304.     rz
  305.     push    b
  306.     call    usd1
  307.     mov    h,b
  308.     mov    l,c
  309.     pop    b
  310.     ret
  311.  
  312.  
  313. usd1:    mvi    b,1
  314. usd2:    mov    a,h
  315.     ora    a
  316.     jm    usd3
  317.     dad    h
  318.     inr    b
  319.     jmp    usd2
  320.  
  321. usd3:    xchg
  322.  
  323. usd4:    mov    a,b
  324.     lxi    b,0
  325. usd5:    push    psw
  326. usd6:    call    cmphd
  327.     jc    usd7
  328.     inx    b
  329.     push    d
  330.     mov    a,d
  331.     cma
  332.     mov    d,a
  333.     mov    a,e
  334.     cma
  335.     mov    e,a
  336.     inx    d
  337.     dad    d
  338.     pop    d
  339. usd7:    xra    a
  340.     mov    a,d
  341.     rar
  342.     mov    d,a
  343.     mov    a,e
  344.     rar
  345.     mov    e,a
  346.     pop    psw
  347.     dcr    a
  348.     rz
  349.     push    psw
  350.     mov    a,c
  351.     ral
  352.     mov    c,a
  353.     mov    a,b
  354.     ral
  355.     mov    b,a
  356.     jmp    usd6
  357.  
  358. sdiv:    xra    a    ;signed divide: return (DE / HL) in HL
  359.     sta    tmp
  360.     call    tstn
  361.     xchg
  362.     call    tstn
  363.     xchg
  364.     call    usdiv
  365.     jmp    smul2
  366.  
  367. cmphd:    mov    a,h    ;this returns C if HL < DE
  368.     cmp    d    ; (unsigned compare only used
  369.     rc        ;  within C.CCC, not from C)
  370.     rnz
  371.     mov    a,l
  372.     cmp    e
  373.     ret
  374.  
  375. ;
  376. ; Shift operators  << and >>:
  377. ;
  378.  
  379. sderbl:    xchg        ;shift DE right by L bits
  380. shlrbe:    inr    e    ;shift HL right by E bits
  381. shrbe2:    dcr    e
  382.     rz
  383.     xra    a
  384.     mov    a,h
  385.     rar
  386.     mov    h,a
  387.     mov    a,l    
  388.     rar
  389.     mov    l,a
  390.     jmp    shrbe2
  391.  
  392. sdelbl:    xchg        ;shift DE left by L bits
  393. shllbe:    inr    e    ;shift HL left by E bits
  394. shlbe2:    dcr    e
  395.     rz
  396.     dad    h
  397.     jmp    shlbe2
  398.  
  399.  
  400. ;
  401. ; Routines to 2's complement HL and DE:
  402. ;
  403.  
  404. cmh:    mov    a,h
  405.     cma
  406.     mov    h,a
  407.     mov    a,l
  408.     cma
  409.     mov    l,a
  410.     inx    h
  411.     ret
  412.  
  413. cmd:    mov    a,d
  414.     cma
  415.     mov    d,a
  416.     mov    a,e
  417.     cma
  418.     mov    e,a
  419.     inx    d
  420.     ret
  421.  
  422.  
  423. ;
  424. ; The following routines yank a formal parameter value off the stack
  425. ; and place it in both HL and A (low byte), assuming the caller
  426. ; hasn't done anything to its stack pointer since IT was called.
  427. ;(DELETED!)
  428. ;ma1toh:
  429. ;ma2toh:
  430. ;ma3toh:
  431. ;ma4toh:
  432. ;ma5toh:
  433. ;ma6toh:
  434. ;ma7toh:
  435. ;
  436. ; This routine takes the first 7 args on the stack
  437. ; and places them contiguously at the "args" ram area.
  438. ; This allows a library routine to make one call    to arghak
  439. ; and henceforth have all it's args available directly
  440. ; through lhld's instead of having to hack the stack as it
  441. ; grows and shrinks. Note that arghak should be called as the
  442. ; VERY FIRST THING a function does, before even pushing BC.
  443. ;(should delete this)
  444. arghak: lxi    d,args
  445.     lxi    h,4    ;pass over two return address
  446.     dad    sp    ;source for block move in HL
  447.     push    b    ;save BC
  448.     mvi    b,14    ;countdown in B
  449. arghk2:    mov    a,m    ;copy loop
  450.     stax    d
  451.     inx    h
  452.     inx    d
  453.     dcr    b
  454.     jnz    arghk2    
  455.     pop    b    ;restore BC
  456.     ret
  457.  
  458. ;
  459. ; ABSOLUTELY NO CHANGES SHOULD EVER BE MADE TO THE CODE BEFORE
  460. ; THIS POINT IN THIS SOURCE FILE (except for customizing the EQU
  461. ; statements at the beginning of the file).
  462. ;(Well, I did.)
  463.  
  464.  
  465. ;
  466. ; The following two routines are used when the "-tn" CLINK option
  467. ; was given, in order to preserve the SP value passed to the transient
  468. ; command by the CCP and return to the CCP after execution without
  469. ; performing a warm-boot.
  470. ;
  471.  
  472. vsnobsp:
  473.     lxi    h,0        ;get CCP's SP value in HL
  474.     dad    sp
  475.     shld    spsav        ;save it for later
  476.     lhld    base+6        ;get BIOS pointer
  477.     lxi    d,-2100        ;subtract size of CCP plus a fudge
  478.     dad    d
  479.     sphl            ;make that the new SP value
  480.     jmp    tpa+3        ;and get things under way...
  481.  
  482. vnobret:
  483.     lhld    spsav        ;restore CCP's SP
  484.     sphl
  485.     ret            ;return to CCP
  486.  
  487.  
  488.  
  489. ;
  490. ; This routine is called first to do argc & argv processing (if
  491. ; running under CP/M) and some odds and ends initializations:
  492. ;
  493.  
  494. init:    pop    h    ;store return address
  495.     shld    tmp2    ; somewhere safe for the time being
  496.  
  497.  
  498. ;room on stack for arglst and comlin and fcbt
  499.  
  500.     lxi    h,-36*nfcbs
  501.     dad    sp
  502.     shld    .fcbt
  503.  
  504.     lxi    h,-131 -36*nfcbs
  505.     dad    sp
  506.     shld    .comlin
  507.     lxi    h,-131-60 -36*nfcbs
  508.     dad    sp
  509.     shld    .arglst
  510.     sphl
  511.  
  512.  
  513.     nop
  514.     nop
  515.     nop
  516.     nop
  517.     nop
  518. ;    nop
  519.  
  520.  
  521.     dcx    h
  522.     dcx    h
  523. ;this is now arglst-2
  524. ;for now, let's keep total bytes the same 'til ram
  525. ;=+14
  526.     push    h
  527.  
  528.             ;Initialize storage allocation pointers:
  529.     lhld    freram    ;get address after end of externals
  530.     shld    allocp    ;store at allocation pointer (for "sbrk.")
  531. ;excessive?
  532. ;    lxi    h,1000    ;default safety space between stack and
  533. ;try 256
  534. ;(now a constant)
  535. ;-    lxi    h,100H
  536. ;-    shld    alocmx    ; highest allocatable address in memory 
  537.             ; (for use by "sbrk".).
  538.  
  539. ;(revise lib so don't need this stuff)
  540.             ;Initialize random seed:
  541. ;    lxi    h,59dch    ;let's stick something wierd into the
  542. ;    shld    rseed    ;first 16 bits of the random-number seed
  543.  
  544.             ;Initialize I/O hack locations:
  545. ;    mvi    a,0dbh        ;"in" op, for "in xx; ret" subroutine
  546. ;    sta    iohack
  547. ;    mvi    a,0d3h        ;"out" op for "out xx; ret" subroutine
  548. ;    sta    iohack+3
  549. ;    mvi    a,0c9h        ;"ret" for above sobroutines
  550. ;    sta    iohack+2    ;the port number is filled in by the
  551. ;    sta    iohack+5    ;"inp" and "outp" library routines.
  552.  
  553. ;    call    khack        ;initialize Kirkland debugger vector
  554.  
  555.                  ;initialize raw I/O parameters
  556.     xra    a
  557.     sta    freeze        ;clear freeze (^S) flag
  558.     sta    pending        ;no pending input yet
  559.     mvi    a,1fh    
  560.     sta    .mode        ;tty mode: all features enabled
  561.     mvi    a,'C'-64
  562.     sta    quitc        ;this is the standard interrupt char
  563.  
  564.  
  565.             ;under CP/M: clear console, process ARGC & ARGV:
  566.     mvi    c,cstat ;interrogate console status to see if there
  567.     call    .bdos    ;  happens to be a stray character there...
  568.  
  569.     ora    a    ;(used to be `ani 1'...they tell me this works
  570. ;    nop        ; better for certain bizarre CP/M-"like" systems)
  571.  
  572.     jz    initzz
  573.     mvi    c,conin   ;if input present, clear it
  574.     call    .bdos
  575.  
  576. initzz:
  577.     lhld    .comlin
  578.     xchg
  579.     lxi    h,tbuff
  580.  
  581.  
  582. ;note that we COULD find our own name in CCP conbuf
  583. ; if we really wanted it for argv[0]
  584.  
  585.     mov    b,m        ;first get length of it from loc. base+80h
  586.     inx    h
  587.     mov    a,b
  588.     ora    a    ;if no arguments, don't parse for argv
  589.     jnz    initl
  590.     lxi    d,1    ;set argc to 1 in such a case.
  591.     jmp    i5
  592.  
  593. initl:    mov    a,m    ;ok, there are arguments. parse...
  594.     stax    d    ;first copy command line to comlin
  595.     inx    h
  596.     inx    d
  597.     dcr    b
  598.     jnz    initl
  599.     xra    a    ;place zero following line
  600.     stax    d
  601.  
  602.     lhld    .arglst    ;where pointers will all go
  603.     mov    b,h
  604.     mov    c,l
  605.     lhld    .comlin    ;now compute pointers to each arg
  606.     lxi    d,1        ;arg count
  607.  
  608.  
  609.     xra    a        ;clear "in a string" flag
  610.     sta    tmp1
  611. i2:    mov    a,m    ;between args...
  612.     inx    h
  613.     cpi    ' '
  614.     jz    i2
  615.     ora    a
  616.     jz    i5    ;if null byte, done with list
  617.     cpi    '"'
  618.     jnz    i2a    ;quote?
  619.     sta    tmp1    ;yes. set "in a string" flag
  620.     jmp    i2b    
  621.  
  622. i2a:    dcx    h
  623. i2b:    mov    a,l    ;ok, HL is a pointer to the start
  624.     stax    b    ;of an arg string. store it.
  625.     inx    b
  626.     mov    a,h
  627.     stax    b
  628.     inx    b
  629.     inx    d    ;bump arg count
  630. i3:    mov    a,m
  631.     inx    h    ;pass over text of this arg
  632.     ora    a    ;if at end, all done
  633.     jz    i5
  634.     push    b    ;if tmp1 set, in a string 
  635.     mov    b,a    ; (so we have to ignore spaces)
  636.     lda    tmp1
  637.     ora    a
  638.     mov    a,b
  639.     pop    b
  640.     jz    i3a
  641.     cpi    '"'    ;we are in a string.
  642.     jnz    i3    ;check for terminating quote
  643.     xra    a    ;if found, reset "in string" flag
  644.     sta    tmp1
  645.     dcx    h
  646.     mov    m,a    ;and stick a zero byte after the string
  647.     inx    h    ;and go on to next arg
  648. i3a:    cpi    ' '    ;now find the space between args
  649.     jnz    i3
  650.     dcx    h    ;found it. stick in a zero byte
  651.     mvi    m,0
  652.     inx    h
  653.     jmp    i2    ;and go on to next arg
  654.  
  655. i5:    push    d    ;all done finding args. Set argc.
  656.  
  657.     mvi    b,3*nfcbs  ;now initialize all the file info
  658.     lxi    h,fdt    ;by zeroing the fd table)
  659. i6:    mvi    m,0
  660.     inx    h
  661.     dcr    b
  662.     jnz    i6
  663.  
  664.  
  665.     call    clrex    ;clear externals, if CLINK -z option NOT used
  666.  
  667.     xra    a
  668.     sta    ungetl    ;clear the push-back byte,
  669.     sta    errnum    ;and file error code
  670.  
  671.     lhld    tmp2
  672.     pchl        ;all done initializing.
  673.  
  674. ;
  675. ; The following routine gets called to clear the external
  676. ; data area, unless the CLINK "-z" option is used.
  677. ;
  678.  
  679. vclrex:    lhld    freram    ;clear externals
  680.     xchg
  681.     lhld    extrns
  682.     call     cmh
  683.     dad    d    ;HL now holds size of external data area
  684. clrex1:    mov     a,h    ;loop till done
  685.     ora    l
  686.     rz
  687.     dcx    d
  688.     dcx    h
  689.     xra    a
  690.     stax    d
  691.     jmp    clrex1
  692.  
  693.  
  694. ;
  695. ; Initialize Kirkland interrupt vector... enables
  696. ; programs compiled with "-k" to run without the debugger:
  697. ;(DELETED)
  698.  
  699. ;
  700. ; General purpose error value return routine:
  701. ;
  702.  
  703. verror:    lxi    h,-1    ;general error handler...just
  704.     ret        ;returns -1 in HL
  705.  
  706. ;
  707. ; Here are file I/O handling routines, only needed under CP/M:
  708. ;
  709.  
  710. ;
  711. ; Close any open files and reboot:
  712. ;
  713.  
  714. vexit:
  715.                 ;if under CP/M, close all open files
  716.     mvi    a,7+nfcbs    ;start with largest possible fd
  717. exit1:    push    psw        ;and scan all fd's for open files
  718.     call    vfgfd        ;is file whose fd is in A open?
  719.     jc    exit2        ;if not, go on to next fd
  720.     mov    l,a        ;else close the associated file
  721.     mvi    h,0
  722.     push    h
  723.     call    vclose
  724.     pop    h
  725. exit2:    pop    psw
  726.     dcr    a        ;and go on to next one
  727.     cpi    7
  728.     jnz    exit1
  729.  
  730.     jmp    fexitv        ;done closing...now return
  731.                 ; to CP/M or whatever.
  732.  
  733.  
  734. ;
  735. ; Close the file whose fd is 1st arg:
  736. ;
  737.  
  738. vclose:
  739. ;    call    ma1toh    ;get fd in A
  740.     pop    d
  741.     pop    h
  742.     mov    a,l
  743.     push    h
  744.     push    d
  745.     sta    ..fd
  746.  
  747.     call    vfgfd    ;see if it is open
  748.     jc    verror    ;if not, complain
  749.     mov    a,m
  750.     call    setusr    ;set user area to match current fd
  751.     ani    4    ;check if open for writing
  752.  
  753.  
  754.     IF NOT MPM2    ;if not MP/M, and
  755.     jz    close2    ;the file isn't open for write, don't bother to close
  756.     ENDIF
  757.  
  758.  
  759.     push    h    ;save fd table entry addr
  760.  
  761. ;    call    ma2toh    ;get the fd in A again
  762.     lda    ..fd
  763.  
  764.     push    b
  765.     call    vfgfcb    ;get the appropriate fcb address
  766.     xchg        ;put it in DE
  767.     mvi    c,closec  ;get BDOS function # for close
  768.     call    .bdos    ;and do it!
  769.     pop    b
  770.     pop    h
  771. close2:    call    rstusr    ;reset user number to original state
  772.     mvi    m,0    ;close the file logically
  773.     cpi    255    ;if 255 came back from .bdos, we got problems
  774.     lxi    h,0    
  775.     rnz        ;return 0 if OK
  776.     dcx    h    ;return -1 on error
  777.     ret
  778.  
  779. ..fd:    ds    1
  780. ;
  781. ; Determine status of file whose fd is in A...if the file
  782. ; is open, return Cy clear and with the address of the fd table
  783. ; entry for the open file in HL. If the file is not open,
  784. ; return Cy set:
  785. ;
  786.  
  787. vfgfd:    mov    d,a
  788.     sui    8
  789.     rc        ;if fd < 8, error
  790.     cpi    nfcbs
  791.     cmc        ;don't allow too big an fd either
  792.     rc
  793.     push    d
  794.     mov    e,a    ;OK, we have a value in range. Now
  795.     mvi    d,0    ;  see if the file is open or not
  796.     lxi    h,fdt
  797.     dad    d    ;offset for 3-byte table entries
  798.     dad    d
  799.     dad    d
  800.     mov    a,m
  801.     ani    1    ;bit 0 is high if file is open
  802.     stc
  803.     pop    d
  804.     mov    a,d
  805.     rz        ;return C set if not open
  806.     cmc
  807.     ret        ;else reset C and return
  808.  
  809. ;
  810. ; Set up a CP/M file control block at HL with the file whose
  811. ; simple null-terminated name is pointed to by DE:
  812. ; Format for filename must be: "[white space][d:]filename.ext"
  813. ; The user number prefix hack is NOT recognized by this subroutine.
  814. ;
  815.  
  816. vsetfcb:
  817.     push    b
  818.     call    igwsp    ;ignore blanks and tabs    
  819.     push    h    ;save fcb ptr
  820.     inx    d    ;peek at 2nd char of filename
  821.     ldax    d
  822.     dcx    d
  823.     cpi    ':'    ;default disk byte value is 0
  824.     mvi    a,0    ; (for currently logged disk)
  825.     jnz    setf1
  826.     ldax    d    ;oh oh...we have a disk designator
  827.     call    mapuc    ;make it upper case
  828.     sui    'A'-1    ;and fudge it a bit
  829.     inx    d    ;advance DE past disk designator to filename
  830.     inx    d
  831. setf1:    mov    m,a    ;set disk byte
  832.     inx    h
  833.     mvi    b,8
  834.     call    setnm    ;set filename, pad with blanks
  835.     call    setnm3    ;ignore extra characters in filename
  836.     ldax    d
  837.     cpi    '.'    ;if an extension is given,
  838.     jnz    setf2
  839.     inx    d    ;skip the '.'
  840. setf2:    mvi    b,3
  841.     call    setnm    ;set the extension field and pad with blanks
  842.     xra    a    ;and zero the appropriate fields of the fcb
  843.     mov    m,a
  844.     lxi    d,20
  845.     dad    d
  846.     mov    m,a
  847.     inx    h
  848.     mov    m,a    ;zero random record bytes of fcb
  849.     inx    h
  850.     mov    m,a
  851.     inx    h
  852.     mov    m,a
  853.     pop    d
  854.     pop    b
  855.     ret
  856.  
  857. ;
  858. ; This routine copies up to B characters from (DE) to (HL),
  859. ; padding with blanks on the right. An asterisk causes the rest
  860. ; of the field to be padded with '?' characters:
  861. ;
  862.  
  863. setnm:    push    b
  864. setnm1:    ldax    d
  865.     cpi    '*'    ;wild card?
  866.     mvi    a,'?'    ;if so, pad with ? characters
  867.     jz    pad2
  868.  
  869. setnm2:    ldax    d
  870.     call    legfc    ;next char legal filename char?
  871.     jc    pad    ;if not, go pad for total of B characters
  872.     mov    m,a    ;else store
  873.     inx    h
  874.     inx    d
  875.     dcr    b
  876.     jnz    setnm1    ;and go for more if B not yet zero
  877.     pop    b
  878. setnm3:    ldax    d    ;skip rest of filename if B chars already found
  879.     call    legfc
  880.     rc
  881.     inx    d
  882.     jmp    setnm3
  883.  
  884. pad:    mvi    a,' '    ;pad with B blanks
  885. pad2:    mov    m,a    ;pad with B instances of char in A
  886.     inx    h
  887.     dcr    b
  888.     jnz    pad2
  889.     pop    b
  890.     ret
  891.  
  892. ;
  893. ; Process filename having optional user area number prefix of form "<u#>/",
  894. ; return the effective user area number of the given filename in the upper
  895. ; 5 bits of A, and also store this value at "usrnum". Note that if no user
  896. ; number is specified, the current user area is presumed by default. After
  897. ; the user area prefix is processed, do a regular "setfcb":
  898. ;
  899. ; Note: a filename is considered to have a user number if the first char
  900. ;     in the name is a decimal digit and the first non-decimal-digit
  901. ;    character in the name is a slash (/).
  902.  
  903. vsetfcu:
  904.     push    b    ;save BC
  905.     push    h    ;save vcb pointer
  906.     call    igwsp    ;ignore blanks and tabs    
  907.     call    isdec    ;decimal digit?
  908.     jnc    setfc2    ;if so, go process
  909.  
  910. setfc0:    push    d    ;save text pointer
  911.     mvi    c,gsuser  ;else get current effective user number
  912.     mvi    e,0ffh
  913.  
  914.     call    .bdos    ;get current user area if implemented
  915.  
  916.     pop    d    ;restore text pointer
  917.  
  918. setfc1:    rlc        ;rotate into upper 5 bits of A
  919.     rlc
  920.     rlc
  921.     sta    usrnum    ;and save
  922.     pop    h    ;restore junk
  923.     pop    b
  924.     jmp    setfcb    ;and parse rest of filename
  925.  
  926. setfc2:    mvi    b,0    ;clear user number counter
  927.     push    d    ;save text pointer in case we invalidate user prefix
  928. setfc3:    sui    '0'    ;save next digit value
  929.     mov    c,a    ; in C
  930.     mov    a,b    ;multiply previous sum by 10
  931.     add    a    ;*2
  932.     add    a    ;*4
  933.     add    a    ;*8
  934.     add    b    ;*9
  935.     add    b    ;*10
  936.     add    c    ;add new digit
  937.     mov    b,a    ;put sum in B
  938.     inx    d    ;look at next char in text
  939.     ldax    d    ;is it a digit?    
  940.     call    isdec
  941.     jnc    setfc3    ;if so, go on looping and summing digits
  942.     cpi    '/'    ;make sure number is terminated by a slash
  943.     jz    setfc4
  944.     pop    d    ;if not, entire number prefix is not really a 
  945.     jmp    setfc0    ; user number, so just ignore it all.
  946.  
  947. setfc4:    inx    d    ;ok, allow the user number
  948.     pop    h    ;get old text pointer off the stack
  949.     mov    a,b    ;get user number value
  950.     jmp    setfc1    ;and go store it and parse rest of filename
  951.  
  952.  
  953. ;
  954. ; Test if char in A is legal character to be in a filename:
  955. ;
  956.  
  957. legfc:    call    mapuc
  958.     cpi    '.'    ; '.' is illegal in a filename or extension
  959.     stc
  960.     rz
  961.     cpi    ':'    ;so is ':'
  962.     stc     
  963.     rz
  964.     cpi    7fh    ;delete is no good
  965.     stc
  966.     rz
  967.     cpi    '!'    ;if less than exclamation pt, not legal char
  968.     ret        ;else good enough
  969.  
  970. ;
  971. ; Map character in A to upper case if it is lower case:
  972. ;
  973.  
  974. mapuc:    cpi    'a'
  975.     rc
  976.     cpi    'z'+1
  977.     rnc
  978.     sui    32    ;if lower case, map to upper
  979.     ret
  980.  
  981. ;
  982. ; Ignore blanks and tabs at text pointed to by DE:
  983. ;
  984.  
  985. igwsp:    dcx    d
  986. igwsp1:    inx    d
  987.     ldax    d
  988.     cpi    ' '
  989.     jz    igwsp1
  990.     cpi    9
  991.     jz    igwsp1
  992.     ret
  993.  
  994. ;
  995. ; Return Cy if char in A is not a decimal digit:
  996. ;
  997.  
  998. isdec:    cpi    '0'
  999.     rc
  1000.     cpi    '9'+1
  1001.     cmc
  1002.     ret
  1003.  
  1004.  
  1005. ;
  1006. ; This routine does one of two things, depending
  1007. ; on the value passed in A.
  1008. ;
  1009. ; If A is zero, then it finds a free file slot
  1010. ;  (if possible), else returns C set.
  1011. ;
  1012. ; If A is non-zero, then it returns the address
  1013. ; of the fcb corresponding to an open file whose
  1014. ; fd happens to be the value in A, or C set if there
  1015. ; is no file associated with fd.
  1016. ;
  1017.  
  1018. vfgfcb:    push    b
  1019.     ora    a    ;look for free slot?
  1020.     mov    c,a
  1021.     jnz    fgfc2    ;if not, go away
  1022.     mvi    b,nfcbs    ;yes. do it...
  1023.     lxi    d,fdt
  1024. ;    lxi    h,fcbt
  1025.     lhld    .fcbt
  1026.     mvi    c,8
  1027. fgfc1:    ldax    d
  1028.     ani    1
  1029.     mov    a,c
  1030.     jnz    fgfc1a    ;found free slot?
  1031.     pop    b    ;yes. all done.
  1032.     ret
  1033.  
  1034. fgfc1a:    push    d
  1035.     lxi    d,36    ;fcb length to accommodate random I/O
  1036.     dad    d
  1037.     pop    d
  1038.     inx    d    ;bump to next 3-byte table entry
  1039.     inx    d
  1040.     inx    d
  1041.     inr    c
  1042.     dcr    b
  1043.     jnz    fgfc1
  1044. fgfc1b:    stc
  1045.     pop    b
  1046.     ret        ;return C if no more free slots
  1047.  
  1048. fgfc2:    call    vfgfd    ;compute fcb address for fd in A:
  1049.     jc    fgfc1b    ;return C if file isn't open
  1050.  
  1051.     sui    8
  1052.     mov    l,a    ;put (fd-8) in HL
  1053.     mvi    h,0
  1054.     dad    h    ;double it
  1055.     dad    h    ;4*a
  1056.     mov    d,h    ;save 4*a in DE
  1057.     mov    e,l
  1058.     dad    h    ;8*a
  1059.     dad    h    ;16*a
  1060.     dad    h    ;32*a
  1061.     dad    d    ;36*a
  1062.     xchg        ;put 36*a in DE
  1063. ;-    lxi    h,fcbt    ;add to base of table
  1064.     lhld    .fcbt
  1065.     dad    d    ;result in HL
  1066.     mov    a,c    ;and return original fd in A
  1067.     pop    b
  1068.     ret
  1069.  
  1070. ;
  1071. ; The following two subroutines change the current CP/M user area for
  1072. ; user with file I/O:
  1073. ;
  1074.  
  1075. vsetusr:
  1076.     push    b    ;SET user number to upper bits of A, save current:
  1077.     push    h
  1078.     push    d
  1079.     push    psw    ;save A
  1080.     mvi    c,gsuser ;get user code
  1081.     mvi    e,0ffh
  1082.     call    .bdos
  1083.     sta    curusr    ;save current user number
  1084.     pop    psw    ;get new user number byte
  1085.     push    psw
  1086.     rar        ;shift user number down to low bits
  1087.     rar
  1088.     rar
  1089.     ani    1fh    ;and mask off high order garbage
  1090. setu0:    mov    e,a
  1091.     mvi    c,gsuser  ;set user code
  1092.     call    .bdos
  1093.     pop    psw
  1094.     pop    d
  1095.     pop    h
  1096.     pop    b
  1097.     ret
  1098.  
  1099. vrstusr:
  1100.     push    b
  1101.     push    h
  1102.     push    d
  1103.     push    psw
  1104.     lda    curusr    ;get last saved user number
  1105.     jmp    setu0    ;and go set current user area to that
  1106.  
  1107. ;
  1108. ; Ram area:
  1109. ;
  1110.  
  1111. ram    equ    $
  1112.  
  1113. ;no    ds    20    ;reserved by BDS
  1114.  
  1115. errnum:    ds    1    ;error code from file I/O operations
  1116. ;pbase:    ds    2    ;screen-DMA address
  1117. ;ysize:    ds    2    ;screen width
  1118. ;xsize:    ds    2    ;screen height
  1119. ;psize:    ds    2    ;screen length
  1120.  
  1121. ;rseed:    ds    8    ;the random generator seed
  1122.  
  1123.  
  1124. args:
  1125. arg1:    ds    2
  1126. arg2:    ds    2
  1127. arg3:    ds    2
  1128. arg4:    ds    2
  1129. arg5:    ds    2
  1130. arg6:    ds    2
  1131. arg7:    ds    2
  1132.             ;"arghak" puts args passed on stack here.
  1133.  
  1134. ;iohack:    ds    6    ;room for I/O subroutines for use by "inp"
  1135.             ;and "outp" library routines
  1136.  
  1137. allocp:    ds    2    ;pointer to free storage for use by "sbrk" func
  1138. alocmx:    dw    100H    ;highest location to be made available to the
  1139.             ;storage allocator
  1140.  
  1141. ;room:    ds    30    ;reserved for use by BDS C system code
  1142. ;uroom:    ds    20    ;available for use by user
  1143.  
  1144.     .comment    `
  1145. (Too much garbage here)
  1146. tmp    equ    room    ;this is misc. garbage space
  1147. tmp1    equ    room+1
  1148. tmp2    equ    room+2
  1149. tmp2a    equ    room+4
  1150. ungetl    equ    room+6    ;where characters are "ungotten"
  1151. unused    equ    room+7
  1152. curusr    equ    room+8    ;used to save current user number during file I/O
  1153. usrnum    equ    room+9    ;set by "setfcu" to user number of given filename
  1154.  
  1155. .mode    equ    room+10    ;tty mode
  1156. freeze    equ    room+11    ;true if output frozen (^S)
  1157. pending equ    room+12    ;true if input character waiting
  1158. pendch    equ    room+13    ;if pending true, this is the character
  1159. quitc    equ    room+14    ;the general system abort character (^C usually)
  1160. spsav    equ    room+15    ;saved SP value from CCP
  1161. ;    equ    room+17    ;where next thing goes
  1162.  
  1163.             `
  1164.  
  1165.  
  1166. tmp:    ds    1    ;this is misc. garbage space
  1167. tmp1:    ds    1
  1168. tmp2:    ds    2
  1169. tmp2a:    ds    2
  1170. ungetl:    ds    1    ;where characters are "ungotten"
  1171. ;unused
  1172. curusr:    ds    1    ;used to save current user number during file I/O
  1173. usrnum:    ds    1    ;set by "setfcu" to user number of given filename
  1174.  
  1175. .mode:    ds    1    ;tty mode
  1176. freeze:    ds    1    ;true if output frozen (^S)
  1177. pending:ds    1    ;true if input character waiting
  1178. pendch:    ds    1    ;if pending true, this is the character
  1179. quitc:    ds    1    ;the general system abort character (^C usually)
  1180. spsav:    ds    2    ;saved SP value from CCP
  1181.  
  1182.  
  1183. echo    equ    1    ;masks for "mode" byte...echo mode
  1184. quit    equ    2    ;quit enabled
  1185. flow    equ    4    ;^S/^Q protocol honored
  1186. strip    equ    8    ;strip parity
  1187. expand    equ    16    ;expand '\n' into CR-LF on output
  1188.  
  1189. ;
  1190. ;--------------------------------------------------------------------------
  1191. ;
  1192. ; The fcb table (fcbt): 36 bytes per file control block
  1193. ;
  1194.  
  1195. ;fcbt:    ds    36*nfcbs    ;reserve room for fcb's (extra byte for IMDOS)
  1196.  
  1197.  
  1198. ;
  1199. ; The fd table: one byte per file specifying r/w/open as follows:
  1200. ;     bit 0 is high if open, low if closed
  1201. ;     bit 1 is high if open for read
  1202. ;     bit 2 is high if open for write     (both b1 and b2 may be high)
  1203. ;    bits 3-7 contain the user number in which the file is active (0-31)
  1204. ;
  1205.  
  1206. fdt:    ds    3*nfcbs    ;3 bytes per fcb: 1 for active, r/w, etc., and
  1207.             ;          2 to specify highest sector num seen
  1208.  
  1209. ;
  1210. ; The command line is copied here by init:
  1211. ;
  1212.  
  1213. ;
  1214. ; End of CP/M-only data area
  1215. ;---------------------------------------------------------------------------
  1216.  
  1217. .fcbt:        ds    2
  1218. .comlin:    ds    2
  1219. .arglst:    ds    2
  1220.  
  1221. _main    equ    $    ;where "main" program will be loaded under CP/M
  1222.  
  1223.     .list
  1224.  
  1225.     end
  1226.  
  1227.