?login_element?

Subversion Repositories NedoOS

Rev

Rev 671 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. PRESRV  EQU     TRUE    ;True to preserve CRL-file address compatibility
  2.                         ;       under various ZOPT1-ZOPT7 configurations
  3.                         ;(changing to FALSE will shorten run-time pkg., but
  4.                         ;will require re-CASM-ing .CSM files for new DEFF2.CRL)
  5.  
  6. USAREA  EQU     FALSE;TRUE      ;True if "user areas" are implemented on target system
  7.  
  8. USERST  EQU     FALSE   ;True to use a restart vector for CDB interfacing
  9. RSTNUM  EQU     6       ;Use "RST n" as default CDB vector (if USERST true)
  10.                         ;(If used, be sure corresponding ZOPTn below is FALSE)
  11. rstloc  equ    RSTNUM*8 ;physical address of debugger restart vector
  12.  
  13. ZOPT1   EQU     FALSE   ;The following five equates control the
  14. ZOPT2   EQU     FALSE   ;initialization of restart vectors 1 through 7
  15. ZOPT3   EQU     FALSE   ;(rst 1 - rst 7) for use by C programs to achieve
  16. ZOPT4   EQU     FALSE   ;optimum space efficiency. If any of these vectors are
  17. ZOPT5   EQU     FALSE   ;used by your system for I/O, set them FALSE here!
  18. ZOPT6   EQU     FALSE   ; (set FALSE if CDB w/RST 6 is to be used with object.)
  19. ZOPT7   EQU     FALSE   ; (set FALSE if DDT or SID are to be used with object.)
  20.  
  21. ;
  22. ; The "ld sp,0" instruction at the start of the code is changed by
  23. ; CLINK, if the "-t" option is NOT used, into:
  24. ;               lhld    base+6
  25. ;               ld sp,hl
  26. ;
  27. ; If "-t <addr>" is used, then the sequence becomes:
  28. ;               lxi     sp,<addr>
  29. ;               nop
  30. ;
  31. ; If "-n" is used, to indicate no-warm-boot, then the sequence becomes:
  32. ;               jp      snobsp
  33. ;               nop
  34. ;
  35.  
  36.         ld sp,0 ;These two instructions change depending on whether
  37.         nop             ;or not the CLINK "-t" or "-n" options are given.
  38.  
  39.         nop
  40.         nop
  41.  
  42.         jp      skpfex  ;skip over the following vector (don't ask...)
  43.  
  44. fexitv: jp      EXITAD  ;final exit vector. If "-n" used, this
  45.                         ;becomes address of the "nobret" routine.
  46.  
  47. skpfex: call    init    ;do ARGC & ARGV processing, plus misc. initializations
  48.         call    main    ;go crunch!!!!
  49.         jp      vexit   ;close open files and reboot
  50.  
  51. extrns: ds      2               ;set by CLINK to external data base address
  52. cccsiz: dw      main-ORIGIN     ;size of this code (for use by CLINK)
  53. codend: ds      2               ;set by CLINK to (last addr of code + 1)
  54. freram: ds      2               ;set by CLINK to (last addr of externals + 1)
  55.  
  56. ;
  57. ; Jump vectors to some file i/o utility routines:
  58. ;
  59.  
  60. error:  jp      verror  ;loads -1 into HL and returns
  61. exit:   jp      vexit   ;close all open files and reboot
  62.  
  63.         IF      CPM
  64. close:  jp      vclose  ;close a file
  65. setfcb: jp      vsetfcb ;set up fcb at HL given filename at DE
  66. fgfd:   jp      vfgfd   ;return C set if file fd in A not open
  67. fgfcb:  jp      vfgfcb  ;compute address of internal fcb for fd in A
  68. setfcu: jp      vstfcu  ;set up FCB and process user number prefix
  69. setusr: jp      vsetusr ;set user area to upper 5 bits of A, save previous
  70. rstusr: jp      vrstusr ;restore user area to what it was before setusr call
  71. snobsp: jp      vsnobsp ;set up SP for non-boot ("-tn") CLINK option
  72. nobret: jp      vnobret ;return to CCP when non-boot ("-tn") in effect.
  73. khack:  jp      vkhack  ;Kirkland interrupt vector initialization
  74.         ENDIF
  75.  
  76.         IF      NOT CPM ;if not under CP/M, file I/O routines
  77.         jp      verror  ;are not used.
  78.         jp      verror
  79.         jp      verror
  80.         jp      verror
  81.         jp      verror
  82.         jp      verror
  83.         jp      verror
  84.         jp      verror
  85.         jp      verror
  86.         jp      verror
  87.         ENDIF
  88.  
  89. clrex:  jp      vclrex  ;routine to clear external data area
  90.  
  91.         ds      9       ;reserved
  92.  
  93. ;
  94. ; The following routines fetch a variable value from either
  95. ; the local stack frame or the external area, given the relative
  96. ; offset of the datum required immediately following the call;
  97. ; for the "long displacement" routines, the offset must be 16 bits,
  98. ; for the "short displacement" routines, the offset must be 8 bits.
  99. ;
  100.  
  101. ;
  102. ; long-displacement, double-byte external indirection:
  103. ;
  104. ;       format: call ldei               ; get 16-bit value in HL
  105. ;               dw offset_from_extrns   ; >= 256
  106. ;
  107.  
  108. ldei:   pop hl  ;get address of offset
  109.         ld      e,(hl)  ;put offset in DE
  110.         inc hl
  111.         ld      d,(hl)
  112.         inc hl         
  113.         push hl ;save return address
  114.         lhld    extrns  ;add offset to external area base
  115.         add hl,de
  116. mindir: ld      a,(hl)  ;and get the value into HL
  117.         inc hl
  118.         ld      h,(hl)
  119.         ld      l,a
  120.         ret
  121.  
  122. ;
  123. ; short-displacement, double-byte external indirection:
  124. ;
  125. ;       format:         call sdei               ; get 16-bit value in L
  126. ;                       db offset_from_extrns   ; < 256
  127. ;
  128.  
  129. sdei:   pop hl
  130.         ld      e,(hl)
  131.         inc hl
  132.         push hl
  133.         ld      d,0
  134.         lhld    extrns
  135.         add hl,de
  136.         ld      a,(hl)
  137.         inc hl
  138.         ld      h,(hl)
  139.         ld      l,a
  140.         ret
  141.  
  142. ;
  143. ; long-displacement, single-byte external indirection:
  144. ;
  145. ;       format:         call    lsei            ; get 8-bit value in L
  146. ;                       dw offset_from_extrns   ; >= 256
  147. ;
  148.  
  149. lsei:   pop hl
  150.         ld      e,(hl)
  151.         inc hl
  152.         ld      d,(hl)
  153.         inc hl
  154.         push hl
  155.         lhld    extrns
  156.         add hl,de
  157.         ld      l,(hl)
  158.         ret
  159.  
  160. ;
  161. ; short-displacement, single-byte external indirection:
  162. ;
  163. ;       format:         call    ssei            ; get 8-bit value in L
  164. ;                       db offset_from_externs  ; < 256
  165. ;
  166.  
  167. ssei:   pop hl
  168.         ld      e,(hl) 
  169.         inc hl
  170.         push hl
  171.         ld      d,0
  172.         lhld    extrns
  173.         add hl,de
  174.         ld      l,(hl)
  175.         ret
  176.  
  177. ;
  178. ; long-displacement, double-byte local indirection:
  179. ;
  180. ;       format:         call    ldli            ; get 16-bit value in HL
  181. ;                       dw offset_from_BC       ; >= 256
  182. ;
  183.  
  184. ldli:   pop hl
  185.         ld      e,(hl)
  186.         inc hl
  187.         ld      d,(hl)
  188.         inc hl
  189.         push hl
  190.         ex de,hl
  191.         add hl,bc
  192.         ld      a,(hl)
  193.         inc hl
  194.         ld      h,(hl)
  195.         ld      l,a
  196.         ret
  197.  
  198. ;
  199. ; short-displacement, double-byte local indirection:
  200. ;
  201. ;       format:         call    sdli            ; get 16-bit value in HL
  202. ;                       db offset_from_BC       ; < 256
  203. ;
  204.  
  205. sdli:   pop hl
  206.         ld      e,(hl)
  207.         inc hl
  208.         push hl
  209.         ex de,hl
  210.         ld      h,0
  211.         add hl,bc
  212.         ld      a,(hl)
  213.         inc hl
  214.         ld      h,(hl)
  215.         ld      l,a
  216.         ret
  217.  
  218. ;
  219. ; Flag conversion routines:
  220. ;
  221.  
  222. pzinh:  ld hl,1 ;return HL = true if Z set
  223.         ret z
  224.         dec hl
  225.         ret
  226.  
  227. pnzinh: ld hl,0 ;return HL = false if Z set
  228.         ret z
  229.         inc hl
  230.         ret
  231.  
  232. pcinh:  ld hl,1 ;return HL = true if C set
  233.         ret c
  234.         dec hl
  235.         ret
  236.  
  237. pncinh: ld hl,0 ;return HL = false if C set
  238.         ret c
  239.         inc hl
  240.         ret
  241.  
  242. ppinh:  ld hl,1 ;return HL = true if P (plus) flag set
  243.         ret p
  244.         dec hl
  245.         ret
  246.  
  247. pminh:  ld hl,1 ;return HL = true if M (minus) flag set
  248.         ret m
  249.         dec hl
  250.         ret
  251.  
  252. pzind:  ld de,1 ;return DE = true if Z set
  253.         ret z
  254.         dec de
  255.         ret
  256.  
  257. pnzind: ld de,0 ;return DE = false if Z set
  258.         ret z
  259.         inc de
  260.         ret
  261.  
  262. pcind:  ld de,1 ;return DE = true if C set
  263.         ret c
  264.         dec de
  265.         ret
  266.  
  267. pncind: ld de,0 ;return DE = false if C set
  268.         ret c
  269.         inc de
  270.         ret
  271.  
  272. ppind:  ld de,1 ;return DE = true if P (plus) flag set
  273.         ret p
  274.         dec de
  275.         ret
  276.  
  277. pmind:  ld de,1 ;return DE = true if M (minus) flag set
  278.         ret m
  279.         dec de
  280.         ret
  281.        
  282.  
  283. ;      
  284. ; Relational operator routines: take args in DE and HL,
  285. ; and return a flag bit either set or reset.
  286. ;
  287. ; ==, >, < :
  288. ;
  289.  
  290. eqwel:  ld      a,l     ;return Z if HL == DE, else NZ
  291.         cp      e
  292.         ret nz          ;if L <> E, then HL <> DE
  293.         ld      a,h     ;else HL == DE only if H == D
  294.         cp      d
  295.         ret
  296.  
  297. blau:   ex de,hl                ;return C if HL < DE, unsigned
  298. albu:   ld      a,d     ;return C if DE < HL, unsigned
  299.         cp      h
  300.         ret nz          ;if D <> H, C is set correctly
  301.         ld      a,e     ;else compare E with L
  302.         cp      l
  303.         ret
  304.  
  305. bgau:   ex de,hl                ;return C if HL > DE, unsigned
  306. agbu:   ld      a,h     ;return C if DE > HL, unsigned
  307.         cp      d
  308.         ret nz          ;if H <> D, C is set correctly
  309.         ld      a,l     ;else compare L with E
  310.         cp      e
  311.         ret
  312.  
  313. blas:   ex de,hl                ;return C if HL < DE, signed
  314. albs:   ld      a,h     ;return C if DE < HL, signed
  315.         xor     d
  316.         jp      p,albu  ;if same sign, do unsigned compare
  317.         ld      a,d
  318.         or      a
  319.         ret p           ;else return NC if DE is positive and HL is negative
  320.         scf             ;else set carry, since DE is negative and HL is pos.
  321.         ret
  322.  
  323. bgas:   ex de,hl                ;return C if HL > DE, signed
  324. agbs:   ld      a,h     ;return C if DE > HL, signed
  325.         xor     d
  326.         jp      p,agbu  ;if same sign, go do unsigned compare
  327.         ld      a,h
  328.         or      a
  329.         ret p           ;else return NC is HL is positive and DE is negative
  330.         scf
  331.         ret             ;else return C, since HL is neg and DE is pos
  332.  
  333.  
  334. ;
  335. ; Multiplicative operators: *, /, and %:
  336. ;
  337.  
  338. smod:   ld      a,d     ;signed MOD routine: return (DE % HL) in HL
  339.         push    af      ;save high bit of DE as sign of result
  340.         call    tstn    ;get absolute value of args
  341.         ex de,hl
  342.         call    tstn
  343.         ex de,hl
  344.         call    usmod   ;do unsigned mod
  345.         pop     af      ;was DE negative?
  346.         or      a       ;if not,
  347.         ret p           ;       all done
  348.         ld      a,h     ;else make result negative
  349.         cpl
  350.         ld      h,a
  351.         ld      a,l
  352.         cpl
  353.         ld      l,a
  354.         inc hl
  355.         ret
  356.  
  357.         IF PRESRV
  358.         nop             ;maintain address compatibility with some
  359.         nop             ; pre-release v1.4's.
  360.         ENDIF
  361.  
  362. usmod:  ld      a,h     ;unsigned MOD: return (DE % HL) in HL
  363.         or      l
  364.         ret z
  365.         push de
  366.         push hl
  367.         call    usdiv
  368.         pop de
  369.         call    usmul
  370.         ld      a,h
  371.         cpl
  372.         ld      h,a
  373.         ld      a,l
  374.         cpl
  375.         ld      l,a
  376.         inc hl
  377.         pop de
  378.         add hl,de
  379.         ret
  380.  
  381. smul:   jp      usmul   ;turns out signed and unsigned multipilication
  382.                         ; are equivalent for 16 bits, so just do unsigned
  383.  
  384.                         ;rst optimization of function entry sequence (ZOPT1):
  385. fentrc: pop de  ;pop  arg byte address into DE
  386.         push    bc      ;save BC
  387.         ld a,(de)       ;put stack offset byte value into L, setting up
  388.         ld      l,a     ;       HL with negative stack offset
  389.         ld      h,0ffh
  390.         inc de  ;DE now points to return address
  391.         add hl,sp       ;calculate new SP value
  392.         ld sp,hl                ;set new SP value
  393.         ld      b,h     ;place into BC as new frame base ptr
  394.         ld      c,l    
  395.         ex de,hl                ;put return address in HL
  396.         jp (hl)         ;and return
  397.        
  398.  
  399. smul2:  lda     tmp
  400.         rra
  401.         ret nc
  402.         jp      cmh
  403.  
  404.         ds      3       ;preserve address compatibility with previous versions
  405.  
  406. tstn:   ld      a,h
  407.         or      a
  408.         ret p
  409.         cpl
  410.         ld      h,a
  411.         ld      a,l
  412.         cpl
  413.         ld      l,a
  414.         inc hl
  415.         lda     tmp
  416.         inc     a
  417.         sta     tmp
  418.         ret
  419.  
  420. usmul:  push bc ;unsigned multiply: return (DE * HL) in HL
  421.         call    usm2
  422.         pop bc
  423.         ret
  424.  
  425. usm2:   ld      b,h
  426.         ld      c,l
  427.         ld hl,0
  428. usm3:   ld      a,b
  429.         or      c
  430.         ret z
  431.         ld      a,b
  432.         rra
  433.         ld      b,a
  434.         ld      a,c
  435.         rra
  436.         ld      c,a
  437.         jp nc,usm4
  438.         add hl,de
  439. usm4:   ex de,hl
  440.         add hl,hl
  441.         ex de,hl
  442.         jp      usm3
  443.  
  444. usdiv:  ld      a,h     ;unsigned divide: return (DE / HL) in HL
  445.         or      l       ;return 0 if HL is 0
  446.         ret z
  447.         push bc
  448.         call    usd1
  449.         ld      h,b
  450.         ld      l,c
  451.         pop bc
  452.         ret
  453.  
  454.  
  455. usd1:   ld      b,1
  456. usd2:   ld      a,h
  457.         or      a
  458.         jp m,usd3
  459.         add hl,hl
  460.         inc     b
  461.         jp      usd2
  462.  
  463. usd3:   ex de,hl
  464.  
  465. usd4:   ld      a,b
  466.         ld bc,0
  467. usd5:   push    af
  468. usd6:   call    cmphd
  469.         jp c,usd7
  470.         inc bc
  471.         push de
  472.         ld      a,d
  473.         cpl
  474.         ld      d,a
  475.         ld      a,e
  476.         cpl
  477.         ld      e,a
  478.         inc de
  479.         add hl,de
  480.         pop de
  481. usd7:   xor     a
  482.         ld      a,d
  483.         rra
  484.         ld      d,a
  485.         ld      a,e
  486.         rra
  487.         ld      e,a
  488.         pop     af
  489.         dec     a
  490.         ret z
  491.         push    af
  492.         ld      a,c
  493.         rla
  494.         ld      c,a
  495.         ld      a,b
  496.         rla
  497.         ld      b,a
  498.         jp      usd6
  499.  
  500. sdiv:   xor     a       ;signed divide: return (DE / HL) in HL
  501.         sta     tmp
  502.         call    tstn
  503.         ex de,hl
  504.         call    tstn
  505.         ex de,hl
  506.         call    usdiv
  507.         jp      smul2
  508.  
  509. cmphd:  ld      a,h     ;this returns C if HL < DE
  510.         cp      d       ; (unsigned compare only used
  511.         ret c           ;  within C.CCC, not from C)
  512.         ret nz
  513.         ld      a,l
  514.         cp      e
  515.         ret
  516.  
  517. ;
  518. ; Shift operators  << and >>:
  519. ;
  520.  
  521. sderbl: ex de,hl                ;shift DE right by L bits
  522. shlrbe: inc     e       ;shift HL right by E bits
  523. shrbe2: dec     e
  524.         ret z
  525.         xor     a
  526.         ld      a,h
  527.         rra
  528.         ld      h,a
  529.         ld      a,l    
  530.         rra
  531.         ld      l,a
  532.         jp      shrbe2
  533.  
  534. sdelbl: ex de,hl                ;shift DE left by L bits
  535. shllbe: inc     e       ;shift HL left by E bits
  536. shlbe2: dec     e
  537.         ret z
  538.         add hl,hl
  539.         jp      shlbe2
  540.  
  541.  
  542. ;
  543. ; Routines to 2's complement HL and DE:
  544. ;
  545.  
  546. cmh:    ld      a,h
  547.         cpl
  548.         ld      h,a
  549.         ld      a,l
  550.         cpl
  551.         ld      l,a
  552.         inc hl
  553.         ret
  554.  
  555. cmd:    ld      a,d
  556.         cpl
  557.         ld      d,a
  558.         ld      a,e
  559.         cpl
  560.         ld      e,a
  561.         inc de
  562.         ret
  563.  
  564.  
  565. ;
  566. ; The following routines yank a formal parameter value off the stack
  567. ; and place it in both HL and A (low byte), assuming the caller
  568. ; hasn't done anything to its stack pointer since IT was called.
  569. ;
  570. ; The mnemonics are "lde Arg #n To HL",
  571. ; where arg #1 is the third thing on the stack (where the first
  572. ; and second things are, respectively, the return address of the
  573. ; routine making the call       to here, and the previous return
  574. ; address to the routine which actually pushed the args on the
  575. ; stack.) Thus, a call  to "ma1toh" would return with the first
  576. ; passed parameter in HL and A; "ma2toh" would return the second,
  577. ; etc. Note that if the caller has pushed [n] items on the stack
  578. ; before calling "ma [x] toh", then the [x-n]th formal parameter
  579. ; value will be returned, not the [x]th.
  580. ;
  581.  
  582. ma1toh: ld hl,4 ;get first arg
  583. ma0toh: add hl,sp
  584.         ld      a,(hl)
  585.         inc hl
  586.         ld      h,(hl)
  587.         ld      l,a
  588.         ret
  589.  
  590. ma2toh: ld hl,6 ;get 2nd arg
  591.         jp      ma0toh
  592.  
  593. ma3toh: ld hl,8 ;get 3rd arg
  594.         jp      ma0toh
  595.  
  596. ma4toh: ld hl,10        ;get 4th arg
  597.         jp      ma0toh
  598.  
  599. ma5toh: ld hl,12        ;get 5th arg
  600.         jp      ma0toh
  601.  
  602. ma6toh: ld hl,14        ;get 6th arg
  603.         jp      ma0toh
  604.  
  605. ma7toh: ld hl,16        ;get 7th arg
  606.         jp      ma0toh
  607.  
  608. ;
  609. ; This routine takes the first 7 args on the stack
  610. ; and places them contiguously at the "args" ram area.
  611. ; This allows a library routine to make one call        to arghak
  612. ; and henceforth have all it's args available directly
  613. ; through lhld's instead of having to hack the stack as it
  614. ; grows and shrinks. Note that arghak should be called as the
  615. ; VERY FIRST THING a function does, before even pushing BC.
  616. ;
  617.  
  618. arghak: ld de,args      ;destination for block lde in DE
  619.         ld hl,4 ;pass over two return address
  620.         add hl,sp       ;source for block lde in HL
  621.         push bc ;save BC
  622.         ld      b,14    ;countdown in B
  623. arghk2: ld      a,(hl)  ;copy loop
  624.         ld (de),a
  625.         inc hl
  626.         inc de
  627.         dec     b
  628.         jp nz,arghk2
  629.         pop bc  ;restore BC
  630.         ret
  631.  
  632. ;
  633. ; ABSOLUTELY NO CHANGES SHOULD EVER BE MADE TO THE CODE BEFORE
  634. ; THIS POINT IN THIS SOURCE FILE (except for customizing the EQU
  635. ; statements at the beginning of the file).
  636. ;
  637.  
  638.  
  639. ;
  640. ; This routine is called first to do argc & argv processing (if
  641. ; running under CP/M) and other initializations:
  642. ;
  643.  
  644. init:   pop hl  ;store return address
  645.         shld    tmp2    ; somewhere safe for the time being
  646.  
  647.         OS_HIDEFROMPARENT
  648.         ld e,6 ;textmode
  649.         OS_SETGFX
  650.        
  651.         IF      CPM
  652.         ld hl,arglst;-2 ;set up "argv" for the C main program
  653.         ENDIF
  654.        
  655.         IF      NOT CPM
  656.         ld hl,0
  657.         ENDIF
  658.  
  659.         push hl
  660.  
  661.                         ;Initialize storge allocation pointers:
  662.         lhld    freram  ;get address after end of externals
  663.         shld    allocp  ;store at allocation pointer (for "sbrk.")
  664.         ld hl,1000      ;default safety space between stack and
  665.         shld    alocmx  ; highest allocatable address in memory
  666.                         ; (for use by "sbrk".).
  667.  
  668.                         ;Initialize random seed:
  669.         ld hl,59dch     ;let's stick something wierd into the
  670.         shld    rseed   ;first 16 bits of the random-number seed
  671.  
  672.                         ;Initialize I/O hack locations:
  673.         ld      a,0dbh          ;"in" op, for "in xx; ret" subroutine
  674.         sta     iohack
  675.         ld      a,0d3h          ;"out" op for "out xx; ret" subroutine
  676.         sta     iohack+3
  677.         ld      a,0c9h          ;"ret" for above sobroutines
  678.         sta     iohack+2        ;the port number is filled in by the
  679.         sta     iohack+5        ;"inp" and "outp" library routines.
  680.  
  681.         IF      CPM
  682.         call    khack           ;initialize Kirkland debugger vector
  683.         ENDIF
  684.  
  685.         IF      CPM     ;under CP/M: clear console, process ARGC & ARGV:
  686.         ld      c,cstat ;interrogate console status to see if there
  687.         call    bdos    ;  happens to be a stray character there...
  688.  
  689.         or      a       ;(used to be `and 1'...they tell me this works
  690.         nop             ; better for certain bizarre CP/M-"like" systems)
  691.  
  692.         jp z,initzz
  693.         ld      c,conin   ;if input present, clear it
  694.         call    bdos
  695.  
  696. initzz: ld hl,tbuff             ;if arguments given, process them.
  697.         ld de,comlin    ;get ready to copy command line
  698.         ;ld     b,(hl)          ;first get length of it from loc. base+80h
  699.         ;inc hl
  700.         ;ld     a,b
  701.         ;or     a       ;if no arguments, don't parse for argv
  702.         ;jp nz,initl
  703.         ;ld de,1        ;set argc to 1 in such a case.
  704.         ;jp     i5
  705.  
  706. initl:  ld      a,(hl)  ;ok, there are arguments. parse...
  707.         ld (de),a       ;first copy command line to comlin
  708.          or a
  709.         inc hl
  710.         inc de
  711.         ;dec    b
  712.         jp nz,initl
  713.         ;xor    a       ;place zero following line
  714.         ;ld (de),a
  715.  
  716.         ld hl,comlin    ;now compute pointers to each arg
  717.         ld de,0;1               ;arg count
  718.         ld bc,arglst    ;where pointers will all go
  719.         xor     a               ;clear "in a string" flag
  720.         sta     tmp1
  721. i2:     ld      a,(hl)  ;between args...
  722.         inc hl
  723.         cp      ' '
  724.         jp z,i2
  725.         or      a
  726.         jp z,i5 ;if null byte, done with list
  727.         cp      '"'
  728.         jp nz,i2a       ;quote?
  729.         sta     tmp1    ;yes. set "in a string" flag
  730.         jp      i2b    
  731.  
  732. i2a:    dec hl
  733. i2b:    ld      a,l     ;ok, HL is a pointer to the start
  734.         ld (bc),a       ;of an arg string. store it.
  735.         inc bc
  736.         ld      a,h
  737.         ld (bc),a
  738.         inc bc
  739.         inc de  ;bump arg count
  740. i3:     ld      a,(hl)
  741.         inc hl  ;pass over text of this arg
  742.         or      a       ;if at end, all done
  743.         jp z,i5
  744.         push bc ;if tmp1 set, in a string
  745.         ld      b,a     ; (so we have to ignore spaces)
  746.         lda     tmp1
  747.         or      a
  748.         ld      a,b
  749.         pop bc
  750.         jp z,i3a
  751.         cp      '"'     ;we are in a string.
  752.         jp nz,i3        ;check for terminating quote
  753.         xor     a       ;if found, reset "in string" flag
  754.         sta     tmp1
  755.         dec hl
  756.         ld      (hl),a  ;and stick a zero byte after the string
  757.         inc hl  ;and go on to next arg
  758. i3a:    cp      ' '     ;now find the space between args
  759.         jp nz,i3
  760.         dec hl  ;found it. stick in a zero byte
  761.         ld      (hl),0
  762.         inc hl
  763.         jp      i2      ;and go on to next arg
  764.  
  765. i5:     push de ;all done finding args. Set argc.
  766.  
  767.         ld      b,3*nfcbs  ;now initialize all the file info
  768.         ld hl,fdt       ;by zeroing the fd table)
  769. i6:     ld      (hl),0
  770.         inc hl
  771.         dec     b
  772.         jp nz,i6
  773.         ENDIF
  774.  
  775.         IF      NOT CPM ;if not under CP/M, force ARGC value   
  776.         ld hl,1 ; of one.
  777.         push hl
  778.         ENDIF
  779.  
  780.         call    clrex   ;clear externals, if CLINK -z option NOT used
  781.         xor     a
  782.         sta     ungetl  ;clear the push-back byte,
  783.         sta     errnum  ;and file error code
  784.  
  785.         ld      a,0c3h  ;call c,'-Z' optimization initialization
  786.  
  787. ;
  788. ; -Z optimization initializations:
  789. ;
  790.  
  791.         IF      ZOPT1
  792.         sta     8       ;rst 1: jp fentrc
  793.         ld hl,fentrc
  794.         shld    9
  795.         ENDIF
  796.  
  797.         IF      NOT ZOPT1 AND PRESRV
  798.         nop
  799.         dw      0,0,0,0         ;more NOPs
  800.         ENDIF
  801.  
  802.  
  803.         IF      ZOPT2
  804.         sta     10h
  805.         ld hl,fexitc ;rst 2:jp fexitc
  806.         shld    11h
  807.         ENDIF
  808.  
  809.         IF      NOT ZOPT2 AND PRESRV
  810.         nop
  811.         dw      0,0,0,0         ;more NOPs
  812.         ENDIF
  813.  
  814.  
  815.         IF      ZOPT5
  816.         sta     28h     ;rst5:  jp sdli
  817.         ld hl,sdli
  818.         shld    29h
  819.         ENDIF  
  820.  
  821.         IF      NOT ZOPT5 AND PRESRV
  822.         nop
  823.         dw      0,0,0,0         ;more NOPs
  824.         ENDIF
  825.  
  826.  
  827.         IF      ZOPT6
  828.         sta     30h     ;rst6:  jp ldli
  829.         ld hl,ldli
  830.         shld    31h
  831.         ENDIF
  832.  
  833.         IF      NOT ZOPT6 AND PRESRV
  834.         nop
  835.         dw      0,0,0,0         ;more NOPs
  836.         ENDIF
  837.  
  838.  
  839.         IF      ZOPT3
  840.         ld hl,237eh     ;rst3:  ld a,(hl)
  841.         shld    18h     ;       inc hl
  842.         ld hl,6f66h     ;       ld h,(hl)
  843.         shld    1ah     ;       ld l,a
  844.         ld      a,0c9h  ;       ret
  845.         sta     1ch
  846.         ENDIF
  847.  
  848.         IF      NOT ZOPT3 AND PRESRV
  849.         nop
  850.         dw      0,0,0,0         ;more NOPs
  851.         dw      0,0,0,0
  852.         ENDIF
  853.  
  854.  
  855.         IF      ZOPT4
  856.         ld hl,2373h     ;rst4:  ld (hl),e
  857.         shld    20h     ;       inc hl
  858.         ld hl,0c972h ;  ld (hl),d
  859.         shld    22h     ;       ret
  860.         ENDIF
  861.  
  862.         IF      NOT ZOPT4 AND PRESRV
  863.         dw      0,0,0   ;lotsa NOPs
  864.         dw      0,0,0
  865.         ENDIF
  866.  
  867.  
  868.         IF      ZOPT7
  869.         ld hl,235eh
  870.         shld    38h
  871.         ld hl,0c956h
  872.         shld    3ah
  873.  
  874.         ld hl,2b72h
  875.         shld    3ch
  876.         ld hl,0c973h
  877.         shld    3eh
  878.         ENDIF
  879.  
  880.         IF      NOT ZOPT7 AND PRESRV
  881.         dw      0,0,0,0,0,0     ;you guessed it -- NOPs
  882.         dw      0,0,0,0,0,0
  883.         ENDIF
  884.  
  885.         lhld    tmp2
  886.         jp (hl)         ;all done initializing.
  887.        
  888.         IF      ZOPT2   ;object of rst 2 vector, if enabled
  889. fexitc: pop de  ;get offset address
  890.         ex de,hl                ;return value in DE, &offset in HL
  891.         ld      l,(hl)  ;put byte offset in HL
  892.         ld      h,0
  893.         add hl,sp       ;add to SP
  894.         ld sp,hl
  895.         ex de,hl                ;put return value back in HL
  896.         pop bc  ;restore BC
  897.         ret             ;and return to previous function
  898.         ENDIF
  899.  
  900.         IF      NOT ZOPT2 AND PRESRV
  901.         dw      0,0,0   ;NOPs
  902.         dw      0,0
  903.         ENDIF
  904.  
  905.  
  906. ;
  907. ; The following two routines are used when the "-tn" CLINK option
  908. ; is given, in order to preserve the SP value passed to the transient
  909. ; command by the CCP and return to the CCP after execution without
  910. ; needing to perform a warm-boot.
  911. ;
  912.  
  913.         IF CPM
  914. vsnobsp:
  915.         ld hl,0         ;get CCP's SP value in HL
  916.         add hl,sp
  917.         shld    spsav           ;save it for later
  918.         lhld    base+6          ;get BIOS pointer
  919.         ld de,-2100             ;subtract size of CCP plus a fudge
  920.         add hl,de
  921.         ld sp,hl                        ;make that the new SP value
  922.         jp      tpa+3           ;and get things under way...
  923.  
  924. vnobret:
  925.         lhld    spsav           ;restore CCP's SP
  926.         ld sp,hl
  927.         ret                     ;return to CCP
  928.         ENDIF
  929.  
  930. ;
  931. ; The following routine gets called to clear the external
  932. ; data area, unless the CLINK "-z" option is used.
  933. ;
  934.  
  935. vclrex: lhld    freram  ;clear externals
  936.         ex de,hl
  937.         lhld    extrns
  938.         call    cmh
  939.         add hl,de       ;HL now holds size of external data area
  940. clrex1: ld      a,h     ;loop till done
  941.         or      l
  942.         ret z
  943.         dec de
  944.         dec hl
  945.         xor     a
  946.         ld (de),a
  947.         jp      clrex1
  948.  
  949.  
  950. ;
  951. ; Initialize Kirkland interrupt vector... enables
  952. ; programs compiled with "-k" to run without the debugger:
  953. ;
  954.  
  955.         IF USERST
  956. vkhack: ld hl,0E1H+2300H        ;pop hl - inc hl
  957.         shld    rstloc          ; put at "RST 6" location (or wherever)
  958.         ld hl,023H+0E900H       ;inc hl - jp (hl)
  959.         shld    rstloc+2
  960.         ret
  961.         ENDIF
  962.  
  963.         IF NOT USERST
  964. vkhack: ret
  965.         ENDIF
  966.  
  967.         IF NOT USERST AND PRESRV
  968.         ds 12
  969.         ENDIF
  970.  
  971. ;
  972. ; General purpose error value return routine:
  973. ;
  974.  
  975. verror: ld hl,-1        ;general error handler...just
  976.         ret             ;returns -1 in HL
  977.  
  978. ;
  979. ; Here are file I/O handling routines, only needed under CP/M:
  980. ;
  981.  
  982. ;
  983. ; Close any open files and reboot:
  984. ;
  985.  
  986. vexit:
  987.         IF      CPM             ;if under CP/M, close all open files
  988.         ld      a,7+nfcbs       ;start with largest possible fd
  989. exit1:  push    af              ;and scan all fd's for open files
  990.         call    vfgfd           ;is file whose fd is in A open?
  991.         jp c,exit2              ;if not, go on to next fd
  992.         ld      l,a             ;else close the associated file
  993.         ld      h,0
  994.         push hl
  995.         call    vclose
  996.         pop hl
  997. exit2:  pop     af
  998.         dec     a               ;and go on to next one
  999.         cp      7
  1000.         jp nz,exit1
  1001.         ENDIF
  1002.  
  1003.         jp      fexitv          ;done closing...now return
  1004.                                 ; to CP/M or whatever.
  1005.  
  1006.  
  1007. ;
  1008. ; Close the file whose fd is 1st arg:
  1009. ;
  1010.        if NEDOOS != 0
  1011. vclose:
  1012.         call    ma1toh  ;get fd in A
  1013.         call    vfgfd   ;see if it is open ;return hl=fd addr
  1014.         ;jp c,verror    ;if not, complain
  1015.         push bc
  1016.         ld b,(hl)
  1017.         ld (hl),0 ;fd closed
  1018.         OS_CLOSEHANDLE
  1019.         ld hl,0 ;OK
  1020.         pop bc
  1021.         ret
  1022.        else
  1023.  
  1024.         IF      CPM     ;here comes a lot of CP/M stuff...
  1025. vclose:
  1026.         call    ma1toh  ;get fd in A
  1027.         call    vfgfd   ;see if it is open
  1028.         ;jp c,verror    ;if not, complain ;TODO why fail?
  1029.         ;ld     a,(hl)
  1030.         ;call   setusr  ;set user area to match current fd
  1031.         ;and    4       ;check if open for writing
  1032.  
  1033.         ENDIF
  1034.  
  1035.         IF CPM AND NOT MPM2     ;if not MP/M, and
  1036.         ;jp z,close2    ;the file isn't open for write, don't bother to close
  1037.         ENDIF
  1038.  
  1039.         IF CPM AND MPM2 AND PRESRV  ;always close all files under MP/M
  1040.         nop
  1041.         nop
  1042.         nop
  1043.         ENDIF
  1044.  
  1045.         IF CPM
  1046.         push hl ;save fd table entry addr
  1047.         call    ma2toh  ;get the fd in A again
  1048.         push bc
  1049.         call    vfgfcb  ;get the appropriate fcb address
  1050.         ex de,hl                ;put it in DE
  1051.         ld      c,closec  ;get BDOS function # for close
  1052.         call    bdos    ;and do it!
  1053.         pop bc
  1054.         pop hl
  1055. close2: ;call   rstusr  ;reset user number to original state
  1056.         ld      (hl),0  ;close the file logically
  1057.         cp      255     ;if 255 came back from bdos, we got problems
  1058.         ld hl,0
  1059.         ret nz          ;return 0 if OK
  1060.         dec hl  ;return -1 on error
  1061.         ret
  1062.         ENDIF
  1063.  
  1064.        endif
  1065.  
  1066. ;
  1067. ; Determine status of file whose fd is in A...if the file
  1068. ; is open, return Cy clear and with the address of the fd table
  1069. ; entry for the open file in HL. If the file is not open,
  1070. ; return Cy set:
  1071. ;
  1072.  
  1073. vfgfd:  ld      d,a
  1074.         sub     8
  1075.         ret c           ;if fd < 8, error
  1076.         cp      nfcbs
  1077.         ccf             ;don't allow too big an fd either
  1078.         ret c
  1079.         push de
  1080.         ld      e,a     ;OK, we have a value in range. Now
  1081.         ld      d,0     ;  see if the file is open or not
  1082.         ld hl,fdt
  1083.         add hl,de       ;offset for 3-byte table entries
  1084.         add hl,de
  1085.         add hl,de
  1086.         ld      a,(hl)
  1087.         pop de
  1088.        if NEDOOS != 0        
  1089.         sub 1
  1090.         ret ;CY if not open
  1091.        else
  1092.         and     1       ;bit 0 is high if file is open
  1093.         scf
  1094.         ld      a,d
  1095.         ret z           ;return C set if not open
  1096.         ccf
  1097.         ret             ;else reset C and return
  1098.        endif
  1099.  
  1100.        if NEDOOS != 0
  1101. vsetfcb=0
  1102.        else
  1103. ;
  1104. ; Set up a CP/M file control block at HL with the file whose
  1105. ; simple null-terminated name is pointed to by DE:
  1106. ; Format for filename must be: "[white space][d:]filename.ext"
  1107. ; The user number prefix hack is NOT recognized by this subroutine.
  1108. ;
  1109.        IF CPM
  1110. vsetfcb: push bc
  1111.         call    igwsp   ;ignore blanks and tabs
  1112.         push hl ;save fcb ptr
  1113.         inc de  ;peek at 2nd char of filename
  1114.         ld a,(de)
  1115.         dec de
  1116.         cp      ':'     ;default disk byte value is 0
  1117.         ld      a,0     ; (for currently logged disk)
  1118.         jp nz,setf1
  1119.         ld a,(de)       ;oh oh...we have a disk designator
  1120.         call    mapuc   ;make it upper case
  1121.         sub     'A'-1   ;and fudge it a bit
  1122.         inc de  ;advance DE past disk designator to filename
  1123.         inc de
  1124. setf1:  ld      (hl),a  ;set disk byte
  1125.         inc hl
  1126.         ld      b,8
  1127.         call    setnm   ;set filename, pad with blanks
  1128.         call    setnm3  ;ignore extra characters in filename
  1129.         ld a,(de)
  1130.         cp      '.'     ;if an extension is given,
  1131.         jp nz,setf2
  1132.         inc de  ;skip the '.'
  1133. setf2:  ld      b,3
  1134.         call    setnm   ;set the extension field and pad with blanks
  1135.         xor     a       ;and zero the appropriate fields of the fcb
  1136.         ld      (hl),a
  1137.         ld de,20
  1138.         add hl,de
  1139.         ld      (hl),a
  1140.         inc hl
  1141.         ld      (hl),a  ;zero random record bytes of fcb
  1142.         inc hl
  1143.         ld      (hl),a
  1144.         inc hl
  1145.         ld      (hl),a
  1146.         pop de
  1147.         pop bc
  1148.         ret
  1149.        ENDIF
  1150.        endif
  1151. ;
  1152. ; This routine copies up to B characters from (DE) to (HL),
  1153. ; padding with blanks on the right. An asterisk causes the rest
  1154. ; of the field to be padded with '?' characters:
  1155. ;
  1156.  
  1157.        if NEDOOS == 0
  1158.        if CPM
  1159. setnm:  push bc
  1160. setnm1: ld a,(de)
  1161.         cp      '*'     ;wild card?
  1162.         ld      a,'?'   ;if so, pad with ? characters
  1163.         jp z,pad2
  1164.  
  1165. setnm2: ld a,(de)
  1166.         call    legfc   ;next char legal filename char?
  1167.         jp c,pad        ;if not, go pad for total of B characters
  1168.         ld      (hl),a  ;else store
  1169.         inc hl
  1170.         inc de
  1171.         dec     b
  1172.         jp nz,setnm1    ;and go for more if B not yet zero
  1173.         pop bc
  1174. setnm3: ld a,(de)       ;skip rest of filename if B chars already found
  1175.         call    legfc
  1176.         ret c
  1177.         inc de
  1178.         jp      setnm3
  1179.  
  1180. pad:    ld      a,' '   ;pad with B blanks
  1181. pad2:   ld      (hl),a  ;pad with B instances of char in A
  1182.         inc hl
  1183.         dec     b
  1184.         jp nz,pad2
  1185.         pop bc
  1186.         ret
  1187.        endif
  1188.        endif
  1189.  
  1190.        if NEDOOS != 0
  1191. vstfcu=0
  1192.        else
  1193. ; Process filename having optional user area number prefix of form "<u#>/",
  1194. ; return the effective user area number of the given filename in the upper
  1195. ; 5 bits of A, and also store this value at "usrnum". Note that if no user
  1196. ; number is specified, the current user area is presumed by default. After
  1197. ; the user area prefix is processed, do a regular "setfcb":
  1198. ;
  1199. ; Note: a filename is considered to have a user number if the first char
  1200. ;       in the name is a decimal digit and the first non-decimal-digit
  1201. ;       character in the name is a slash (/).
  1202.  
  1203.         if CPM
  1204. vstfcu: push bc ;save BC
  1205.         push hl ;save vcb pointer
  1206.         call    igwsp   ;ignore blanks and tabs
  1207.         call    isdec   ;decimal digit?
  1208.         jp nc,setfc2    ;if so, go process
  1209.  
  1210. setfc0: push de ;save text pointer
  1211.         ;ld     c,gsuser  ;else get current effective user number
  1212.         ;ld     e,0ffh
  1213.         ENDIF
  1214.  
  1215.         IF      CPM AND USAREA
  1216.         ;call   bdos    ;get current user area if implemented
  1217.          xor a
  1218.         ENDIF
  1219.  
  1220.         IF      CPM AND NOT USAREA
  1221.         ld      a,0
  1222.         nop
  1223.         ENDIF
  1224.  
  1225.         IF      CPM
  1226.         pop de  ;restore text pointer
  1227. setfc1: rlca            ;rotate into upper 5 bits of A
  1228.         rlca
  1229.         rlca
  1230.         sta     usrnum  ;and save
  1231.         pop hl  ;restore junk
  1232.         pop bc
  1233.         jp      setfcb  ;and parse rest of filename
  1234.  
  1235. setfc2: ld      b,0     ;clear user number counter
  1236.         push de ;save text pointer in case we invalidate user prefix
  1237. setfc3: sub     '0'     ;save next digit value
  1238.         ld      c,a     ; in C
  1239.         ld      a,b     ;multiply previous sum by 10
  1240.         add     a       ;*2
  1241.         add     a       ;*4
  1242.         add     a       ;*8
  1243.         add     b       ;*9
  1244.         add     b       ;*10
  1245.         add     c       ;add new digit
  1246.         ld      b,a     ;put sum in B
  1247.         inc de  ;look at next char in text
  1248.         ld a,(de)       ;is it a digit?
  1249.         call    isdec
  1250.         jp nc,setfc3    ;if so, go on looping and summing digits
  1251.         cp      '/'     ;make sure number is terminated by a slash
  1252.         jp z,setfc4
  1253.         pop de  ;if not, entire number prefix is not really a
  1254.         jp      setfc0  ; user number, so just ignore it all.
  1255.  
  1256. setfc4: inc de  ;ok, allow the user number
  1257.         pop hl  ;get old text pointer off the stack
  1258.         ld      a,b     ;get user number value
  1259.         jp      setfc1  ;and go store it and parse rest of filename
  1260.  
  1261.  
  1262. ;
  1263. ; Test if char in A is legal character to be in a filename:
  1264. ;
  1265.  
  1266. legfc:  call    mapuc
  1267.         cp      '.'     ; '.' is illegal in a filename or extension
  1268.         scf
  1269.         ret z
  1270.         cp      ':'     ;so is ':'
  1271.         scf    
  1272.         ret z
  1273.         cp      7fh     ;delete is no good
  1274.         scf
  1275.         ret z
  1276.         cp      '!'     ;if less than exclamation pt, not legal char
  1277.         ret             ;else good enough
  1278.  
  1279. ;
  1280. ; Map character in A to upper case if it is lower case:
  1281. ;
  1282.  
  1283. mapuc:  cp      'a'
  1284.         ret c
  1285.         cp      'z'+1
  1286.         ret nc
  1287.         sub     32      ;if lower case, map to upper
  1288.         ret
  1289.  
  1290. ;
  1291. ; Ignore blanks and tabs at text pointed to by DE:
  1292. ;
  1293.  
  1294. igwsp:  dec de
  1295. igwsp1: inc de
  1296.         ld a,(de)
  1297.         cp      ' '
  1298.         jp z,igwsp1
  1299.         cp      9
  1300.         jp z,igwsp1
  1301.         ret
  1302.  
  1303. ;
  1304. ; Return Cy if char in A is not a decimal digit:
  1305. ;
  1306.  
  1307. isdec:  cp      '0'
  1308.         ret c
  1309.         cp      '9'+1
  1310.         ccf
  1311.         ret
  1312.        endif
  1313.        endif
  1314.  
  1315. ;
  1316. ; This routine does one of two things, depending
  1317. ; on the value passed in A.
  1318. ;
  1319. ; If A is zero, then it finds a free file slot
  1320. ;  (if possible), else returns C set.
  1321. ;
  1322. ; If A is non-zero, then it returns the address
  1323. ; of the fcb (fd in NEDOOS) corresponding to an open file whose
  1324. ; fd happens to be the value in A, or C set if there
  1325. ; is no file associated with fd.
  1326. ;
  1327.  
  1328. vfgfcb: push bc
  1329.         or      a       ;look for free slot?
  1330.         ld      c,a
  1331.         jp nz,fgfc2     ;if not, go away
  1332.         ld      b,nfcbs ;yes. do it...
  1333.         ld de,fdt
  1334.         if NEDOOS == 0
  1335.         ld hl,fcbt
  1336.         endif
  1337.         ld      c,8
  1338. fgfc1:  ld a,(de)
  1339.         and     1
  1340.         ld      a,c
  1341.         jp nz,fgfc1a    ;found free slot?
  1342.         pop bc  ;yes. all done.
  1343.         ret ;return DE=fd addr, A=fd
  1344. fgfc1a:
  1345.         if NEDOOS == 0
  1346.         push de
  1347.         ld de,36        ;fcb length to accommodate random I/O
  1348.         add hl,de
  1349.         pop de
  1350.         endif
  1351.         inc de  ;bump to next 3-byte table entry
  1352.         inc de
  1353.         inc de
  1354.         inc     c
  1355.         dec     b
  1356.         jp nz,fgfc1
  1357. fgfc1b: scf
  1358.         pop bc
  1359.         ret             ;return C if no more free slots
  1360. fgfc2:
  1361.         if NEDOOS != 0
  1362.         call    vfgfd   ;compute fd address for fd in A: ;return hl=fd addr
  1363.         ;jp c,fgfc1b    ;return C if file isn't open        
  1364.         else
  1365.         call    vfgfd   ;compute fd address for fd in A:
  1366.         ;jp c,fgfc1b    ;return C if file isn't open ;TODO why fail?
  1367.         sub     8
  1368.         ld      l,a     ;put (fd-8) in HL
  1369.         ld      h,0
  1370.         add hl,hl       ;double it
  1371.         add hl,hl       ;4*a
  1372.         ld      d,h     ;save 4*a in DE
  1373.         ld      e,l
  1374.         add hl,hl       ;8*a
  1375.         add hl,hl       ;16*a
  1376.         add hl,hl       ;32*a
  1377.         add hl,de       ;36*a
  1378.         ex de,hl                ;put 36*a in DE
  1379.         ld hl,fcbt      ;add to base of table
  1380.         add hl,de       ;result in HL
  1381.         endif
  1382.         ld      a,c     ;and return original fd in A
  1383.         pop bc
  1384.         ret
  1385.  
  1386.         if NEDOOS != 0
  1387. vsetusr=0
  1388. vrstusr=0
  1389.         else
  1390. ;
  1391. ; The following two subroutines change the current CP/M user area for
  1392. ; use with file I/O:
  1393. ;
  1394.  
  1395.         IF CPM
  1396. vsetusr:
  1397.         push bc ;SET user number to upper bits of A, save current:
  1398.         push hl
  1399.         push de
  1400.         push    af      ;save A
  1401.         ld      c,gsuser ;get user code
  1402.         ld      e,0ffh
  1403.         ENDIF
  1404.  
  1405.         IF      CPM AND USAREA
  1406.         call    bdos
  1407.         ENDIF
  1408.  
  1409.         IF      CPM AND NOT USAREA
  1410.         ld      a,0
  1411.         nop
  1412.         ENDIF
  1413.  
  1414.         IF CPM
  1415.         sta     curusr  ;save current user number
  1416.         pop     af      ;get new user number byte
  1417.         push    af
  1418.         rra             ;shift user number down to low bits
  1419.         rra
  1420.         rra
  1421.         and     1fh     ;and mask off high order garbage
  1422. setu0:  ld      e,a
  1423.         ld      c,gsuser  ;set user code
  1424.         ENDIF
  1425.  
  1426.         IF      CPM AND USAREA
  1427.         call    bdos
  1428.         ENDIF
  1429.  
  1430.         IF      CPM AND NOT USAREA AND PRESRV
  1431.         nop
  1432.         nop
  1433.         nop
  1434.         ENDIF
  1435.  
  1436.         IF      CPM
  1437.         pop     af
  1438.         pop de
  1439.         pop hl
  1440.         pop bc
  1441.         ret
  1442.  
  1443. vrstusr:
  1444.         push bc
  1445.         push hl
  1446.         push de
  1447.         push    af
  1448.         lda     curusr  ;get last saved user number
  1449.         jp      setu0   ;and go set current user area to that
  1450.  
  1451.         ENDIF           ;end of CP/M-related file I/O routines
  1452.  
  1453.         endif
  1454.  
  1455. ;       IF      NOT CPM
  1456. ;main:  equ     $       ;where main program resides when not under CP/M
  1457.                         ;(under CP/M, the data area comes first)
  1458. ;       ENDIF
  1459.  
  1460.  
  1461. ;
  1462. ; Ram area:
  1463. ;
  1464.  
  1465.         ;IF     CPM     ; Plug this value into BDS.LIB before CASM'ing
  1466. ram     equ     $       ; the new library. The "org ram" at the end of this
  1467.         ;ENDIF          ; source file should cause the assembler to print
  1468.                         ; the value of "ram" at the end of the assembly.
  1469.  
  1470.         ;IF     NOT CPM
  1471.         ;org    ram     ;if not under CP/M, use custom ram area address
  1472.         ;ENDIF
  1473.  
  1474. errnum: ds      1       ;error code from file I/O operations
  1475. rseed:  ds      8       ;the random generator seed
  1476. args:   ds      14      ;"arghak" puts args passed on stack here.
  1477. iohack: ds      6       ;room for I/O subroutines for use by "inp"
  1478.                         ;and "outp" library routines
  1479.  
  1480. allocp: ds      2       ;pointer to free storge for use by "sbrk" func
  1481. alocmx: ds      2       ;highest location to be made available to the
  1482.                         ;storge allocator
  1483.  
  1484.                         ;20 bytes of misc. scratch & state variables:
  1485. tmp     ds      1
  1486. tmp1    ds      1
  1487. tmp2    ds      2
  1488. tmp2a   ds      2
  1489. unused  ds      2
  1490.  
  1491. curusr  ds      1       ;used to save current user number during file I/O
  1492. usrnum  ds      1       ;set by "setfcu" to user number of given filename
  1493.  
  1494.                         ;Console I/O control data:
  1495. chmode  db      0       ;0: single char mode, 1: line buffered mode
  1496. nleft   db      0       ;# of chars left in buffer (if chmode == 1)
  1497. ungetl  db      0       ;"ungetch" data byte (0 if no char pushback)
  1498. iobrf   db      1       ;check for break on character input/output
  1499.  
  1500. spsav   ds      2       ;BDOS's saved SP value upon entry from CCP
  1501.  
  1502.         ds      4       ;total of 20 bytes of misc. data area
  1503.  
  1504. ;
  1505. ;--------------------------------------------------------------------------
  1506. ; The following data areas are needed only if running under CP/M:
  1507. ;
  1508.  
  1509.         IF      CPM
  1510. ;
  1511. ; The fcb table (fcbt): 36 bytes per file control block
  1512. ;
  1513.  
  1514.         if NEDOOS == 0
  1515. fcbt:   ds      36*nfcbs        ;reserve room for fcb's (extra byte for IMDOS)
  1516.         endif
  1517.  
  1518. ;
  1519. ; The fd table: three bytes per file specifying r/w/open as follows:
  1520. ;   BYTE 1:
  1521. ;       bit 0 is high if open, low if closed
  1522. ;       bit 1 is high if open for read
  1523. ;       bit 2 is high if open for write  (both b1 and b2 may be high)
  1524. ;       bits 3-7 contain the user number in which the file is active (0-31)
  1525. ;   BYTES 2&3:
  1526. ;       Highest sector number seen so far during I/O (for cfsize calls)
  1527. ;
  1528.  
  1529. fdt:    ds      3*nfcbs
  1530.  
  1531. ;
  1532. ; The command line is copied here by init:
  1533. ;
  1534.  
  1535. comlin: ds      131     ;copy of the command line pointed to by entries
  1536.                         ;in arglst
  1537.  
  1538.  
  1539. ;
  1540. ; This is where "init" places the array of argument pointers:
  1541. ;
  1542.  
  1543. arglst: ds      40      ;the "argv" paramater points here ([well,
  1544.                         ;actually to 2 bytes before arglst]). Thus,
  1545.                         ;up to 20 parameters may be passed to "main"
  1546.         ENDIF
  1547.  
  1548. ;
  1549. ; End of CP/M-only data area
  1550. ;---------------------------------------------------------------------------
  1551.  
  1552.         ;IF     CPM
  1553. main    equ     $       ;where "main" program will be loaded under CP/M
  1554.         ;ENDIF
  1555.  
  1556.         ;IF NOT M80
  1557.         ;org    ram     ;set next pc value back to ram origin, so the value
  1558.         ;ENDIF          ;will be displayed by the assembler for convenience
  1559.  
  1560.