Subversion Repositories NedoOS

Rev

Rev 634 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download

  1.        
  2. ;
  3. ; clinka.asm:
  4. ;
  5. ; BDS C Linker 3/86
  6. ;
  7. ; Copyright (c) 1980, 1981, 1982, 1986  by Leor Zolman, BD Software, Inc.
  8. ;
  9. ; Added -f option 4/15/81: forces loading all functions in next crl file
  10. ; Added -n option 9/9/82: activates NOBOOT mechandsm in COM file
  11. ; Added defdsk/defusr mechandsm to library file searches 9/12/82
  12. ; Added -z option to inhibit clearing of externals 11/15/82
  13. ;
  14.  
  15. ; MARC-specific features:
  16. ;   Gets deff.crl, deff2.crl and c.ccall c,from /libC unless -c is given
  17. ;   Added -i option to produce absolute image under MARC instead of default
  18. ;          load file formatted output.
  19. ;   Added -m option for MARC: makes c.ccall c,do a "mexmem" call before setting SP
  20. ;       Changed -m to cause NON-maxmem. I.e., replaced "call marc" with "nop"s.
  21.  
  22.  
  23.         ;org tpa
  24.  
  25.         jp clink
  26.  
  27. ;
  28. ; Configuration block:
  29. ;
  30.         if not PREREL
  31. defdsk: db 0ffh         ;default disk for library files (0=A, etc., FF=current)
  32. defusr: db 0ffh         ;default user area for lib files, FF = current
  33.         endif
  34.  
  35.         if PREREL
  36. defdsk: db 0ffh
  37. defusr: db 0ffh
  38.         endif
  39.  
  40. defsub: db 0            ;default submit file on A: for distribution version
  41. conpol: db 1            ;true to poll for console interrupts
  42. wboote: db 0            ;true to perform warm-boot on exit
  43. pstrip: db 1            ;(dummy in CLINK:) parity strip
  44. nouser: db 0            ;true to disable usage of set user area calls
  45.  
  46. ;
  47. ;end configuration block
  48. ;
  49.  
  50. patch:  ds 19           ;reserved for future expansion (adjust when adding
  51.                         ;               bytes above)
  52.  
  53. clink:
  54.         OS_HIDEFROMPARENT
  55.         ld e,6 ;textmode
  56.         OS_SETGFX
  57.        
  58.         ;shld zenvad    ;save Z environment pointer (if in HL)
  59.         ;ld hl,0                ;save system SP
  60.         ;add hl,sp
  61.         ;shld spsav     ;save system SP
  62.         ld sp,0;stack   ;set up own stack
  63.         ;call zsetup
  64.  
  65.         lhld errbyt
  66.         ld (hl),0               ;no error by default
  67.  
  68.          call copycline
  69. ;TODO options before filename? but the example is "clink $1 -s"
  70.         ld de,cline
  71.         ld hl,fcb+1
  72.         ;OS_PARSEFNAME
  73.         ld b,8
  74. parsefn0
  75.         ld a,(de)
  76.         ld (hl),a
  77.         cp '.'
  78.         jr z,parsefn1
  79.         cp ' '+1
  80.         jr c,parsefn1
  81.         inc hl
  82.         inc de        
  83.         djnz parsefn0
  84.         jr parsefnq
  85. parsefn1
  86.         ld (hl),' '
  87.         inc hl
  88.         djnz parsefn1
  89. parsefnq
  90.  
  91.         call linit      ;initialize linker
  92.  
  93.         ld de,stg0      ;print opening message
  94.         call pstg
  95.  
  96.         xor a
  97.         sta zsysf       ;not zsystems, by default
  98.  
  99.         call comlin     ;process immediate command line options
  100.  
  101.         IF CPM
  102.         lda defdsk      ;set default disk for library files
  103.         inc a
  104.         sta deffs
  105.         sta deff2s
  106.         sta deff3s
  107.         sta ccc
  108.         ENDIF
  109.  
  110.  
  111.         ld de,tab1      ;set code area pointer to
  112.         lhld tb1siz
  113.         push hl         ;save tab1 size
  114.         add hl,de
  115.         dec hl
  116.         shld tb1end     ;save addr of end of tab1
  117.         ld de,100       ;leave some space in case of
  118.         add hl,de               ; tab1 overflow
  119.         shld cda
  120.         shld cdp
  121.  
  122.         ld hl,tab1
  123.         pop bc          ;get tab1 size
  124. c4:     ld (hl),0               ;clear tab1
  125.         inc hl
  126.         dec bc
  127.         ld a,b
  128.         or c
  129.         jp nz,c4                ;if not, keep looping
  130.  
  131.         IF TESTING3     ;print out contents of fcbs
  132.         push af
  133.         call printf
  134.           db 'before reading c.ccc, fcbs is: ',0
  135.         push hl
  136.         ld hl,fcbs
  137.         call pfnamu
  138.         pop hl
  139.         call crlf
  140.         pop af
  141.         ENDIF
  142.  
  143.         IF CPM
  144.         call setdu      ;set up default library file user areas
  145.         ENDIF
  146.  
  147.         call readc      ;read in c.ccc (if not a segment)
  148.  
  149.         IF ZSYS ;had no endif!!!
  150.         lhld cda        ;check load address for consistency
  151.         ld de,0bh       ;offset for load addr in header
  152.         add hl,de
  153.         ld e,(hl)
  154.         inc hl
  155.         ld d,(hl)               ;DE now = load addr from header
  156.         lhld runsat
  157.         call cmphd
  158.         jp z,c400
  159.        
  160.         ld de,stglde
  161.         call pstg
  162.         jp abort
  163. c400:
  164.         ENDIF
  165. ;       IF CPM
  166.         ;lda curusr     ;go back to current user area
  167.         ;ld e,a
  168.         ;ld c,sguser
  169.         ;lda nouser
  170.         ;or a
  171.         ;IF NOT ALPHA
  172.         ;call z,bdos
  173.         ;ENDIF
  174.         ;ENDIF
  175.  
  176.         IF MARC
  177.         ld hl,fcbt      ;put crl extension on main filename
  178.         push hl
  179.         call scrl
  180.         pop hl
  181.         ENDIF
  182.  
  183. c40:
  184.         IF TESTING3     ;print out contents of fcbs
  185.         push af
  186.         call printf
  187.           db 'before reading in main file, fcbs is: ',0
  188.         push hl
  189.         ld hl,fcbs
  190.         call pfnamu
  191.         pop hl
  192.         call crlf
  193.         pop af
  194.         ENDIF
  195.  
  196.         call gnfl       ;open main file and read in directory
  197.         jp c,abort      ;abort if can't find it
  198.  
  199.         IF TESTING
  200.         call printf
  201.            db 'just successfully opened main file',0
  202.         ENDIF
  203.  
  204.         IF CPM
  205.         ld a,4
  206.         sta nr
  207.         call reads      ;get external data area size and exaddr parameters
  208.         ENDIF
  209.  
  210.         IF NOT CPM
  211.         ld hl,tbuff
  212.         ld de,10
  213.         call reads
  214.         ENDIF
  215.  
  216.         lhld tbuff+3    ;get external data size.
  217.         shld smsf
  218.  
  219.         lda tbuff       ;was explicit external area given?
  220.         or a
  221.         jp z,c4a                ;if not, skip setting eaddr
  222.         sta eflag       ;if so, tell that we have an explicit address
  223.         lhld tbuff+1    ;and copy the passed external address into eaddr
  224.         shld eaddr
  225.  
  226. c4a:    ld hl,stgm      ;read in "main" function
  227.         call ft2        ;is it there?
  228.         jp nc,link2
  229.  
  230.         ld hl,stgmn     ;try long version also
  231.         call ft2
  232.         jp nc,link2
  233.  
  234.         ld de,stg6      ;no. can't link.
  235. c2:     call pstg       ;print error message
  236.  
  237.         IF MARC
  238.         ld hl,fcbt
  239.         ENDIF
  240.  
  241.         call pfnamu     ;print file in which error occurred
  242.         jp abort        ;and abort
  243.  
  244. link2:  ld a,255        ;initialize function number to -1
  245.         sta fcnt
  246.         ld hl,stgmn
  247.  
  248.         ld a,1
  249.         sta gotf
  250.  
  251.         call entt1      ;enter "main" function in table
  252.  
  253.         call pre        ;read it in
  254.  
  255.         IF TESTING
  256.         call printf
  257.            db 'returned from ''pre'' call for main function...',0
  258.         ENDIF
  259.  
  260. link3:  call gtfns      ;get all we can from current CRL file
  261.  
  262. link4:
  263. ;       IF CPM
  264.         ld a,1
  265.         sta search      ;search default path unless otherwise set by getfn
  266.  
  267.         ;ld c,sguser    ;set user area back to current
  268.         ;lda curusr
  269.         ;ld e,a
  270.         ;lda nouser
  271.         ;or a
  272.         ;IF NOT ALPHA
  273.         ;call z,bdos
  274.         ;ENDIF
  275.  
  276.         ;lda savdrv     ;any drive number saved?
  277.         ;or a
  278.         ;jp z,link5
  279.         ;dec a          ;if so, change back to it
  280.         ;ld e,a
  281.         ;ld c,select
  282.         ;call bdos     
  283.         ;xor a
  284.         ;sta savdrv     ;and clear saved drive byte
  285. ;       ENDIF
  286.  
  287. link5:  call fungt      ;are there any functions missing?
  288.         jp c,linkd      ;if not, all done
  289.  
  290.         call getfn      ;else get name of new CRL file to search
  291.        
  292.         IF MARC
  293.         ld hl,fcbt      ;get pointer to the just-gotten crl filename
  294.         ENDIF
  295.  
  296.         call gnfl       ;open it and read in directory
  297.  
  298.         jp nc,link3     ;if opened OK, go read in some functions...
  299.  
  300. ;
  301. ; At this point, all files on the command line and all default
  302. ; library files have been loaded...
  303. ;
  304.  
  305.         IF MARC         ;under MARC, set dflag to indicate that all
  306.         ld a,1          ;deff?.crl files have been searched at least once.
  307.         sta dflag
  308.         ENDIF
  309.  
  310.         jp link4        ;go see if there's more linking to do.
  311.  
  312.  
  313. linkd:  ld a,1
  314.         sta donef       ;all done. scan rest of command line
  315.         lda mflag       ;if it hasn't been scanned through
  316.         or a            ;already...
  317.         call nz,getfn
  318.         call reslv      ;resolve function references
  319.  
  320.         call getcdf2    ;get default external address
  321.         push hl         ;save in C.Ccall c,for use by "codend()" function
  322.         lhld cda
  323.         ld de,codend
  324.         add hl,de
  325.         pop de
  326.  
  327.         lda segf        ;if segment, don't write into code!
  328.         or a
  329.         jp nz,linkd0
  330.  
  331.         ld (hl),e
  332.         inc hl
  333.         ld (hl),d
  334.  
  335. linkd0: ex de,hl
  336.  
  337.         lda eflag       ;-e option specified?
  338.         or a
  339.         jp z,linkd1
  340.         lhld eaddr      ;yes. get the value given in -e option instead
  341. linkd1: push hl         ;save as external starting address in C.CCC
  342.         lhld cda        ; for run-time use.
  343.         ld de,extrns
  344.         add hl,de
  345.         pop de
  346.  
  347.         lda segf        ;if segment, don't write into code!
  348.         or a
  349.         jp nz,lnkd1a
  350.  
  351.         ld (hl),e
  352.         inc hl
  353.         ld (hl),d
  354.  
  355. lnkd1a: ex de,hl
  356.  
  357.         shld extadd
  358.         ex de,hl
  359.         lhld smsf       ;get size of external area
  360.         add hl,de               ;add to start of external area
  361.         push hl         ;and store where it'll be available to the
  362.         lhld cda        ;endext library routine
  363.         ld de,freram
  364.         add hl,de
  365.         pop de
  366.  
  367.         lda segf        ;if segment, don't write into code!
  368.         or a
  369.         jp nz,lnkd1b
  370.  
  371.         ld (hl),e
  372.         inc hl
  373.         ld (hl),d
  374.  
  375. lnkd1b:
  376.         lda zflag       ;-z option given?
  377.         or a
  378.         jp z,skip1
  379.  
  380.         lhld cda        ;if so, disable the external clearing subroutine
  381.         ld de,clrex     ;by changing the first byte of the appropriate
  382.         add hl,de               ;jump vector from C3 into C9.
  383.         ld (hl),0c9h
  384.  
  385. skip1:
  386.         lda segf        ;if segment, don't do rest of this kludgery
  387.         or a
  388.         jp nz,linkf
  389.  
  390.         lhld cda        ;get ptr to startup code in HL
  391.  
  392.         IF ZSYS
  393.         ld de,lxiloc
  394.         add hl,de              
  395.         ENDIF
  396.        
  397.         lda taddrf      ; -t option given?     
  398.         or a
  399.         jp nz,linke
  400.  
  401.         if 1==0
  402.        
  403.         lda nflag       ;noboot option given?
  404.         or a
  405.         jp z,linkd2     ;if not, go handle normally
  406.  
  407. ; Configure for NOBOOT:
  408.  
  409.         IF ZSYS
  410.         push hl
  411.         lhld    cda
  412.         ld de,noboot
  413.         add hl,de
  414.         ld (hl),1               ;set noboot flag in runtime
  415.         pop hl
  416.         jp linkf
  417.         ENDIF
  418.  
  419.         IF NOT ZSYS
  420.         ld (hl),0c3h
  421.         inc hl
  422.         ld (hl),(snobsp and 255)        ;low byte of snobsp address
  423.         inc hl
  424.         ld (hl),(snobsp / 255)  ;high byte of snobsp
  425.  
  426.         ld de,8         ;point HL at nobret jump op
  427.         add hl,de
  428.         ld (hl),(nobret and 255)        ;low byte of nobret address
  429.         inc hl
  430.         ld (hl),(nobret / 255)  ;high byte of nobret address
  431.  
  432.         lhld curtop             ;get top of available memory
  433.         shld taddr
  434.         jp linkf
  435.         ENDIF
  436.  
  437. linkd2:                 ;no -n given.
  438.         endif
  439.  
  440.         ;ld (hl),2ah    ;no. generate initial "lhld ram+6, ld sp,hl"
  441.         ;inc hl
  442.  
  443.         ;ld de,bdosp    ;at run time, ram+6 points to base of bdos
  444.         ;ld (hl),e
  445.         ;inc hl
  446.         ;ld (hl),d
  447.         ;inc hl
  448.         ;ld (hl),0f9h   ; ld sp,hl to set SP to top of TPA
  449.         jp linkf
  450.  
  451. linke:
  452.         ex de,hl                ;save code loc in DE
  453.         lhld taddr      ;if -t option given, leave the "ld sp, stack"
  454.         ex de,hl                ; instruction there, and insert given value.
  455.  
  456.         inc hl          ;pass the ld sp op
  457.  
  458.         ld (hl),e               ;store value
  459.         inc hl
  460.         ld (hl),d
  461.  
  462. linkf:
  463.         IF ZSYS         ;handle setup of Z3 Header:
  464.         lhld    runsat
  465.         ex de,hl
  466.         ld hl,tpa
  467.         call cmphd      ;standard load address?
  468.         jp z,linkf2     ;if so, no problem
  469.  
  470.         push de         ;save runsat address
  471.  
  472.         lhld cda        ;make type 3 header
  473.         ld (hl),0c7h    ;RST 0, so won't crash vandlla CP/M
  474.  
  475.         ld de,8         ;offset to Z type
  476.         add hl,de
  477.         ld (hl),3               ;make type 3
  478.  
  479.         inc hl
  480.         inc hl
  481.         inc hl          ;point to load addr
  482.  
  483.         pop de          ;get back runsat address
  484.         ld (hl),e               ;store runsat address in header
  485.         inc hl
  486.         ld (hl),d
  487.         ENDIF
  488.  
  489. linkf2:
  490.         call pstat      ;print out stats
  491.  
  492.         ld de,stgok     ;print completion message
  493.         call pstg       ;and free-ram diagnostic
  494.  
  495.         ld hl,NEDOOSMEMTOP;lhld bdosp   ;protected memory highest available tpa addr
  496.         ld l,0
  497.  
  498.         ex de,hl
  499.         lhld cdp
  500.         call cmh
  501.         add hl,de
  502.         ld a,h
  503.         rra
  504.         rra
  505.         and 3fh
  506.         ld l,a
  507.         ld h,0
  508.         dec hl
  509.         call prhld
  510.         ld de,stglo
  511.         call pstg
  512.  
  513.         IF TESTING3     ;print out contents of fcbs
  514.         push af
  515.         call printf
  516.           db 'right before calling writf, fcbs is: ',0
  517.         push hl
  518.         ld hl,fcbs
  519.         call pfnamu
  520.         pop hl
  521.         call crlf
  522.         pop af
  523.         ENDIF
  524.  
  525.         IF MARC
  526.         call crlf
  527.         ld a,2
  528.         sta consfd      ;route write error messages to stderr
  529.         ENDIF
  530.  
  531.         call writf      ;write out file or execute directly
  532.  
  533.         IF CPM
  534. exit:   QUIT ;lda wboote        ;need to do warm boot?
  535.         ;or a
  536.         ;jp nz,ram              ;if so, go do it
  537.         ;lhld spsav     ;put saved CCP stack pointer in SP
  538.         ;ld sp,hl
  539.         ;lda ccpok      ;CCP still intact?
  540.         ;or a
  541.         ;ret nz         ;if so, return
  542.         ;lda oktort     ;ok to return despite ccpok being false?
  543.         ;or a
  544.         ;ret nz         ;if so, return
  545.         ;jp ram         ;otherwise warm-boot
  546.  
  547. subfile: db 1,'$$$     SUB',0,0,0,0
  548.         ENDIF
  549.  
  550.         IF MARC
  551.         ld hl,0         ;return code: A OK
  552. exit:   ld c,m$exit
  553.         jp msys
  554.  
  555. ferror: ld c,m$error
  556.         call msys
  557.         ld hl,-1
  558.         jp exit
  559.  
  560. putext: ld a,(hl)
  561.         or a
  562.         jp z,putxt2
  563.         inc hl
  564.         jp putext
  565.  
  566. putxt2: pop de          ;get pointer to extension to be put on
  567. putxt3: ld a,(de)
  568.         ld (hl),a
  569.         inc hl
  570.         inc de
  571.         or a
  572.         jp nz,putxt3    ;copy to (HL) until zero encountered
  573.         ex de,hl                ;then jump to byte after the zero byte to return
  574.         pchl
  575.         ENDIF
  576.  
  577.         if 1==0
  578. ;
  579. ; Set up ZCPR3 environment:
  580. ;
  581. zsetup: xor a
  582.         sta zenvf
  583.         inc a           ;ok to return, by default
  584.         sta oktort
  585.  
  586.         ld hl,errdum
  587.         shld errbyt
  588.  
  589.         lhld zenvad
  590.         push hl
  591.  
  592.         ld de,1bh
  593.         add hl,de
  594.         ld e,(hl)
  595.         inc hl
  596.         ld d,(hl)               ;get reflexive env addr from env
  597.         pop hl          ;original Z env value
  598.         call cmphd      ;save as reflexive address?
  599.         jp nz,setp2a    ;if not Z system, go handle
  600.  
  601.         push hl
  602.         ld de,22h       ;point to message buffer address
  603.         add hl,de
  604.         ld a,(hl)
  605.         inc hl
  606.         ld h,(hl)
  607.         ld l,a          ;HL -> message buffer
  608.         ld de,6         ;get address of error byte
  609.         add hl,de
  610.         shld errbyt     ;save the address
  611.         pop hl
  612.  
  613.         ld a,1
  614.         sta zenvf       ;Z system.
  615.         push hl         ;save env pointer
  616.         ld de,3fh       ;get potential ccp address
  617.         add hl,de
  618.         ld a,(hl)
  619.         inc hl
  620.         ld h,(hl)
  621.         ld l,a
  622.         shld ccpad
  623.  
  624.         pop hl          ;get env pointer
  625.         ld de,8         ;get type
  626.         add hl,de
  627.         ld a,(hl)
  628.         sta envtyp
  629.        
  630.         and 80h         ;b7 hi?
  631.         lhld ccpad
  632.         jp nz,setup3    ;if so, use ccpad as ccp address
  633. setp2a: lhld 0001h      ;else calculate the old way
  634.         ld de,-1603h
  635.         add hl,de
  636.  
  637. setup3: xor a           ;clear 'ccp volatile' flag
  638.         sta ccpok      
  639.         ex de,hl                ;put ccp address in DE
  640.         ld hl,NEDOOSMEMTOP;lhld bdosp
  641.         ld l,0          ;zero out low-order byte
  642.         call cmphd      ;set Cy if [BASE+6,7] < CCP
  643.         jp c,setup4     ;if BASE+6 < CCP, use BASE+6 as end of prot mem
  644.         ex de,hl                ;else use CCP as end of prot. mem.
  645.         ld a,1          ;and set ccp volatile flag
  646.         sta ccpok
  647.         xor a           ;NOT ok to return by default!
  648.         sta oktort      ;  (go by value of ccpok)
  649. setup4: shld curtop
  650.         ret
  651.  
  652. envtyp: ds 1
  653. zenvf:  ds 1    ;true if running under ZCPR3
  654. zenvad: ds 2
  655. ccpad:  ds 2    ;address of CCP for type 80h ZCPR3
  656.         endif
  657. oktort: ds 1    ;ok to return despite ccpok being false?
  658. errbyt: dw errdum       ;address of Z3 error byte
  659. errdum: ds 1    ;if CP/M, errbyt points here
  660.  
  661.         IF CPM
  662. fcbt:   db 0
  663.         db '        '
  664.         db '   '
  665.         db 0,0,0,0
  666.         ENDIF
  667.  
  668.         IF CPM
  669. fcbs:   db 0            ;name of output file buffer
  670.         db '        '   ;8 filename spots
  671.         db 'COM'
  672.         db 0,0,0,0
  673.         ENDIF
  674.  
  675.  
  676.         IF NOT ALPHA AND NOT ZSYS
  677. stg0:   db 'BD Software C Linker   v'
  678.         ENDIF
  679.        
  680.         IF NOT ALPHA AND ZSYS
  681. stg0:   db 'BD Software C Linker (for ZCPR3)  vZ'
  682.         ENDIF
  683.  
  684.         IF ALPHA
  685. stg0:   db 'BDS Alpha-C Linker   v'
  686.         ENDIF
  687.  
  688.         IF NOT ZSYS
  689.         db '1.'
  690.         ENDIF
  691.  
  692.         db version
  693.  
  694.         IF ZSYS
  695.         db '.'
  696.         ENDIF
  697.  
  698.         db update
  699.  
  700.         IF PREREL
  701.         db ' (pre-release)'
  702.         ENDIF
  703.  
  704.         IF CPM
  705.         db cr,lf,0
  706.         ENDIF
  707.  
  708.         IF MARC
  709.         db '  for MARC',lf,0
  710.         ENDIF
  711.  
  712. stg2:   db 'Error reading: ',0
  713.  
  714.         IF CPM
  715. stgnua: db 'No user area prefix allowed on main filename',0
  716. stg5:   db lf,'Dir full; ',cr
  717.         ENDIF
  718.  
  719.         IF NOT CPM
  720. stg5:   db lf,'Error creating output file: ',0
  721.         ENDIF
  722.  
  723. stg3:   db lf,'Error writing: ',0
  724. stg4:   db lf,'Can''t close: ',0
  725. stg6:   db 'No main function in ',0
  726. stgas:  db cr,lf,'Missing function(s):',cr,lf,0
  727. stg7:   db lf,'Type the name of a CRL file '
  728.         db 'to scan; ',cr,lf
  729.  
  730.         IF CPM
  731.         db '<CR> to scan all DEFF files, '
  732.         ENDIF
  733.  
  734.         IF NOT CPM
  735.         db '<CR> to scan all deff?.crl files, '
  736.         ENDIF
  737.  
  738.         IF CPM
  739.         db '^Z-<CR> to abort: ',0
  740.         ENDIF
  741.  
  742.         IF NOT CPM
  743.         db 'or your ''quit'' char to abort: ',0
  744.         ENDIF
  745.  
  746. stgm:   db 9dh
  747. stgmn:  db 'MAI','N'+80h,0
  748.  
  749.         IF CPM
  750. deffs:  db 0,'DEFF    CRL',0
  751. deff2s: db 0,'DEFF2   CRL',0
  752. deff3s: db 0,'DEFF3   CRL',0
  753. ccc:    db 0,'C       CCC',0
  754.         ENDIF
  755.  
  756.         IF NOT CPM
  757. deffs:  db '/libC/deff0.crl',0
  758. ccc:    db '/libC/c.ccc',0
  759.         ENDIF
  760.  
  761. stg1:   db 'Can''t find ',0
  762. stglca: db 'Last code address: ',0
  763. stgext: db cr,lf,'Externals start at ',0
  764. stgxt2: db ' occupy ',0
  765. stgxt3: db 'bytes, last byte at ',0
  766. stgtm:  db cr,lf, 'Top of memory: ',0
  767. stgds:  db cr,lf, 'Stack space: ',0
  768. stgabt: db bell,'Link ABORTED',cr,lf,0
  769.  
  770.  
  771.         IF CPM
  772. stgdo:  db bell,cr,lf,'Warning! Externals extend into the BDOS!',cr,lf,0
  773.         ENDIF
  774.  
  775.         IF MARC
  776. stgdo:  db lf,'Warning! Externals extend into system memory!',lf,0
  777.         ENDIF
  778.  
  779.         IF CPM
  780. stgdo2: db bell,lf,'Warning! Externals overlap code!',cr,lf,0
  781.         ENDIF
  782.  
  783.         IF MARC
  784. stgdo2: db lf,'Warning! Externals overlap code!',lf,0
  785.         ENDIF
  786.  
  787. stgom:  db 'Out of memory',0
  788. stglo:  db 'K link space remaining',cr,0
  789. stgok:  db 'Writing output...',cr,lf,0
  790. stgserr: db 'Bad symbols',0
  791. stgdeb: db lf,'Executing:',cr,lf,0
  792. stglde: db 'Load Address does not match Z3 Header in Runtime Pkg',cr,lf,0
  793. stgbop: db 'Bad option: -',0
  794. stgt1o: db 'Ref table overflow',0
  795. stgver: db 'Missing arg to -'
  796. stgdps: db 'Symbol re-defined: ',0
  797. stgdpf: db 'Ignoring duplicate function: ',0
  798. stgabo: db '^C',cr,lf,0
  799.  
  800.         IF MARC
  801. stgnof: db 'Usage:  '
  802.  
  803.         db 'clink <main_crl_file> [-cmsvw]'
  804.         db ' [-e <addr>] [-t <addr>] [-l <addr>]'
  805.         db lf
  806.  
  807.         db tab,'[-o <new_name>] [-y <sym_name>]'
  808.         db ' [<crl_file> [<crl_file>] ... ]'
  809.         db 0
  810.         ENDIF
  811.  
  812. opletr: ds 1
  813.         db 0
  814.  
  815. stgtmf: db 'Sorry; 255 funcs max',0
  816.  
  817.  
  818. ;
  819. ; Initialize linker:
  820. ;
  821.  
  822. linit:  ;ld c,sguser    ;get current user area
  823.         ;ld e,0ffh
  824.         ;lda nouser
  825.         ;or a
  826.  
  827.         ;IF NOT ALPHA
  828.         ;call z,bdos
  829.         ;ENDIF
  830.  
  831.         ;IF ALPHA
  832.         ;nop
  833.         ;nop
  834.         ;nop
  835.         ;ENDIF
  836.  
  837.         ;sta curusr     ;save current user area number
  838.         lda defsub
  839.         inc a
  840.         sta subfile
  841.  
  842.         xor a
  843.         sta wrsmf       ; "outch" not to write to SYM file
  844.  
  845.         IF MARC         ;under marc, initialize message output fd
  846.         ld a,1
  847.         sta consfd      ;route first msgs to stdout (1)
  848.         ld hl,0
  849.         shld noffst     ;search /libC by default for deff*.crl & c.ccc
  850.  
  851.         ld c,m$memory   ;get top of memory value before doing maxmem call
  852.         call msys
  853.         ex de,hl
  854.         shld taddr      ;taddr before maxmem call
  855.  
  856.         ld c,m$maxmem   ;do a maxmem call
  857.         call msys
  858.         ld a,1
  859.         sta maxmd       ;note that we've done the maxmem call (once only)
  860.  
  861.         ld c,m$memory   ;get top of memory AFTER maxmem call has been done
  862.         call msys
  863.         ex de,hl
  864.         shld curtop     ;and as physical top of memory for linkage
  865.         ENDIF
  866.  
  867.         IF MARC         ;if MARC version, get argc and argv:
  868.         sta marcfd      ;clear marcfd in case of usage diagnostic
  869.         pop af          ;skip return address
  870.  
  871.         pop hl          ;get argc passed by MARC
  872.         ld a,l          ;set A = argc
  873.         dec a
  874.         ld de,stgnof    ; complain and abort if no filename given
  875.         jp z,stgab
  876.  
  877.         sta argc        ;else save argc for later use
  878.  
  879.         pop hl          ;get argv passed by MARC
  880.         inc hl
  881.         inc hl          ;point argv to main filename pointer
  882.         shld argv       ;save pointer to main filename pointer
  883.  
  884.         ld a,(hl)               ;indirect to get pointer to main filename
  885.         inc hl
  886.         ld h,(hl)
  887.         ld l,a
  888.  
  889.         push hl         ;save it on stack
  890.         ld de,fcbt      ;copy main filename to fcbt for use in reading
  891.         call ldfn       ;   in main crl file
  892.         pop hl          ;and also copy to fcbs for use in writing out file
  893.         ld de,fcbs
  894.         call ldfn
  895.         ENDIF
  896.  
  897.         IF TESTING3     ;print out contents of fcbs
  898.         push af
  899.         call printf
  900.           db 'after first setting fcbs, fcbs is: ',0
  901.         push hl
  902.         ld hl,fcbs
  903.         call pfnamu
  904.         pop hl
  905.         call crlf
  906.         pop af
  907.         ENDIF
  908.  
  909.  
  910.         IF CPM
  911.         ld de,tbuff     ;set DMA address
  912.         ld c,sdma       ;(helps me debug 4200h version
  913.         call bdos       ; on 0-based CP/M system)
  914.  
  915.         ld hl,fcb       ;save filename argument
  916.         ld de,fcbs
  917.         call ldfn       ;lde main filename to fcbs
  918.         ld hl,fcbs+9    ;and give it a COM extension by default
  919.         ld (hl),'C'
  920.         inc hl
  921.         ld (hl),'O'
  922.         inc hl
  923.         ld (hl),'M'
  924.         ENDIF
  925.  
  926.         xor a           ;clear some flags:
  927.         sta segf        ;assume not a segment by default
  928.         sta debugf      ;don't immediately execute file
  929.         sta statf       ;default to no stats
  930.         sta dflag       ;haven't searched DEFF.CRL yet
  931.         sta quietf      ;report unfound files
  932.         sta rdngsm      ;not reading in symbols from disk
  933.         sta taddrf      ;no top of memory address given yet
  934.         sta eflag       ;no external data addr given yet
  935.         sta donef       ;all function references not resolved yet
  936.         sta wflg        ;controls writing of SYM file
  937.  
  938.         IF CPM
  939.         sta zflag       ;don't inhibit clearing of externals
  940.         sta d2flag      ;haven't searched DEFF2.CRL either
  941.         sta d3flag      ;haven't searched DEFF3.CRL either
  942.         sta nflag       ;no noboot option given yet
  943.         sta hflag       ;don't force lhld 4206 unless -h given
  944.         sta savdrv      ;no drive number currently saved
  945.         ENDIF
  946.  
  947.         IF MARC
  948.         sta dlast       ;last DEFF?.CRL searched (0 for none)
  949.         sta marcfd      ;no file open yet
  950.         sta iflag       ;no image mode output file unless -i used
  951.         sta oflag       ;haven't seen -o yet
  952. ;       sta maxmd       ;haven't done maxmem call yet
  953.         ENDIF
  954.  
  955.         IF NOT FORCE
  956.         sta fflag       ;no forced loading of all funcs in crl file
  957.         ENDIF
  958.  
  959.         inc a
  960.         sta mflag       ;more text to process in command line
  961.  
  962.         IF FORCE
  963.         sta fflag       ;force loading of all funcs in crl file
  964.         ENDIF
  965.  
  966.         IF CPM
  967.         lda fcb         ;default disk to get CRL files from
  968.         sta decl        ; is disk where source file came from
  969.  
  970.         lhld curtop     ;set protected memory address
  971.         shld taddr
  972.         ENDIF  
  973.  
  974.         ld hl,dt1siz    ;set default tab1 size
  975.         shld tb1siz
  976.  
  977.         ld hl,tpa       ;default starting address
  978.         shld runsat     ; (alterable only if -l option used)
  979.         ret
  980.  
  981.  
  982. ;
  983. ; Process immediate command line options:
  984. ;
  985.  
  986. copycline
  987.         IF CPM
  988.         ;ld hl,fcb+1    ;see if user area given as part of filename
  989.         ;call gdec
  990.         ;jp c,clink0    ;if not, OK
  991.         ;ld a,(hl)              ;else, followed by slash?
  992.         ;cp '/'
  993.         ;jp nz,clink0   ;if not, no problem
  994.         ;ld de,stgnua   ;else abort with "no user number allowed" message
  995.         ;jp stgab
  996.  
  997. clink0: ld hl,cline     ;copy command line from tbuff to area at cline
  998.         shld cptr       ;under CP/M
  999.         ld hl,tbuff
  1000. comlin_fspac:
  1001.         ld a,(hl)
  1002.         or a
  1003.         jr z,comlin_fspac_skip
  1004.         inc hl
  1005.         cp ' '
  1006.         jr nz,comlin_fspac
  1007. comlin_fspac_skip
  1008.        
  1009.         ;ld b,(hl)
  1010.         ;inc b
  1011.         ;inc hl
  1012.         ld de,cline
  1013. c0:     ;dec b
  1014.         ;jp z,c0a
  1015.         ld a,(hl)
  1016.         ld (de),a
  1017.         or a
  1018.         inc hl
  1019.         inc de
  1020.         jr nz,c0
  1021.         ENDIF
  1022.         IF NOT CPM
  1023.         ld a,2
  1024.         sta consfd      ;future msgs to go stderr
  1025.  
  1026.         ld hl,cline     ;copy all MARC args into area at cline, emulating
  1027.         shld cptr       ;CP/M command line passage
  1028.         ex de,hl                ;DE is destination area
  1029.         lhld argv       ;HL will point to arg text
  1030. c00a:   lda argc        ;get arg count
  1031.         or a
  1032.         jp z,c0a                ;if done, go process the line
  1033.         dec a
  1034.         sta argc        ;not done. debump argc and copy current arg text
  1035.         push hl         ;save pointer to text address
  1036.         ld a,(hl)
  1037.         inc hl
  1038.         ld h,(hl)
  1039.         ld l,a          ;HL points to actual arg text
  1040.         call ldfn       ;lde it to (DE)
  1041.         dec de          ;stomp on the trailing null from the last arg text
  1042.         ld a,' '        ; (replace it with a space)
  1043.         ld (de),a
  1044.         inc de
  1045.         pop hl          ;restore arg pointer
  1046.         inc hl          ;bump to next pointer
  1047.         inc hl
  1048.         jp c00a ;and go for it
  1049.         ENDIF  
  1050.  
  1051. c0a:    xor a           ;OK, command line has been copied
  1052.         ld (de),a               ;terminate with null byte
  1053.         ret
  1054.  
  1055. comlin:
  1056.  
  1057.         ld hl,cline     ;find first non-space char
  1058.         call igsp
  1059.         or a            ;if end of line,
  1060.         jp z,c1         ; don't pass over main filename
  1061. c0c:    inc hl          ;else do.
  1062.         ld a,(hl)
  1063.         or a
  1064.         jp z,c1
  1065.         call twhite
  1066.         jp nz,c0c
  1067.         call igsp       ;pass space between this and next arg
  1068.  
  1069. c1:     shld cptr       ;save text pointer for getfn routine
  1070.  
  1071. ;
  1072. ; Here process -l and -r and -v; set tb1siz to optional arg of -r,
  1073. ; set runsat to optional arg of -l, and set segf if -v given:
  1074. ; also handle new -c option for MARC
  1075. ; Also handle new -m option to call MEXMEM under MARC...
  1076. ;
  1077.         dec hl
  1078. prep0:  inc hl
  1079. prep:   ld a,(hl)
  1080.         or a            ;done preprocessing command line?
  1081.         ret z           ;if so, return
  1082.  
  1083.         cp '"'          ;possible option to "-d"?
  1084.         jp nz,prep3    
  1085. prep2:  inc hl          ;if so, skip it totally
  1086.         ld a,(hl)
  1087.         or a
  1088.         ret z           ;if null byte, done scanning command line
  1089.         cp '"'
  1090.         jp nz,prep2
  1091.         jp prep0        ;then continue scanning for useful options
  1092.  
  1093. prep3:  cp '-'          ;no. dashed option?
  1094.         jp nz,prep0     ;if not, ignore it
  1095.         shld fargb      ;else save first arg byte
  1096.         inc hl
  1097.         ld a,(hl)               ;get option letter
  1098.         call mapuc
  1099.         sta opletr      ;save for error msg
  1100.         inc hl
  1101.  
  1102.         cp 'C'          ;set disk to get c.ccc and deff*.crl from
  1103.         jp z,copt
  1104.  
  1105.         IF CPM
  1106.         cp 'H'          ;little kludge (for Leo only)
  1107.         jp z,hopt
  1108.         ENDIF
  1109.  
  1110.         IF MARC
  1111.         cp 'I'          ;image mode?
  1112.         jp z,iopt
  1113.         ENDIF
  1114.  
  1115.         cp 'L'          ;set load addr?
  1116.         jp z,lopt
  1117.         cp 'R'          ;set ref table size?
  1118.         jp z,ropt
  1119.         dec hl
  1120.         cp 'V'          ;declare a segment?
  1121.         jp nz,prep
  1122.         inc hl
  1123.         sta segf        ;this one's easy.
  1124. purge:  ex de,hl                ;put current text addr in DE
  1125. purge1: lhld fargb      ;get 1st byte of arg
  1126. purge2: ld (hl),' '     ;clear it to a space
  1127.         inc hl
  1128.         call cmphd      ;done?
  1129.         jp nz,purge2    ;keep blanking till done.
  1130.         jp prep ;and go pre-process some more
  1131.  
  1132. ropt:   call gtaddd     ;get argument
  1133.         ex de,hl
  1134.         ld a,h
  1135.         or a
  1136.         jp z,purge1     ;must be at least 100h to be reasonable!
  1137.         shld tb1siz     ;set tab1 size;
  1138.         jp purge1       ;and go purge text from command line
  1139.  
  1140. copt:
  1141.         IF CPM
  1142.         call igsp
  1143.         call cnvtd      ;get source disk for C.CCC and DEFF*.CRL
  1144.         sta defdsk      ;(under CP/M only; MARC version assumes
  1145.                         ;all this stuff is in /etc)
  1146.         inc hl          ;bump text pointer to possible user area
  1147.         call gdec       ;user area given?
  1148.         jp c,copt9      ;if not, done with -c option
  1149.         sta defusr      ;else user area given. Store it at defusr
  1150.        
  1151. copt9:  ex de,hl
  1152.         jp purge1
  1153.  
  1154. ;
  1155. ; Scan text at HL for a decimal number. If found, return in B and clear Cy.
  1156. ; Return Cy set if no legal decimal number.
  1157. ; Upon exit, HL is left pointing to the first non-decimal-digit character.
  1158. ;
  1159.  
  1160. gdec:   ld b,0
  1161.         call igsp
  1162.         call legdd
  1163.         ld a,0
  1164.         ret c  
  1165. gdec1:  ld a,(hl)
  1166.         call legdd
  1167.         ld a,b
  1168.         ccf
  1169.         ret nc
  1170.         add a           ;get A = 10 * B
  1171.         add a           ;(*4)
  1172.         add a           ;(*8)
  1173.         add b           ;(*9)
  1174.         add b           ;(*10)
  1175.         add c           ;now add new digit value in C
  1176.         ld b,a          ;put into B
  1177.         inc hl
  1178.         jp gdec1        ;and go for more
  1179.  
  1180. ;
  1181. ; Check for legal decimal digit, return Cy set if illegal,
  1182. ; else the binary value of the digit in A:
  1183. ;
  1184.  
  1185. legdd:  call mapuc
  1186.         sub '0'
  1187.         ld c,a
  1188.         ret c
  1189.         cp 10
  1190.         ccf    
  1191.         ret
  1192.         ENDIF
  1193.  
  1194.         IF NOT CPM
  1195.         ex de,hl                ;put text pointer in DE
  1196.         ld hl,6         ;set noffst to 6 so current directory is
  1197.         shld noffst     ;searched for deff*.crl & c.ccc instead of /libC
  1198.         jp purge1
  1199.         ENDIF
  1200.  
  1201.  
  1202.         IF CPM
  1203. hopt:   sta hflag       ;kludge for Leo, under CP/M only
  1204.         ex de,hl
  1205.         jp purge1
  1206.         ENDIF
  1207.  
  1208.         IF MARC
  1209. iopt:   sta iflag       ;set image mode to produce absolute image
  1210.         ex de,hl
  1211.         jp purge1
  1212.         ENDIF
  1213.  
  1214. lopt:   call gtaddd     ;get argument
  1215.         ex de,hl
  1216.         shld runsat     ;set origin address
  1217.         jp purge1       ;and purge from command line   
  1218.  
  1219.  
  1220.  
  1221.  
  1222. ;
  1223. ; Check for user abortion:
  1224. ;TODO
  1225.  
  1226. ckabrt: push af
  1227.         push hl
  1228.         push de
  1229.         push bc
  1230.  
  1231.         IF CPM
  1232.         ;lda conpol     ;are we polling for interrupts?
  1233.         ;or a
  1234.         ;jp z,noabrt    ;if not, ignore console...
  1235.  
  1236.         ;ld c,intcon    ;interrogate console status
  1237.         ;call bdos
  1238.         ;or a
  1239.         ;jp z,noabrt
  1240.         ;ld c,coninp
  1241.         ;call bdos
  1242.         ;cp 3           ;control-C aborts
  1243.         ;jp nz,noabrt
  1244.  
  1245.         ;ld de,stgabo   ;abort.
  1246.         ;jp stgab
  1247.         ENDIF
  1248.  
  1249.         IF NOT CPM
  1250.         ;ld c,m$ichec
  1251.         ;call msys
  1252.         ENDIF
  1253.  
  1254. noabrt: pop bc
  1255.         pop de
  1256.         pop hl
  1257.         pop af
  1258.         ret
  1259.  
  1260.  
  1261. ;
  1262. ; Here is a little routine to take an ASCII char in A that
  1263. ; is either a space or a disk letter, and return the
  1264. ; appropriate code in A to put in the first byte of an
  1265. ; fcb accessing the given disk:
  1266. ;
  1267.  
  1268.         IF CPM
  1269. cnvtd:  call twhite
  1270.         jp nz,cnvtd2
  1271.         xor a           ;if space, return 0
  1272.         ret
  1273.  
  1274. cnvtd2: sub 'A'         ;else return 0 for A, 2 for B, etc.
  1275.         cp 'Z'+1
  1276.         ret c           ;OK if a letter
  1277.         xor a           ;else change to zero
  1278.         ret
  1279.         ENDIF
  1280.  
  1281. ;
  1282. ; Given the address of a function as stored in memory during
  1283. ; the linkage process, this function returns the actual address
  1284. ; that the function will occupy when executing:
  1285. ;
  1286.  
  1287. getcdf: lhld runsat
  1288.         push de
  1289.         ex de,hl
  1290.         lhld cda
  1291.         call cmh
  1292.         add hl,de
  1293.         pop de
  1294.         ret
  1295.  
  1296. getcdf2: call getcdf
  1297.         ex de,hl
  1298.         lhld cdp
  1299.         add hl,de
  1300.         ret
  1301.  
  1302.  
  1303. ;
  1304. ; Print out the contents of register pair HL in ASCII:
  1305. ;  (uses the simple gas-pump algorithm)
  1306. ;
  1307.  
  1308. prhld:  push de
  1309.         push hl
  1310.         ld hl,'  '
  1311.         shld ascb
  1312.         ld hl,' 0'
  1313.         shld ascb+2
  1314.         pop hl
  1315.         inc hl
  1316. prh0:   ld a,h
  1317.         or l
  1318.         jp z,prnt
  1319.         dec hl
  1320.         push hl
  1321.         ld hl,ascb+3
  1322. prh1:   ld a,(hl)
  1323.         call twhite
  1324.         jp nz,prh2
  1325.         ld a,'0'
  1326. prh2:   inc a
  1327.         cp '9'+1
  1328.         jp z,prh4
  1329. prh3:   ld (hl),a
  1330.         pop hl
  1331.         jp prh0
  1332. prh4:   ld (hl),'0'
  1333.         dec hl
  1334.         jp prh1
  1335. prnt:   ld de,ascb
  1336.         call pstg
  1337.         pop de
  1338.         ret
  1339.  
  1340. ;
  1341. ; Print out file name in the fcb, with user number preceding:
  1342. ;
  1343. pfnamu:
  1344.         lda nouser
  1345.         or a
  1346.         jp nz,pfnam     ;if user areas disabled, don't even try
  1347.         ld e,0ffh
  1348.         ld c,32
  1349.         call bdos       ;get current user number
  1350.         call prad       ;print out value of A in decimal
  1351.         ld a,'/'
  1352.         call outch      ;follow with slash, then print rest of filename
  1353.  
  1354. ;
  1355. ; Routine to print out the name of the file
  1356. ; in the fcb:
  1357. ;
  1358.        
  1359.         IF CPM
  1360. pfnam:
  1361.         push de
  1362.         push bc
  1363.         lda fcb
  1364.         or a            ;if no special disk, don't print a letter
  1365.         jp nz,pfnam1
  1366.         ;push de
  1367.         ;push hl
  1368.         ;ld c,gdisk
  1369.         ;call bdos
  1370.         ;inc a 
  1371.         ;pop hl
  1372.         ;pop de
  1373.          xor a ;TODO getpath
  1374. pfnam1: add a,'@'               ;make it into the disk letter
  1375.         call outch
  1376.         ld a,':'
  1377.         call outch
  1378. pfnam2: ld b,8
  1379.         ld de,fcb+1
  1380.         call pnseg      ;print filename
  1381.         ld a,'.'
  1382.         call outch
  1383.         ld b,3
  1384.         call pnseg      ;print extension
  1385.         call crlf
  1386.         pop bc
  1387.         pop de
  1388.         ret
  1389.  
  1390.  
  1391. ;
  1392. ; Print out user number in A in decimal:
  1393. ;
  1394.  
  1395. prad:   push bc
  1396.         call prad2
  1397.         pop bc
  1398.         ret
  1399.  
  1400. prad2:  ld c,0          ;compute first digit
  1401. prad3:  cp 10           ;less than 10?
  1402.         jp c,prfd               ;if so, print first digit
  1403.         sub 10          ;else subtract 10
  1404.         inc c           ;bump tens digit
  1405.         jp prad3
  1406.        
  1407. prfd:   push af ;save last digit
  1408.         ld a,c          ;look at first digit
  1409.         or a            ;zero?
  1410.         call nz,prhd    ;if not, print it
  1411.         pop af          ;get last digit
  1412.         jp prhd ;print it
  1413.  
  1414. ;
  1415. ; print out string at DE, max length B,
  1416. ; delete spaces:
  1417. ;
  1418.  
  1419. pnseg:  ld a,(de)
  1420. pns2:   call outch
  1421.         inc de
  1422.         dec b
  1423.         ret z
  1424.         ld a,(de)
  1425.         call twhite
  1426.         jp nz,pns2
  1427. pns3:   inc de
  1428.         dec b
  1429.         jp nz,pns3
  1430.         ret
  1431.         ENDIF
  1432.  
  1433. ;
  1434. ; Print out statistics in response to -s option:
  1435. ;
  1436.  
  1437. pstat:  call alphab     ;alphabetize symbols
  1438.  
  1439. ;       lda statf       ;if printing stats, put out a crlf
  1440. ;       or a
  1441. ;       call nz,crlf
  1442.  
  1443. ;       call crlf
  1444.  
  1445.         call wrsmb      ;write symbols to disk and console
  1446.  
  1447. ;       lda statf       ;if stats desired,
  1448. ;       or a            ;print them out
  1449. ;       jp nz,pst00
  1450. ;
  1451. ;       call ovrlap     ;check for code/externals overlap
  1452. ;
  1453. ;       lhld smsf       ;and check for external area overflow into system
  1454. ;       ex de,hl
  1455. ;       lhld extadd
  1456. ;       add hl,de
  1457. ;       jp c,exovfl     ;if carry, REAL overflow!
  1458. ;       ex de,hl                ;DE holds end of external area
  1459. ;       lhld taddr      ;get end of TPA address
  1460. ;       call cmphd      ;HL better be less than DE....
  1461. ;       ret nc          ;if so, no problem
  1462. ;exovfl:
  1463. ;       ld de,stgdo     ;else trouble!
  1464. ;       call pstg
  1465. ;       ret
  1466.  
  1467. pst00:  ld de,stglca    ; "last code address = ..."
  1468.         call crlf
  1469.         call pstg
  1470.         call getcdf2    ;get last code addr. + 1
  1471.         dec hl
  1472.         call prhls      ;print last code address out
  1473.  
  1474. ;
  1475. ; Print external data stats, but DON'T if linking a segment
  1476. ; and no -e option is given:
  1477. ;
  1478.  
  1479.         lda segf
  1480.         or a
  1481.         jp z,pst00a     ;if not linking a segment, print all stats
  1482.         lda eflag       ;yes, linking a segment. -e option given?
  1483.         or a
  1484.         jp z,pst0               ;if not, skip printing external numbers
  1485.  
  1486. pst00a: ld de,stgext    ;"external data starts at "
  1487.         call pstg
  1488.         lhld extadd
  1489.         push hl
  1490.         call prhlc
  1491.         ld de,stgxt2    ;"occupy "
  1492.         call pstg
  1493.         lhld smsf
  1494.         push hl         ;save size of external data area
  1495.         call prhls
  1496.         ld de,stgxt3    ;" bytes, and and end at"
  1497.         call pstg
  1498.         pop de          ;get size of externals
  1499.         pop hl          ;get start of externals
  1500.         add hl,de               ;get ending address of externals
  1501.         dec hl
  1502.         push hl         ;save for local store calc later
  1503.         ld a,d          ;special case of no externals
  1504.         or e
  1505.         jp nz,skpfdg
  1506.         inc hl
  1507. skpfdg: call prhls
  1508.         pop hl
  1509.  
  1510. pst0:   push hl
  1511.         ld de,stgtm     ;"top of memory=..."
  1512.         call pstg
  1513.         lhld taddr
  1514.         dec hl
  1515.         push hl         ;save top of memory address
  1516.         call prhls
  1517.  
  1518.         lda segf
  1519.         or a
  1520.         jp z,pst1
  1521.  
  1522.         pop de
  1523.         pop de
  1524.         jp crlf
  1525.  
  1526. pst1:   ld de,stgds             ;print data & stack space
  1527.         call pstg               ; only if not a segment
  1528.         pop de                  ;get saved top of memory address in DE
  1529.         pop hl                  ;get external ending address in HL
  1530.         call cmphd              ;check for overflow
  1531.         push af         ;save Cy flag for msg later
  1532.         jp c,pst2                       ;externals end before top of memory?
  1533.         ld a,'-'                ;no-- print a negative sign
  1534.         call outch     
  1535.         ex de,hl                        ;exchange so it'll come out positive
  1536. pst2:   call cmh
  1537.         add hl,de
  1538.         call prhls              ;print out amount of stack space
  1539.         call crlf
  1540.         pop af                  ;check for overflow condition
  1541.         ld de,stgdo             ;data overflow message
  1542.         call nc,pstg
  1543.  
  1544. ovrlap: call getcdf2            ;check for externals/code overlap
  1545.         ex de,hl                        ;put end of code addr in DE
  1546.         lhld extadd             ;get external base address
  1547.         call cmphd              ;is external base < end of code + 1?
  1548.         ld de,stgdo2
  1549.         call c,pstg    
  1550.         ret
  1551.  
  1552. wrsmb:  ld a,4
  1553.         sta gotf        ;init symbols-per-line countdown
  1554.         sta wrsmf       ;and make outch write to disk as well as console
  1555.         lda wflg        ;       (to disk only if wflg is on)
  1556.         or a
  1557.         jp z,wrsm2      ;write symbols to disk?
  1558.  
  1559.         IF CPM
  1560.         ld hl,fcbs
  1561.         ld de,fcb
  1562.         call ldfn
  1563.         ld hl,fcb+9
  1564.         ld (hl),'S'
  1565.         inc hl
  1566.         ld (hl),'Y'
  1567.         inc hl
  1568.         ld (hl),'M'
  1569.         ENDIF
  1570.  
  1571.         IF NOT CPM
  1572.         ld hl,fcbs
  1573.         ld de,fcbt
  1574.         push de
  1575.         push de
  1576.         call ldfn
  1577.         pop hl
  1578.         call putext
  1579.            db '.sym',0
  1580.         pop hl
  1581.         ENDIF
  1582.  
  1583.         call crate2     ;if so, create symbols file
  1584.         ld hl,tbuff     ;init buffer pointer
  1585.         shld bufp
  1586. wrsm2:  lhld nsmbs      ;set BC = countdown of # of symbols
  1587.         ld b,h
  1588.         ld c,l
  1589.         ld de,0         ;while DE will go the other way
  1590. wrsm3:  call wsmb       ;write out a single symbol and address
  1591.         inc de          ;bump pointer to next symbol pointer
  1592.         inc de
  1593.         dec bc          ;de-bump countdown
  1594.         ld a,b          ;done?
  1595.         or c
  1596.         jp nz,wrsm3     ;if not, keep loopin'
  1597.  
  1598.         call crlf       ;done. put out trailing crlf
  1599.  
  1600.         IF CPM
  1601.         ld b,1ah        ;terminating control-Z for CP/M (cough cough)
  1602.         lda wflg        ;were we writing to a file?
  1603.         or a
  1604.         call nz,wrb3    ;(into file only)
  1605.         ENDIF
  1606.  
  1607.         xor a
  1608.         sta wrsmf       ;turn off kludge flag
  1609.         lda wflg
  1610.         or a            ;writing to disk?
  1611.         ret z           ;if not, all done
  1612.  
  1613.         IF CPM
  1614.         ld b,1ah        ;if so, pad file with control-Z's for CP/M
  1615.         lhld bufp
  1616.         ld a,l
  1617.         and 7fh        
  1618.         jp z,wrsm6
  1619. wrsm4:  ld a,l
  1620.         and 7fh
  1621.         jp z,wrsm5
  1622.         ld (hl),b
  1623.         inc hl
  1624.         jp wrsm4
  1625. wrsm5:  ld hl,tbuff
  1626.         call writs
  1627.         ENDIF
  1628.  
  1629.         IF NOT CPM      ;for MARC, just write out rest of the buffer
  1630.         lhld bufp
  1631.         ld a,l
  1632.         and 7fh
  1633.         jp z,wrsm6
  1634.         ld e,a          ;put byte count in DE
  1635.         ld d,0
  1636.         ld hl,tbuff
  1637.         call writs      ;write out remainder of symbol text buffer
  1638.         ENDIF
  1639.  
  1640. wrsm6:  call close
  1641.         ret
  1642.  
  1643. wsmb:   push de         ;write out symbol #DE and its value
  1644.         ld hl,tab2      ;first get value
  1645.         add hl,de
  1646.         ld a,(hl)
  1647.         inc hl
  1648.         ld h,(hl)
  1649.         ld l,a          ;HL = raw value
  1650.         push hl         ;add offset    
  1651.         call getcdf
  1652.         pop de
  1653.         add hl,de
  1654.         call prhls      ;print out value in hex
  1655.         ld hl,wsp       ;now get name
  1656.         pop de
  1657.         add hl,de
  1658.         ld a,(hl)
  1659.         inc hl
  1660.         ld h,(hl)
  1661.         ld l,a
  1662.         call pfnm       ;print out name
  1663.         lda gotf        ;see if we trail with tab or crlf
  1664.         dec a
  1665.         sta gotf
  1666.         jp z,wsmbcr
  1667.         ld a,9          ;tab.
  1668.         call outch
  1669.         ret
  1670. wsmbcr: call crlf       ;crlf
  1671.         ld a,4          ;reset line count
  1672.         sta gotf
  1673.         ret
  1674.  
  1675.        
  1676. ;
  1677. ; Alphabetize symbols for SYM file output and stat printout:
  1678. ;
  1679.  
  1680. alphab: call setwsp     ;set up swp with pointers to symbols in tab1
  1681.         ld bc,0         ;initalize outer loop variable
  1682. ab1:    lhld nsmbs      ;done with outer loop?
  1683.         dec hl
  1684.         call cmpbh
  1685.         ret nc          ;if so, all done sorting
  1686.         ld d,b          ;else initialize inner loop
  1687.         ld e,c
  1688.         inc de
  1689. ab2:    lhld nsmbs      ;done with inner iteration?
  1690.         dec hl
  1691.         call cmphd
  1692.         jp c,ab4                ;if so, iterate outer loop
  1693.         call cmptx      ;else compare two symbols
  1694.         jp nc,ab3               ;out of order?
  1695.         ld hl,wsp       ;yes. swap pointers.
  1696.         shld tbase     
  1697.         call swap
  1698.         ld hl,tab2
  1699.         shld tbase
  1700.         call swap
  1701. ab3:    inc de          ;bump inner loop variable
  1702.         jp ab2
  1703. ab4:    inc bc          ;bump outer loop variable
  1704.         jp ab1
  1705.  
  1706. cmpbh:  ld a,b          ;return C set if BC < HL
  1707.         cp h
  1708.         ret nz
  1709.         ld a,c
  1710.         cp l
  1711.         ret
  1712.  
  1713. cmphd:  ld a,h          ;return C set if HL < DE
  1714.         cp d
  1715.         ret nz
  1716.         ld a,l
  1717.         cp e
  1718.         ret
  1719.  
  1720. swap:   lhld tbase      ;switch the 16 bit values at tbase+BC
  1721.         add hl,bc               ;                       and  tbase+DE
  1722.         add hl,bc
  1723.         push bc         ;save BC
  1724.         push hl         ;save tbase+BC
  1725.         ld c,(hl)               ; BC := (tbase+BC)
  1726.         inc hl
  1727.         ld b,(hl)
  1728.         lhld tbase
  1729.         add hl,de
  1730.         add hl,de
  1731.         push de         ;save DE
  1732.         ld e,(hl)               ; DE := (tbase+DE)
  1733.         inc hl
  1734.         ld d,(hl)
  1735.         ld (hl),b               ; (tbase+DE) := (tbase+BC)
  1736.         dec hl
  1737.         ld (hl),c
  1738.         pop hl          ;get back original DE in HL
  1739.         ex de,hl                ;put into DE, and HL:=(tbase+DE)
  1740.         ld b,h          ; BC := (tbase+DE)
  1741.         ld c,l
  1742.         pop hl          ;get back tbase+BC
  1743.         ld (hl),c               ; tbase+BC := tbase+DE
  1744.         inc hl
  1745.         ld (hl),b
  1746.         pop bc          ;and restore original BC
  1747.         ret             ;oh well, so it isn't documented all that greatly!
  1748.  
  1749. setwsp: ld hl,tab1      ;initialize wsp with pointers to entries in tab1
  1750.         ld bc,0         ;symbol count
  1751.         ld de,wsp       ;destination of pointers
  1752. setw2:  ld a,(hl)               ;end of tab1?
  1753.         or a   
  1754.         jp nz,setw3
  1755.         ld h,b          ;yes. set nsmbs to # of symbols found
  1756.         ld l,c
  1757.         shld nsmbs
  1758.         ret             ;and return.
  1759.  
  1760. setw3:  push hl         ;not done. enter pointer into wsp...
  1761.         cp 9dh          ;"main" is a special code; fudge the pointer
  1762.         jp nz,setw4
  1763.         ld hl,stgmn     ;"MAIN'"
  1764.  
  1765. setw4:  ld a,l
  1766.         ld (de),a
  1767.         inc de
  1768.         ld a,h
  1769.         ld (de),a
  1770.         inc de
  1771.         inc bc          ;bump symbol count
  1772.         pop hl          ;restore pointer into tab1
  1773.  
  1774. setw5:  ld a,(hl)               ;pass over symbol text
  1775.         inc hl 
  1776.         or a
  1777.         jp p,setw5
  1778.  
  1779.         ld a,(hl)               ;and references (if any)
  1780.         and 7fh
  1781.         push de
  1782.         ld d,a
  1783.         inc hl
  1784.         ld e,(hl)
  1785.         inc hl
  1786.         add hl,de
  1787.         add hl,de
  1788.         pop de
  1789.         jp setw2        ;and go to next symbol
  1790.  
  1791. cmptx:  push bc         ;compare two symbols
  1792.         push de         ;return C set if wsp(BC) > wsp(DE)
  1793.         ld hl,wsp
  1794.         add hl,de
  1795.         add hl,de
  1796.         ld e,(hl)
  1797.         inc hl
  1798.         ld d,(hl)               ;DE points to second symbol
  1799.         ld hl,wsp
  1800.         add hl,bc
  1801.         add hl,bc
  1802.         ld c,(hl)
  1803.         inc hl
  1804.         ld b,(hl)               ;BC points to first symbol
  1805.         call cmpt2      ;compare symbols at BC and DE
  1806.         pop de          ;restore registers
  1807.         pop bc
  1808.         ret
  1809.  
  1810. cmpt2:  ld a,(bc)               ;get char
  1811.         and 7fh         ;strip end-of-symbol bit
  1812.         ld h,a          ;save
  1813.         ld a,(de)               ;do same with other symbol
  1814.         and 7fh
  1815.         cp h            ;compare with first one
  1816.         ret nz          ;if not same, all done
  1817.         ld a,(de)               ;else...is 2nd symbol ended?
  1818.         or a
  1819.         jp p,cmpt3
  1820.         ld a,(bc)               ;yes..result depends on whether
  1821.         or a            ;1st symbol is ended...
  1822.         scf
  1823.         ret p
  1824.         ccf
  1825.         ret
  1826.  
  1827. cmpt3:  ld a,(bc)               ;is 1st symbol ended?
  1828.         or a
  1829.         ret m           ;if so, it must be smaller than second.
  1830.         inc bc          ;else check next character...
  1831.         inc de
  1832.         jp cmpt2
  1833.  
  1834. ;
  1835. ; Print out the function name pointed to by HL for the SYM file and
  1836. ; stat report:
  1837. ;
  1838.  
  1839. pfnm:   push bc
  1840.         ld b,0          ;char count
  1841. pfnm1:  inc b
  1842.         ld a,(hl)
  1843.         push af
  1844.         inc hl
  1845.         and 7fh
  1846.         call outch
  1847.         pop af
  1848.         or a
  1849.         jp p,pfnm1
  1850.         ld a,b
  1851.         pop bc
  1852.         cp 3            ;if >= 3 characters, ignore
  1853.         ret nc
  1854.         ld a,9          ;else follow with a tab
  1855.         jp outch
  1856.  
  1857. ;
  1858. ; Print out the value in HL in hex and follow by a space:
  1859. ;
  1860.  
  1861. prhls:  call prhl
  1862.         ld a,' '
  1863.         jp outch
  1864.  
  1865. ;
  1866. ; Print out the value in HL in hex and follow by a comma:
  1867. ;
  1868.  
  1869. prhlc:  call prhl
  1870.         ld a,','
  1871.         jp outch
  1872.  
  1873. ;
  1874. ; Print out the value in HL in hex:
  1875. ;
  1876.  
  1877. prhl:   ld a,h
  1878.         call pra
  1879.         ld a,l
  1880.         jp pra
  1881.  
  1882. ;
  1883. ; Print out the value in A in hex:
  1884. ;
  1885.  
  1886. pra:    push af
  1887.         rrca
  1888.         rrca
  1889.         rrca
  1890.         rrca
  1891.         call prhd
  1892.         pop af
  1893.  
  1894. ;
  1895. ; Print out the hex digit in A:
  1896. ;
  1897.  
  1898. prhd:   and 15
  1899.         add a,30h
  1900.         cp 3ah
  1901.         jp c,outch
  1902.         add a,7
  1903.         jp outch
  1904.  
  1905. writf:
  1906.         IF 1==0;CPM             ;don't bother with debugging kludge under MARC yet
  1907.         lda debugf      ;debugging before output?
  1908.         or a
  1909.         jp z,writf0     ;if not, go write out CRL file
  1910.  
  1911.         lda nflag       ;don't try it if -n given, though...
  1912.         or a
  1913.         jp nz,writf0
  1914.  
  1915.         ld de,stgdeb    ;yes. tell about debugging
  1916.         call pstg
  1917.  
  1918.         ld b,0          ;first assume no text string
  1919.  
  1920.         lda debugf      ;now, was a text string given?
  1921.         dec a
  1922.         jp z,copybt     ;if not, just go ahead and do the bootstrap
  1923.  
  1924.         lhld argptr     ;else copy the text line down into tbuff
  1925.         ex de,hl                ;DE points to the text
  1926.         ld hl,tbuff+1   ;HL points into tbuff
  1927.         ld (hl),' '     ;stick in a leading space for good measure
  1928.         inc hl
  1929.         inc b
  1930. cpytxt: ld a,(de)               ;get next text character
  1931.         call mapuc      ;map to upper case just like CP/M does
  1932.         cp '"'          ;end of text?
  1933.         jp z,copybt     ;if so, go set line length and copy bootc
  1934.         ld (hl),a               ;else store the character
  1935.         inc hl          ;bump destination ptr
  1936.         inc de          ;bump source ptr
  1937.         inc b           ;bump char count
  1938.         jp cpytxt       ;and loop till done.
  1939.  
  1940. copybt: ld a,b          ;get text line count
  1941.         sta tbuff       ;set it for the argc&argv processor
  1942.  
  1943.         ld hl,boots     ;the bootstrap
  1944.         ld de,bootsa    ;where it's going
  1945.         ld b,bbytes     ;length of it
  1946. bootlp: ld a,(hl)
  1947.         ld (de),a
  1948.         inc hl
  1949.         inc de
  1950.         dec b
  1951.         jp nz,bootlp
  1952.  
  1953.         jp bootsa       ;and go copy the program and run it
  1954.  
  1955. ;
  1956. ; This is the bootstrap code, to copy the linked COM file down
  1957. ; into the tpa and run it.
  1958. ;
  1959.  
  1960. bootsa: equ tbuff+90    ;where this boot will reside
  1961.  
  1962. boots:  lhld cda        ;source addr.
  1963.         ex de,hl                ;  into DE
  1964.         lhld cdp        ;last addr. of code + 1
  1965.         ld b,h
  1966.         ld c,l          ;put last addr. in BC
  1967.         ld hl,tpa       ;destination (start of TPA)
  1968. boots1: ld a,(de)               ;copy a byte
  1969.         ld (hl),a
  1970.         inc hl
  1971.         inc de
  1972.         ld a,d          ;done yet?
  1973.         cp b
  1974.         jp nz,bootsa+12 ;jp nz,boots1
  1975.         ld a,e
  1976.         cp c
  1977.         jp nz,bootsa+12
  1978.         jp tpa          ;yes. Go execute code
  1979.  
  1980. bbytes: equ $-boots
  1981.         ENDIF           ;end of CP/M-only debugging hack, 4 the time being
  1982.  
  1983. writf0:
  1984.         IF MARC         ;under MARC, put an ".out" extension by default
  1985.         ld hl,fcbs
  1986.         push hl         ;save for create...
  1987.         push hl         ;save for ldfn...
  1988.         lda oflag       ;was -o option given?
  1989.         or a
  1990.         jp nz,writf1    ;if so, don't append '.out' to filename
  1991.         call putext
  1992.            db '.out',0
  1993. writf1:
  1994.         pop hl
  1995.         ld de,fcbt      ;copy into fcbt for error reporting
  1996.         call ldfn
  1997.  
  1998.         pop hl          ;create new version
  1999.         ld de,755q      ;protection mode allows execution
  2000.         call create
  2001.         ENDIF
  2002.  
  2003.         IF CPM
  2004.         call crate      ;create an output file under CP/M
  2005.         ENDIF
  2006.  
  2007.         lhld cda
  2008.        
  2009.         IF CPM          ;under CP/M, copy and write one sector at a time
  2010. writ2:  call copys
  2011.         push af
  2012.         push hl
  2013.         ld hl,tbuff
  2014.         call writs
  2015.         pop hl
  2016.         pop af
  2017.         jp nc,writ2
  2018.         ENDIF  
  2019.  
  2020.         IF NOT CPM      ;under MARC
  2021.         push hl         ;save starting address
  2022.         call cmh
  2023.         ex de,hl
  2024.         lhld cdp        ;get ending address
  2025.         add hl,de               ;subtract starting address
  2026.         inc hl          ;add one to get length of file to write
  2027.         ex de,hl                ;put length in DE
  2028.         pop hl          ;get back starting address
  2029.                         ;now HL = start, DE = length
  2030.         lda iflag       ;doing image mode?
  2031.         or a
  2032.         jp z,writld     ;if not, go write MARC load format file
  2033.  
  2034.         call writs      ;write it out  
  2035.         jp close
  2036.  
  2037. writld: push hl         ;save object code starting address
  2038.         lhld runsat     ;get load address
  2039.         shld runsav     ;save for load address record at end of write
  2040.         pop hl          ;restore code starting addr
  2041.  
  2042. wrtld1: call doblck     ;set up and write out a block of object file data
  2043.         jp nz,wrtld1    ;keep going till all data has been written
  2044.         ld a,1          ;now write out load address record
  2045.         sta doblck      ;use where "doblck" code was for a buffer
  2046.         lhld runsav     ;get load address
  2047.         shld doblck+1
  2048.  
  2049.         ld hl,0400h     ;this gets stored as "00 04"
  2050.         shld doblck+3
  2051.  
  2052.         ld hl,doblck    ;  as load address.
  2053.         ld de,5         ;four bytes for load address record, plus EOF byte
  2054.         call writs      ;and write out this final record
  2055.         ENDIF
  2056.  
  2057.         jp close
  2058.  
  2059.         IF MARC
  2060. doblck: shld datap      ;save object code pointer for later use
  2061.         dec hl          ;HL--> where length byte goes
  2062.         ld a,d          ;more than 255 bytes left to write?
  2063.         or a
  2064.         sta endtst      ;(save for EOF Test later)
  2065.         ld c,e          ;in case this is the last, put count in c
  2066.         jp z,sets2      ;and go away if this is the last
  2067.  
  2068.         push hl         ;else subtract 255 from DE
  2069.         ld hl,-255
  2070.         add hl,de
  2071.         ex de,hl
  2072.         pop hl
  2073.  
  2074.         ld c,255        ;and set count = 255
  2075. sets2:  ld (hl),c               ;set length count byte for this block
  2076.         dec hl
  2077.  
  2078.         push de         ;C contains byte count; save countDOWN register on stk
  2079.         ex de,hl                ;save memory pointer in DE
  2080.         lhld runsat     ;get load address for this block
  2081.         ex de,hl                ;put into DE
  2082.         ld (hl),d               ;transfer to memory
  2083.         dec hl
  2084.         ld (hl),e
  2085.         dec hl
  2086.         ld (hl),1               ;abs record header byte
  2087.  
  2088.         push hl         ;save start of block for writs
  2089.  
  2090.         ld l,c          ;update load address
  2091.         ld h,0
  2092.         add hl,de               ;by adding byte count to old load address
  2093.         shld runsat     ;and get it ready for the next iteration
  2094.  
  2095.         push bc         ;save C for count total later
  2096.         lhld datap      ;compute checksum. get pointer to start of data
  2097.         xor a           ;clear checksum accumulation
  2098. sets3:  add m           ;add next byte
  2099.         inc hl          ;bump memory pointer
  2100.         dec c           ;de-bump countdown
  2101.         jp nz,sets3     ;keep going till done
  2102.  
  2103.         cpl             ;negate A to acheive actual checksum byte
  2104.         inc a
  2105.         ld b,a          ;save checksum in B
  2106.         ld a,(hl)               ;get byte of data AFTER end of block
  2107.         sta btsav       ;save somewhere for later restortion
  2108.         ld (hl),b               ;replace with checksum (kludge, kludge)
  2109.  
  2110.         pop bc          ;get back byte count in C
  2111.         ld b,0          ;use to compute total size of block to be written
  2112.         ld hl,5         ;fudge factor because of ld format overhead
  2113.         add hl,bc               ;add to byte count to get total # byte to write
  2114.         ex de,hl                ;put into DE for writs routine
  2115.         pop hl          ;get pointer to start of block
  2116.         push de         ;save this stuff
  2117.         push hl
  2118.         call writs      ;write the block
  2119.         pop hl          ;restore this stuff
  2120.         pop de
  2121.         add hl,de               ;get pointer to start of data for NEXT block
  2122.         dec hl          ;point back to checksum byte, which overwrote data
  2123.         lda btsav       ;get back the byte butchered by the checksum byte
  2124.         ld (hl),a               ;restore it to its proper place
  2125.         pop de          ;get back countdown register
  2126.         lda endtst      ;was this the last block? if so, endtst will be zero
  2127.         or a            ;set the Z flag if so, so we don't come here again...
  2128.         ret
  2129.  
  2130.         ENDIF
  2131.  
  2132.  
  2133.  
  2134.         IF CPM          ;only need this sector copy kludge under CP/M
  2135. copys:  ld de,tbuff
  2136.         ld b,80h
  2137. copy1:  ld a,(hl)
  2138.         ld (de),a
  2139.         push de
  2140.         ex de,hl
  2141.         lhld cdp
  2142.         ld a,h
  2143.         cp d
  2144.         jp nz,copy2
  2145.         ld a,l
  2146.         cp e
  2147.         jp z,copy3
  2148. copy2:  ex de,hl
  2149.         pop de
  2150.         inc hl
  2151.         inc de
  2152.         dec b
  2153.         jp nz,copy1
  2154.         xor a
  2155.         ret
  2156.  
  2157. copy3:  pop de
  2158. copy4:  dec b
  2159.         jp z,copy5
  2160.         ld a,1ah
  2161.         ld (de),a
  2162.         inc de
  2163.         jp copy4
  2164. copy5:  scf
  2165.         ret
  2166.         ENDIF           ;end of sector copy kludge
  2167.  
  2168. ;
  2169. ; Open a file. Under CP/M, the default fcb is assumed to have been
  2170. ; set up for a file. Under MARC, a name pointer is passed in HL
  2171. ; and location "marcfd" is set here to the file descriptor returned
  2172. ; by the MARC system call; register A is 0 for opening for READ, or
  2173. ; 1 for WRITE.
  2174. ;
  2175.  
  2176. open:   push bc         ;save registers
  2177.         push de
  2178.         push hl
  2179.  
  2180.         IF TESTING
  2181.         push af
  2182.         call printf
  2183.           db 'trying to open: ',0
  2184.         push hl
  2185.         call pfnamu
  2186.         pop hl
  2187.         pop af
  2188.         ENDIF
  2189.  
  2190.         IF CPM
  2191.          xor a
  2192.          sta fcb+_rrn
  2193.          sta fcb+_rrn+1
  2194.         ld de,fcb
  2195.         ld c,openfil
  2196.         call bdos
  2197.         ENDIF
  2198.  
  2199.         IF NOT CPM
  2200.         ld c,m$open
  2201.         call msys
  2202.         ENDIF
  2203.  
  2204.         IF CPM
  2205.         cp 255          ;return value of 255 from CP/M's 'open' means error
  2206.         jp nz,op2
  2207.         ENDIF
  2208.  
  2209.         IF NOT CPM
  2210.         jp z,op2                ;Z flag under MARC indicates success
  2211.         ENDIF
  2212.  
  2213.         ld de,stg1      ;spew an error.
  2214. op1:    lda quietf      ;be quiet about it?
  2215.         or a
  2216.         call z,pstg
  2217.         ld a,0          ;clear quietf.
  2218.         sta quietf     
  2219.  
  2220.         IF MARC
  2221.         pop hl
  2222.         push hl         ;get filename string for MARC pfnam routine
  2223.         ENDIF
  2224.  
  2225.         call z,pfnamu   ;tell name of file that can't be opened
  2226.         scf             ;set carry to indicate error to calling routine
  2227.         jp op3
  2228.  
  2229. op2:
  2230.         IF CPM
  2231.         xor a
  2232.         sta nr
  2233.         ENDIF
  2234.  
  2235.         IF NOT CPM
  2236.         sta marcfd      ;store fd for file only if it opened correctly
  2237.         ENDIF
  2238.  
  2239.         IF TESTING
  2240.         call printf
  2241.           db '....opened OK',0
  2242.         ENDIF
  2243.  
  2244. op3:    pop hl
  2245.         pop de
  2246.         pop bc
  2247.         ret
  2248.  
  2249.  
  2250. ;
  2251. ; Close a file. Under CP/M, also zero out the extent field so the
  2252. ; next routine to use the default fcb doesn't go apeshit opening
  2253. ; the wrong extent.
  2254. ;
  2255.  
  2256.  
  2257. close:
  2258.         IF TESTING
  2259.         call printf
  2260.            db 'closing...',0
  2261.         ENDIF
  2262.  
  2263.         IF CPM
  2264.         push bc
  2265.         push de
  2266.         push hl
  2267.         ld de,fcb
  2268.         ld c,closefil
  2269.         call bdos
  2270.         ld hl,fcb+12
  2271.         ld (hl),0
  2272.         pop hl
  2273.         pop de
  2274.         pop bc
  2275.         cp 255
  2276.         ret nz
  2277.         ld de,stg4
  2278.         jp c2
  2279.         ENDIF
  2280.  
  2281.         IF NOT CPM
  2282.         lda marcfd
  2283.         ld c,m$close
  2284.         call msys
  2285.         jp nz,ferror
  2286.         ld a,0
  2287.         sta marcfd      ;clear fd byte for error recovery
  2288.         ret
  2289.         ENDIF
  2290.  
  2291.  
  2292. ;
  2293. ; Delete a file. Name is in default fcb for CP/M, or passed in HL
  2294. ; for MARC:
  2295. ;
  2296.  
  2297. delfil:
  2298.         IF CPM
  2299.         ld de,fcb
  2300.         ld hl,fcb+12
  2301.         ld (hl),0
  2302.         ld c,delete
  2303.         jp bdos
  2304.         ENDIF
  2305.  
  2306.         IF NOT CPM
  2307.         ld c,m$unlink
  2308.         call msys
  2309.         jp nz,ferror
  2310.         ret
  2311.         ENDIF
  2312.  
  2313.  
  2314.  
  2315.  
  2316. ;
  2317. ; Create a file and open it, first deleting any old version.
  2318. ; Filename is set up in default fcb under CP/M, or pointed to by
  2319. ; HL under MARC:
  2320. ;
  2321.  
  2322. crate:
  2323.         IF CPM
  2324.         ld hl,fcbs
  2325.         ld de,fcb
  2326.         call ldfn
  2327. crate2: call delfil
  2328.         call create
  2329. ;       call open       ;don't need to call open, since "create" does it for us
  2330.         ret
  2331.         ENDIF
  2332.  
  2333.         IF NOT CPM
  2334. crate2: ld de,644q      ;protection mode
  2335.         jp create
  2336.         ENDIF
  2337.  
  2338.  
  2339. ;
  2340. ; Create a new file. Default fcb is set up under CP/M; name pointer
  2341. ; is passed under MARC along with a protection mode in DE; marcfd
  2342. ; is set to the fd of the file (since the creat call opens it for writing):
  2343. ;
  2344.  
  2345.  
  2346. create:
  2347.         IF CPM
  2348.         ld de,fcb
  2349.         ld hl,fcb+12
  2350.         ld (hl),0
  2351.         ld hl,fcb+32
  2352.         ld (hl),0
  2353.          inc hl
  2354.          ld (hl),0 ;_rrn
  2355.          inc hl
  2356.          ld (hl),0 ;_rrn+1
  2357.         ld c,makfil
  2358.         call bdos
  2359.         cp 255
  2360.         ret nz
  2361.         ld de,stg5
  2362.         jp c2
  2363.         ENDIF
  2364.  
  2365.         IF NOT CPM
  2366.         ld c,m$creat
  2367.         call msys
  2368.         jp nz,ferror
  2369.         sta marcfd      ;save the fd if it got created OK
  2370.         ret
  2371.         ENDIF  
  2372.  
  2373.  
  2374. ;
  2375. ; Under CP/M, reads in a sector of data from the default fcb,
  2376. ; setting the Carry flag on end of file,
  2377. ; and prints something appropriately nasty on error. Under MARC,
  2378. ; reads in DE bytes to memory at HL from the currently open file:
  2379. ;
  2380.  
  2381. reads: 
  2382.         IF CPM
  2383.         push hl         ;under CP/M
  2384.         push de
  2385.         ld de,fcb
  2386.         ld c,CMD_RNDRD;rsequen
  2387.         call bdos       ;read a sector
  2388.          ld hl,(fcb+_rrn) ;ok
  2389.          inc hl
  2390.          ld (fcb+_rrn),hl ;ok
  2391.         pop de
  2392.         pop hl
  2393.         or a
  2394.         ret z           ;error?
  2395.         ;dec a          ;maybe.
  2396.         scf
  2397.         ret ;z          ;return carry if EOF
  2398.         ENDIF           ;else fall through to error reporting routine
  2399.  
  2400.  
  2401.         IF MARC         ;under MARC:
  2402.         lda marcfd
  2403.         ld c,m$read
  2404.         push de         ;save byte count
  2405.         call msys       ;read data
  2406.         pop bc          ;get back byte count in BC
  2407.         jp nz,ferror
  2408.         ld a,d
  2409.         cp b
  2410.         jp nz,reads1
  2411.         ld a,e
  2412.         cp c
  2413.         ret z
  2414.  
  2415. ;       ld a,d
  2416. ;       or e            ;is DE zero? if so, EOF: an error here
  2417. ;       ret nz
  2418.         ENDIF
  2419.  
  2420. reads1: ld de,stg2      ;read error: barfsville
  2421.         jp c2
  2422.  
  2423.  
  2424. ;
  2425. ; Write a sector of data to the file in the default fcb under CP/M;
  2426. ; write DE bytes from memory at HL to the "marcfd" file under MARC:
  2427. ;
  2428.  
  2429. writs:
  2430.         IF CPM
  2431.         push de
  2432.         push hl
  2433.         ld c,wsequen
  2434.         ld de,fcb
  2435.         call bdos
  2436.         pop hl
  2437.         pop de
  2438.         or a
  2439.         ret z
  2440.         ENDIF
  2441.  
  2442.         IF NOT CPM
  2443.         ld c,m$write
  2444.         lda marcfd
  2445.         call msys
  2446.         ret z
  2447.         jp ferror
  2448.         ENDIF  
  2449.  
  2450.         ld de,stg3      ;write error: complain
  2451.         jp c2
  2452.  
  2453. ;
  2454. ; Read a character from the standard input (console only under CP/M):
  2455. ;
  2456.  
  2457. inch:
  2458.         IF CPM
  2459.         push bc
  2460.         push hl
  2461.         ;ld c,coninp
  2462.         ;call bdos
  2463.         YIELDGETKEYLOOP
  2464.         pop hl
  2465.         pop bc
  2466.         ENDIF
  2467.  
  2468.         IF NOT CPM
  2469.         xor a           ;standard input under MARC
  2470.         ld c,m$getcf
  2471.         call msys
  2472.         jp nz,ferror
  2473.         ENDIF
  2474.  
  2475.         IF CPM
  2476.         call mapuc
  2477.         ENDIF
  2478.  
  2479.         cp key_esc;3
  2480.         jp z,abort
  2481.         ret
  2482.  
  2483. ;
  2484. ; Print a CR-LF onto standard output (console only under CP/M):
  2485. ;
  2486.  
  2487. crlf:   IF CPM
  2488.         ld a,cr
  2489.         call outch
  2490.         ENDIF
  2491.  
  2492.         ld a,lf         ;and fall through to outch
  2493.  
  2494. ;
  2495. ; Write a character to standard output, and to the symbols file
  2496. ; if wrsmf flag is active:
  2497. ;
  2498.  
  2499. outch:
  2500.         IF MARC
  2501.         cp cr           ;ignore CR's under MARC
  2502.         ret z
  2503.         ENDIF
  2504.  
  2505.         push hl
  2506.         push bc
  2507.         push de
  2508.         push af
  2509.         ld b,a
  2510.         lda wrsmf       ;if wrsmf true,
  2511.         or a
  2512.         jp z,outc1
  2513.         ld a,b          ;do it all in wrb
  2514.         call wrb
  2515.         jp outc1a
  2516.  
  2517. outc1:  ld a,b          ;otherwise just put out to the console
  2518.         call outch2
  2519.  
  2520. outc1a: pop af
  2521.         pop de
  2522.         pop bc
  2523.         pop hl
  2524.         ret
  2525.  
  2526.  
  2527. ;
  2528. ; Write the character in A to the standard output (console only under CP/M):
  2529. ;
  2530.  
  2531. outch2:
  2532.         IF CPM
  2533.         push bc
  2534.         ;ld c,conout
  2535.         ;ld e,a
  2536.         ;call bdos
  2537.         PRCHAR
  2538.         pop bc
  2539.         ret
  2540.         ENDIF
  2541.        
  2542.         IF NOT CPM      ; for MARC:
  2543.         ld b,a          ;put character in B
  2544.         lda consfd      ;stdout or stderr under MARC   
  2545.         ld c,m$putcf
  2546.         call msys
  2547.         jp nz,ferror
  2548.         ret
  2549.         ENDIF
  2550.  
  2551.  
  2552. wrb:    ld b,a          ;write out a byte of symbol table text
  2553.         lda statf       ;displaying symbol values on console?
  2554.         or a
  2555.         jp z,wrb2
  2556.         ld a,b          ;yes. do it.
  2557.         call outch2
  2558. wrb2:   lda wflg        ;writing symbols to disk?
  2559.         or a
  2560.         ret z
  2561. wrb3:   push hl
  2562.         lhld bufp      
  2563.         ld (hl),b
  2564.         inc hl
  2565.         ld a,l
  2566.         and 7fh
  2567.         jp nz,wrb4
  2568.  
  2569.         IF MARC
  2570.         ld de,128
  2571.         ENDIF
  2572.  
  2573.         ld hl,tbuff
  2574.         call writs      ;if buffer full, dump to disk
  2575. wrb4:   shld bufp
  2576.         pop hl
  2577.         ret
  2578.  
  2579.         IF MARC
  2580. pfnam:  ex de,hl                ;for MARC, put name pointer in DE
  2581.         call pstg
  2582.         call crlf
  2583.         ret
  2584.         ENDIF
  2585.  
  2586. pstg:   push af
  2587. pstg1:  ld a,(de)
  2588.         or a
  2589.         jp nz,pstg2
  2590.         pop af
  2591.         ret
  2592. pstg2:  call outch
  2593.         inc de
  2594.         jp pstg1
  2595.  
  2596. gnfl:
  2597.         IF CPM
  2598.         call scrl       ;set CRL extension
  2599.         lda quietf
  2600.         ld b,a
  2601.         ld a,1
  2602.         sta quietf      ;don't complain yet under CP/M
  2603.         ENDIF
  2604.  
  2605.         xor a
  2606.         call open       ;try to open for reading
  2607.  
  2608.         IF CPM
  2609.         ld a,b
  2610.         sta quietf
  2611.         ENDIF  
  2612.  
  2613.         IF MARC
  2614.         ret c           ;don't read in directory if not there
  2615.         ENDIF
  2616.  
  2617. ;       IF CPM
  2618.         jp nc,gnflok    ;found in first path? if so, go process
  2619.  
  2620.         lda search      ;no. should we search default area also?
  2621.         or a
  2622.         jp z,cmpln      ;if not, go complain
  2623.  
  2624.         ;lda defusr     ;now try default disk and user area
  2625.         ;ld e,a
  2626.         ;inc a          ;default user area is current user area?
  2627.         ;jp z,gnfl2     ;if so, don't change
  2628.         ;ld c,sguser
  2629.         ;lda nouser
  2630.         ;or a
  2631.         ;IF NOT ALPHA
  2632.         ;call z,bdos
  2633.         ;ENDIF
  2634.  
  2635. gnfl2:  lda defdsk      ;default disk current disk?
  2636.         ld e,a
  2637.         cp 0ffh ;if so, don't switch
  2638.         jp z,gnfl3
  2639.  
  2640.         ;push de
  2641.         ;ld c,gdisk     ;first get and save currently logged drive
  2642.         ;call bdos
  2643.         ;inc a          ;add 1 so A == 1, and 0 can mean "unused"
  2644.          xor a ;TODO getpath
  2645.         sta savdrv
  2646.         ;pop de         ;get back drive to switch to in E
  2647.         ;ld c,select
  2648.         ;call bdos
  2649.  
  2650. gnfl3:  xor a           ;now try to open in new directory
  2651.         call open
  2652.         jp nc,gnflok    ;and go on processing if we finally found it
  2653.         ret             ;else return, since this time "open" printed an error
  2654.  
  2655. cmpln:  lda quietf      ;do we bitch?
  2656.         or a
  2657.         scf
  2658.         ret nz          ;if quietf set, don't bitch, just set Cy and return
  2659.         ld de,stg1      ;can't find:
  2660.         call pstg
  2661.         call pfnamu     ;print filename
  2662.         scf             ;and return Cy set to indicate failure
  2663.         ret
  2664.  
  2665. gnflok:
  2666. ;       ENDIF
  2667.  
  2668.         ld hl,direc
  2669.  
  2670.         IF CPM          ;read in directory under CP/M one sector at a time
  2671.         call reads
  2672.         call cpys
  2673.         call reads
  2674.         call cpys
  2675.         call reads
  2676.         call cpys
  2677.         call reads
  2678.         call cpys
  2679.         ENDIF
  2680.        
  2681.         IF NOT CPM      ;under MARC  just read in 512 bytes
  2682.         ld de,512
  2683.         call reads
  2684.         ENDIF
  2685.  
  2686.         xor a           ;clear carry
  2687.         ret
  2688.  
  2689. scrl:
  2690.         IF CPM
  2691.         ld hl,fcb+9
  2692.         ld (hl),'C'
  2693.         inc hl
  2694.         ld (hl),'R'
  2695.         inc hl
  2696.         ld (hl),'L'
  2697.         ret
  2698.         ENDIF
  2699.  
  2700.         IF NOT CPM
  2701.         call putext
  2702.            db '.crl',0
  2703.         ret
  2704.         ENDIF
  2705.  
  2706.         IF CPM
  2707. cpys:   ld de,tbuff
  2708.         ld b,80h
  2709. cps2:   ld a,(de)
  2710.         ld (hl),a
  2711.         inc hl
  2712.         inc de
  2713.         dec b
  2714.         jp nz,cps2
  2715.         ret
  2716.         ENDIF           ;end of CP/M-only stuff
  2717.  
  2718.  
  2719.  
  2720.  
  2721. ;
  2722. ; Get any functions out of the currently open CRL file
  2723. ; as may be needed, until the supply is exhausted:
  2724. ;
  2725.  
  2726. gtfns:  lda fflag       ;force loading of all function in this crl file?
  2727.         or a
  2728.         jp z,gtfn0      ;if not, go scan normally for needed functions
  2729.  
  2730. ;
  2731. ; Load in EVERY function in the currently open CRL file. Once this works,
  2732. ; it should also refrain from loading any functions which have already been
  2733. ; loaded...
  2734. ;
  2735.  
  2736.         ld hl,direc     ;and try to get every function we can...
  2737. gtfn00: ld a,(hl)
  2738.         cp 80h          ;end of directory?
  2739.         jp z,gtfn03     ;if so, done with this crl file
  2740.         push hl
  2741.         push hl
  2742.         shld savnam     ;save a pointer to the name for entt1 to bitch with
  2743.         call pb7hi      ;pass name (we don't care about it yet)
  2744.         call ft23a      ;else set function parameters for rdfun
  2745.         pop hl
  2746.         ld a,1          ;say that we got it
  2747.         sta gotf
  2748.         sta rdngsm      ;set this so symbol routine does duplicate checking
  2749.         call entt1      ;enter into tab1 as gotten
  2750.         lda gotf        ;was it a duplicate?
  2751.         cp 81h
  2752.         call nz,pre             ;read it in and process if not a duplicate
  2753. gtfn02: pop hl          ;get back pointer to directory
  2754.         call pb7hi      ;pass over name of the function we just read in
  2755.         inc hl          ;and its address
  2756.         inc hl
  2757.         jp gtfn00       ;and go for the next one.
  2758.  
  2759. gtfn03:
  2760.         IF NOT FORCE
  2761.         xor a
  2762.         sta fflag       ;turn off forcing after this file.
  2763.         ENDIF
  2764.         sta rdngsm      ;and reset this also
  2765.         jp close        ;and go close the current one
  2766.  
  2767. ;
  2768. ; Scan the current CRL file for NEEDED functions only (restarting scan
  2769. ; every time one is loaded, so any local backward-references are
  2770. ; resolved):
  2771. ;
  2772.  
  2773. gtfn0:  ld hl,tab1      ;init needed-function table pointer
  2774.         shld fngtt
  2775. gtfn1:  call fung2      ;any more ungotten functions in the n-f table?
  2776.         jp c,close      ;if not, all done with this crl file
  2777.         push hl
  2778.         call ft2        ;else see if the next needed function is in the
  2779.         pop hl          ;  current CRL file
  2780.         jp c,gtfn1      ;is it?
  2781.         ld a,1          ;yes.
  2782.         sta gotf        ;say that we got it
  2783.         call entt1      ;enter the fact in tab1
  2784.         call pre        ;and actually read it in
  2785.         jp gtfn1        ;and go for more
  2786.  
  2787. ;
  2788. ; This routine scans through the entire needed-function table for
  2789. ; any non-loaded needed function entries. As soon as one is found,
  2790. ; HL is returned with a pointer to the loaded byte for that entry, and
  2791. ; Z is set. Z is returned NOT set if there aren't any more unloaded functions:
  2792. ;
  2793.  
  2794. fungt:  ld hl,tab1      ;start at the beginning
  2795.         shld fngtt
  2796.  
  2797. ;
  2798. ; This routine scans the needed-function table from the current position
  2799. ; of the table pointer (fngtt) to the end, looking for unloaded needed
  2800. ; functions. See above for return values.
  2801. ;
  2802.  
  2803. fung2:  lhld fngtt      ;get pointer into tab1
  2804. fng21:  push hl
  2805.         call past1e
  2806.         shld fngtt
  2807.         pop hl
  2808.         ld a,(hl)
  2809.         or a
  2810.         scf
  2811.         ret z
  2812.         push hl
  2813. fng22:  call pb7hi      ;pass function name
  2814.         ld a,(hl)               ;next byte a zero?
  2815.         or a
  2816.         pop hl
  2817.         jp m,fung2
  2818.         ret             ;no...so
  2819.  
  2820.  
  2821. past1e: call pb7hi
  2822.         push de
  2823.         ld a,(hl)
  2824.         and 7fh
  2825.         ld d,a
  2826.         inc hl
  2827.         ld e,(hl)
  2828.         inc hl
  2829.         ex de,hl
  2830.         add hl,hl
  2831.         add hl,de
  2832.         pop de
  2833.         ret
  2834.  
  2835.  
  2836. pb7hi:  ld a,(hl)
  2837.         inc hl
  2838.         or a
  2839.         jp p,pb7hi
  2840.         ret
  2841.  
  2842.  
  2843. mapuc:  cp 61h
  2844.         ret c
  2845.         cp 7bh
  2846.         ret nc
  2847.         sub 32
  2848.         ret
  2849.  
  2850. ;
  2851. ; This routine scans the current CRL file directory for an occurence of
  2852. ; the function whose name is pointed to by HL. If found, Cy is returned not
  2853. ; set. Cy is returned set if not found, of course:
  2854. ;
  2855.  
  2856. ft2:    push hl         ;save the name pointer
  2857.         ex de,hl                ;and also put in DE
  2858.         ld hl,direc     ;start at the beginning of directory
  2859. ft21:   ld a,(hl)               ;end of directory?
  2860.         cp 80h
  2861.         jp nz,ft22
  2862.         pop hl          ;yes. no match found, so set carry and return
  2863.         scf
  2864.         ret
  2865.  
  2866. ft22:   call stcmp      ;not end of directory. current entry match?
  2867.         jp nz,ft24
  2868.  
  2869. ft23:   call ft23a
  2870.         pop hl          ;all done. Note carry isn't set because of pb7hi
  2871.         ret
  2872.  
  2873. ft23a:  ld a,(hl)               ;yes--next two bytes after name are file address
  2874.         inc hl          ;put the file address in HL
  2875.         push hl
  2876.         ld h,(hl)
  2877.         ld l,a
  2878.         shld enst       ;save for rdfun to use in loading it later
  2879.         pop hl          ;find out what the address after the subsequent
  2880.         inc hl          ;entry is, to get ending address in file...
  2881.         call pb7hi
  2882.         ld a,(hl)               ;here it is. load into HL
  2883.         inc hl
  2884.         ld h,(hl)
  2885.         ld l,a
  2886.         shld enend      ;and save for rdfun
  2887.         ret
  2888.  
  2889. ft24:   call pb7hi      ;no match. go on to next entry
  2890.         inc hl
  2891.         inc hl
  2892.         jp ft21
  2893.  
  2894.         ;IF LASM
  2895.         ;link clinkb
  2896.         ;ENDIF
  2897.