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 / CPM / BDSC / BDSC-1 / CCC.ASM < prev    next >
Assembly Source File  |  2000-06-30  |  25KB  |  1,192 lines

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