home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-05-21 | 25.4 KB | 1,227 lines |
-
- .comment `
- This version of CCC.C is incompatible with the code generated
- by the standard version of cc. It is missing sdei, etc., and
- routines are at different addresses.
-
- `
- .xlist
-
- FALSE equ 0
- TRUE equ NOT FALSE
- MPM2 equ FALSE
-
- USERST equ FALSE ;True to use a restart vector for CDB interfacing
- RSTNUM equ 6 ;Use "RST n" as default debugger vector. Has no
- ;effect if USERST is false.
- rstloc equ RSTNUM*8 ;Memory address where "RST n" vector falls
-
- nfcbs equ 9 ;maximum # of files open at one time
- base equ 0 ;start of ram in system (either 0 or 4200h)
- .bdos equ base+5 ;the rest of these do not vary between CP/M systems.
- tpa equ base+100h
- tbuff equ base+80h
- fcb equ base+5ch
- origin equ tpa
- exitad equ base ;warm boot location
-
- cntrlc equ 3
- cr equ 0dh
- lf equ 0ah
- newlin equ 0ah
- errorv equ 255
-
-
- conin equ 1 ;get a character from console
- conout equ 2 ;write a character to console
- lstout equ 5 ;write a character to list device
- dconio equ 6 ;direct console I/O (only for CP/M 2.0)
- pstrng equ 9 ;print string (terminated by '$')
- getlin equ 10 ;get buffered line from console
- cstat equ 11 ;get console status
- select equ 14 ;select disk
- openc equ 15 ;open a file
- closec equ 16 ;close a file
- delc equ 19 ;delete a file
- reads equ 20 ;read a sector (sequential)
- ;writs equ 21 ;write a sector (sequential)
- creatc equ 22 ;make a file
- renc equ 23 ;rename file
- sdma equ 26 ;set dma
- gsuser equ 32 ;get/set user code
- readr equ 33 ;read random sector
- writr equ 34 ;write random sector
- cfsizc equ 35 ;compute file size
- srrecc equ 36 ;set random record
-
- aseg
- org origin
-
- ;
- ; The "lxi sp,0" instruction at the start of the code is changed by
- ; CLINK, if the "-t" option is NOT used, into:
- ; lhld base+6
- ; sphl
- ;
- ; If "-t <addr>" is used, then the sequence becomes:
- ; lxi sp,<addr>
- ; nop
- ;
- ; If "-n" is used, to indicate no-warm-boot, then the the sequence becomes:
- ; jmp snobsp
- ; nop
- ;
-
- lxi sp,0 ;These two instructions change depending on whether
- nop ;or not the CLINK "-t" or "-n" options are given.
-
- nop
- nop
-
- jmp skpfex ;skip over the following vector (don't ask...)
-
- fexitv: jmp exitad ;final exit vector. If "-n" used, this
- ;becomes address of the "nobret" routine.
-
- skpfex: call init ;do ARGC & ARGV processing, plus misc. initializations
- call _main ;go crunch!!!!
- jmp vexit ;close open files and reboot
-
- extrns: ds 2 ;set by CLINK to external data base address
- cccsiz: dw _main-origin ;size of this code (for use by CLINK)
- codend: ds 2 ;set by CLINK to (last addr of code + 1)
- freram: ds 2 ;set by CLINK to (last addr of externals + 1)
-
- ;
- ; Jump vectors to some file i/o utility routines:
- ;
-
- error: jmp verror ;loads -1 into HL and returns
- .exit: jmp vexit ;close all open files and reboot
-
- .close: jmp vclose ;close a file
- setfcb: jmp vsetfcb ;set up fcb at HL given filename at DE
- fgfd: jmp vfgfd ;return C set if file fd in A not open
- fgfcb: jmp vfgfcb ;compute address of internal fcb for fd in A
- setfcu: jmp vsetfcu ;set up FCB and process user number prefix
- setusr: jmp vsetusr ;set user area to upper 5 bits of A, save previous
- rstusr: jmp vrstusr ;restore user area to what it was before setusr call
- snobsp: jmp vsnobsp ;set up SP for non-boot ("-tn") CLINK option
- nobret: jmp vnobret ;return to CCP when non-boot ("-tn") in effect.
-
- ;khack: jmp vkhack ;Kirkland interrupt vector initialization
- ds 3
-
- clrex: jmp vclrex ;routine to clear external data area
-
- ;no ds 9 ;reserved
-
- ;
- ; The following routines fetch a variable value from either
- ; the local stack frame or the external area, given the relative
- ; offset of the datum required immediately following the call;
- ; for the "long displacement" routines, the offset must be 16 bits,
- ; for the "short displacement" routines, the offset must be 8 bits.
- ;(DELETED!!)
- ;ldei:
- ;sdei:
- ;lsei:
- ;ssei:
- ;ldli:
- ;sdli:
-
- ;
- ; Flag conversion routines:
- ;
- ;(DELETED!)
- ;pzinh:
- ;pnzinh:
- ;pcinh:
- ;pncinh:
- ;ppinh:
- ;pminh:
- ;pzind:
- ;pnzind:
- ;pcind:
- ;pncind:
- ;ppind:
- ;pmind:
-
- ;
- ; Relational operator routines: take args in DE and HL,
- ; and return a flag bit either set or reset.
- ;
- ; ==, >, < :
- ;
-
- eqwel: mov a,l ;return Z if HL == DE, else NZ
- cmp e
- rnz ;if L <> E, then HL <> DE
- mov a,h ;else HL == DE only if H == D
- cmp d
- ret
-
- blau: xchg ;return C if HL < DE, unsigned
- albu: mov a,d ;return C if DE < HL, unsigned
- cmp h
- rnz ;if D <> H, C is set correctly
- mov a,e ;else compare E with L
- cmp l
- ret
-
- bgau: xchg ;return C if HL > DE, unsigned
- agbu: mov a,h ;return C if DE > HL, unsigned
- cmp d
- rnz ;if H <> D, C is set correctly
- mov a,l ;else compare L with E
- cmp e
- ret
-
- blas: xchg ;return C if HL < DE, signed
- albs: mov a,h ;return C if DE < HL, signed
- xra d
- jp albu ;if same sign, do unsigned compare
- mov a,d
- ora a
- rp ;else return NC if DE is positive and HL is negative
- stc ;else set carry, since DE is negative and HL is pos.
- ret
-
- bgas: xchg ;return C if HL > DE, signed
- agbs: mov a,h ;return C if DE > HL, signed
- xra d
- jp agbu ;if same sign, go do unsigned compare
- mov a,h
- ora a
- rp ;else return NC is HL is positive and DE is negative
- stc
- ret ;else return C, since HL is neg and DE is pos
-
-
- ;
- ; Multiplicative operators: *, /, and %:
- ;
-
- smod: mov a,d ;signed MOD routine: return (DE % HL) in HL
- push psw ;save high bit of DE as sign of result
- call tstn ;get absolute value of args
- xchg
- call tstn
- xchg
- call usmod ;do unsigned mod
- pop psw ;was DE negative?
- ora a ;if not,
- rp ; all done
- mov a,h ;else make result negative
- cma
- mov h,a
- mov a,l
- cma
- mov l,a
- inx h
- ret
-
- ; nop ;maintain address compatibility with some
- ; nop ; pre-release v1.4's.
-
- usmod: mov a,h ;unsigned MOD: return (DE % HL) in HL
- ora l
- rz
- push d
- push h
- call usdiv
- pop d
- call usmul
- mov a,h
- cma
- mov h,a
- mov a,l
- cma
- mov l,a
- inx h
- pop d
- dad d
- ret
-
- smul: xra a ;signed multiply: return (DE * HL) in HL
- sta tmp
- call tstn
- xchg
- call tstn
- call usmul
- smul2: lda tmp
- rar
- rnc
- mov a,h
- cma
- mov h,a
- mov a,l
- cma
- mov l,a
- inx h
- ret
-
- tstn: mov a,h
- ora a
- rp
- cma
- mov h,a
- mov a,l
- cma
- mov l,a
- inx h
- lda tmp
- inr a
- sta tmp
- ret
-
- usmul: push b ;unsigned multiply: return (DE * HL) in HL
- call usm2
- pop b
- ret
-
- usm2: mov b,h
- mov c,l
- lxi h,0
- usm3: mov a,b
- ora c
- rz
- mov a,b
- rar
- mov b,a
- mov a,c
- rar
- mov c,a
- jnc usm4
- dad d
- usm4: xchg
- dad h
- xchg
- jmp usm3
-
- usdiv: mov a,h ;unsigned divide: return (DE / HL) in HL
- ora l ;return 0 if HL is 0
- rz
- push b
- call usd1
- mov h,b
- mov l,c
- pop b
- ret
-
-
- usd1: mvi b,1
- usd2: mov a,h
- ora a
- jm usd3
- dad h
- inr b
- jmp usd2
-
- usd3: xchg
-
- usd4: mov a,b
- lxi b,0
- usd5: push psw
- usd6: call cmphd
- jc usd7
- inx b
- push d
- mov a,d
- cma
- mov d,a
- mov a,e
- cma
- mov e,a
- inx d
- dad d
- pop d
- usd7: xra a
- mov a,d
- rar
- mov d,a
- mov a,e
- rar
- mov e,a
- pop psw
- dcr a
- rz
- push psw
- mov a,c
- ral
- mov c,a
- mov a,b
- ral
- mov b,a
- jmp usd6
-
- sdiv: xra a ;signed divide: return (DE / HL) in HL
- sta tmp
- call tstn
- xchg
- call tstn
- xchg
- call usdiv
- jmp smul2
-
- cmphd: mov a,h ;this returns C if HL < DE
- cmp d ; (unsigned compare only used
- rc ; within C.CCC, not from C)
- rnz
- mov a,l
- cmp e
- ret
-
- ;
- ; Shift operators << and >>:
- ;
-
- sderbl: xchg ;shift DE right by L bits
- shlrbe: inr e ;shift HL right by E bits
- shrbe2: dcr e
- rz
- xra a
- mov a,h
- rar
- mov h,a
- mov a,l
- rar
- mov l,a
- jmp shrbe2
-
- sdelbl: xchg ;shift DE left by L bits
- shllbe: inr e ;shift HL left by E bits
- shlbe2: dcr e
- rz
- dad h
- jmp shlbe2
-
-
- ;
- ; Routines to 2's complement HL and DE:
- ;
-
- cmh: mov a,h
- cma
- mov h,a
- mov a,l
- cma
- mov l,a
- inx h
- ret
-
- cmd: mov a,d
- cma
- mov d,a
- mov a,e
- cma
- mov e,a
- inx d
- ret
-
-
- ;
- ; The following routines yank a formal parameter value off the stack
- ; and place it in both HL and A (low byte), assuming the caller
- ; hasn't done anything to its stack pointer since IT was called.
- ;(DELETED!)
- ;ma1toh:
- ;ma2toh:
- ;ma3toh:
- ;ma4toh:
- ;ma5toh:
- ;ma6toh:
- ;ma7toh:
- ;
- ; This routine takes the first 7 args on the stack
- ; and places them contiguously at the "args" ram area.
- ; This allows a library routine to make one call to arghak
- ; and henceforth have all it's args available directly
- ; through lhld's instead of having to hack the stack as it
- ; grows and shrinks. Note that arghak should be called as the
- ; VERY FIRST THING a function does, before even pushing BC.
- ;(should delete this)
- arghak: lxi d,args
- lxi h,4 ;pass over two return address
- dad sp ;source for block move in HL
- push b ;save BC
- mvi b,14 ;countdown in B
- arghk2: mov a,m ;copy loop
- stax d
- inx h
- inx d
- dcr b
- jnz arghk2
- pop b ;restore BC
- ret
-
- ;
- ; ABSOLUTELY NO CHANGES SHOULD EVER BE MADE TO THE CODE BEFORE
- ; THIS POINT IN THIS SOURCE FILE (except for customizing the EQU
- ; statements at the beginning of the file).
- ;(Well, I did.)
-
-
- ;
- ; The following two routines are used when the "-tn" CLINK option
- ; was given, in order to preserve the SP value passed to the transient
- ; command by the CCP and return to the CCP after execution without
- ; performing a warm-boot.
- ;
-
- vsnobsp:
- lxi h,0 ;get CCP's SP value in HL
- dad sp
- shld spsav ;save it for later
- lhld base+6 ;get BIOS pointer
- lxi d,-2100 ;subtract size of CCP plus a fudge
- dad d
- sphl ;make that the new SP value
- jmp tpa+3 ;and get things under way...
-
- vnobret:
- lhld spsav ;restore CCP's SP
- sphl
- ret ;return to CCP
-
-
-
- ;
- ; This routine is called first to do argc & argv processing (if
- ; running under CP/M) and some odds and ends initializations:
- ;
-
- init: pop h ;store return address
- shld tmp2 ; somewhere safe for the time being
-
-
- ;room on stack for arglst and comlin and fcbt
-
- lxi h,-36*nfcbs
- dad sp
- shld .fcbt
-
- lxi h,-131 -36*nfcbs
- dad sp
- shld .comlin
- lxi h,-131-60 -36*nfcbs
- dad sp
- shld .arglst
- sphl
-
-
- nop
- nop
- nop
- nop
- nop
- ; nop
-
-
- dcx h
- dcx h
- ;this is now arglst-2
- ;for now, let's keep total bytes the same 'til ram
- ;=+14
- push h
-
- ;Initialize storage allocation pointers:
- lhld freram ;get address after end of externals
- shld allocp ;store at allocation pointer (for "sbrk.")
- ;excessive?
- ; lxi h,1000 ;default safety space between stack and
- ;try 256
- ;(now a constant)
- ;- lxi h,100H
- ;- shld alocmx ; highest allocatable address in memory
- ; (for use by "sbrk".).
-
- ;(revise lib so don't need this stuff)
- ;Initialize random seed:
- ; lxi h,59dch ;let's stick something wierd into the
- ; shld rseed ;first 16 bits of the random-number seed
-
- ;Initialize I/O hack locations:
- ; mvi a,0dbh ;"in" op, for "in xx; ret" subroutine
- ; sta iohack
- ; mvi a,0d3h ;"out" op for "out xx; ret" subroutine
- ; sta iohack+3
- ; mvi a,0c9h ;"ret" for above sobroutines
- ; sta iohack+2 ;the port number is filled in by the
- ; sta iohack+5 ;"inp" and "outp" library routines.
-
- ; call khack ;initialize Kirkland debugger vector
-
- ;initialize raw I/O parameters
- xra a
- sta freeze ;clear freeze (^S) flag
- sta pending ;no pending input yet
- mvi a,1fh
- sta .mode ;tty mode: all features enabled
- mvi a,'C'-64
- sta quitc ;this is the standard interrupt char
-
-
- ;under CP/M: clear console, process ARGC & ARGV:
- mvi c,cstat ;interrogate console status to see if there
- call .bdos ; happens to be a stray character there...
-
- ora a ;(used to be `ani 1'...they tell me this works
- ; nop ; better for certain bizarre CP/M-"like" systems)
-
- jz initzz
- mvi c,conin ;if input present, clear it
- call .bdos
-
- initzz:
- lhld .comlin
- xchg
- lxi h,tbuff
-
-
- ;note that we COULD find our own name in CCP conbuf
- ; if we really wanted it for argv[0]
-
- mov b,m ;first get length of it from loc. base+80h
- inx h
- mov a,b
- ora a ;if no arguments, don't parse for argv
- jnz initl
- lxi d,1 ;set argc to 1 in such a case.
- jmp i5
-
- initl: mov a,m ;ok, there are arguments. parse...
- stax d ;first copy command line to comlin
- inx h
- inx d
- dcr b
- jnz initl
- xra a ;place zero following line
- stax d
-
- lhld .arglst ;where pointers will all go
- mov b,h
- mov c,l
- lhld .comlin ;now compute pointers to each arg
- lxi d,1 ;arg count
-
-
- xra a ;clear "in a string" flag
- sta tmp1
- i2: mov a,m ;between args...
- inx h
- cpi ' '
- jz i2
- ora a
- jz i5 ;if null byte, done with list
- cpi '"'
- jnz i2a ;quote?
- sta tmp1 ;yes. set "in a string" flag
- jmp i2b
-
- i2a: dcx h
- i2b: mov a,l ;ok, HL is a pointer to the start
- stax b ;of an arg string. store it.
- inx b
- mov a,h
- stax b
- inx b
- inx d ;bump arg count
- i3: mov a,m
- inx h ;pass over text of this arg
- ora a ;if at end, all done
- jz i5
- push b ;if tmp1 set, in a string
- mov b,a ; (so we have to ignore spaces)
- lda tmp1
- ora a
- mov a,b
- pop b
- jz i3a
- cpi '"' ;we are in a string.
- jnz i3 ;check for terminating quote
- xra a ;if found, reset "in string" flag
- sta tmp1
- dcx h
- mov m,a ;and stick a zero byte after the string
- inx h ;and go on to next arg
- i3a: cpi ' ' ;now find the space between args
- jnz i3
- dcx h ;found it. stick in a zero byte
- mvi m,0
- inx h
- jmp i2 ;and go on to next arg
-
- i5: push d ;all done finding args. Set argc.
-
- mvi b,3*nfcbs ;now initialize all the file info
- lxi h,fdt ;by zeroing the fd table)
- i6: mvi m,0
- inx h
- dcr b
- jnz i6
-
-
- call clrex ;clear externals, if CLINK -z option NOT used
-
- xra a
- sta ungetl ;clear the push-back byte,
- sta errnum ;and file error code
-
- lhld tmp2
- pchl ;all done initializing.
-
- ;
- ; The following routine gets called to clear the external
- ; data area, unless the CLINK "-z" option is used.
- ;
-
- vclrex: lhld freram ;clear externals
- xchg
- lhld extrns
- call cmh
- dad d ;HL now holds size of external data area
- clrex1: mov a,h ;loop till done
- ora l
- rz
- dcx d
- dcx h
- xra a
- stax d
- jmp clrex1
-
-
- ;
- ; Initialize Kirkland interrupt vector... enables
- ; programs compiled with "-k" to run without the debugger:
- ;(DELETED)
-
- ;
- ; General purpose error value return routine:
- ;
-
- verror: lxi h,-1 ;general error handler...just
- ret ;returns -1 in HL
-
- ;
- ; Here are file I/O handling routines, only needed under CP/M:
- ;
-
- ;
- ; Close any open files and reboot:
- ;
-
- vexit:
- ;if under CP/M, close all open files
- mvi a,7+nfcbs ;start with largest possible fd
- exit1: push psw ;and scan all fd's for open files
- call vfgfd ;is file whose fd is in A open?
- jc exit2 ;if not, go on to next fd
- mov l,a ;else close the associated file
- mvi h,0
- push h
- call vclose
- pop h
- exit2: pop psw
- dcr a ;and go on to next one
- cpi 7
- jnz exit1
-
- jmp fexitv ;done closing...now return
- ; to CP/M or whatever.
-
-
- ;
- ; Close the file whose fd is 1st arg:
- ;
-
- vclose:
- ; call ma1toh ;get fd in A
- pop d
- pop h
- mov a,l
- push h
- push d
- sta ..fd
-
- call vfgfd ;see if it is open
- jc verror ;if not, complain
- mov a,m
- call setusr ;set user area to match current fd
- ani 4 ;check if open for writing
-
-
- IF NOT MPM2 ;if not MP/M, and
- jz close2 ;the file isn't open for write, don't bother to close
- ENDIF
-
-
- push h ;save fd table entry addr
-
- ; call ma2toh ;get the fd in A again
- lda ..fd
-
- push b
- call vfgfcb ;get the appropriate fcb address
- xchg ;put it in DE
- mvi c,closec ;get BDOS function # for close
- call .bdos ;and do it!
- pop b
- pop h
- close2: call rstusr ;reset user number to original state
- mvi m,0 ;close the file logically
- cpi 255 ;if 255 came back from .bdos, we got problems
- lxi h,0
- rnz ;return 0 if OK
- dcx h ;return -1 on error
- ret
-
- ..fd: ds 1
- ;
- ; Determine status of file whose fd is in A...if the file
- ; is open, return Cy clear and with the address of the fd table
- ; entry for the open file in HL. If the file is not open,
- ; return Cy set:
- ;
-
- vfgfd: mov d,a
- sui 8
- rc ;if fd < 8, error
- cpi nfcbs
- cmc ;don't allow too big an fd either
- rc
- push d
- mov e,a ;OK, we have a value in range. Now
- mvi d,0 ; see if the file is open or not
- lxi h,fdt
- dad d ;offset for 3-byte table entries
- dad d
- dad d
- mov a,m
- ani 1 ;bit 0 is high if file is open
- stc
- pop d
- mov a,d
- rz ;return C set if not open
- cmc
- ret ;else reset C and return
-
- ;
- ; Set up a CP/M file control block at HL with the file whose
- ; simple null-terminated name is pointed to by DE:
- ; Format for filename must be: "[white space][d:]filename.ext"
- ; The user number prefix hack is NOT recognized by this subroutine.
- ;
-
- vsetfcb:
- push b
- call igwsp ;ignore blanks and tabs
- push h ;save fcb ptr
- inx d ;peek at 2nd char of filename
- ldax d
- dcx d
- cpi ':' ;default disk byte value is 0
- mvi a,0 ; (for currently logged disk)
- jnz setf1
- ldax d ;oh oh...we have a disk designator
- call mapuc ;make it upper case
- sui 'A'-1 ;and fudge it a bit
- inx d ;advance DE past disk designator to filename
- inx d
- setf1: mov m,a ;set disk byte
- inx h
- mvi b,8
- call setnm ;set filename, pad with blanks
- call setnm3 ;ignore extra characters in filename
- ldax d
- cpi '.' ;if an extension is given,
- jnz setf2
- inx d ;skip the '.'
- setf2: mvi b,3
- call setnm ;set the extension field and pad with blanks
- xra a ;and zero the appropriate fields of the fcb
- mov m,a
- lxi d,20
- dad d
- mov m,a
- inx h
- mov m,a ;zero random record bytes of fcb
- inx h
- mov m,a
- inx h
- mov m,a
- pop d
- pop b
- ret
-
- ;
- ; This routine copies up to B characters from (DE) to (HL),
- ; padding with blanks on the right. An asterisk causes the rest
- ; of the field to be padded with '?' characters:
- ;
-
- setnm: push b
- setnm1: ldax d
- cpi '*' ;wild card?
- mvi a,'?' ;if so, pad with ? characters
- jz pad2
-
- setnm2: ldax d
- call legfc ;next char legal filename char?
- jc pad ;if not, go pad for total of B characters
- mov m,a ;else store
- inx h
- inx d
- dcr b
- jnz setnm1 ;and go for more if B not yet zero
- pop b
- setnm3: ldax d ;skip rest of filename if B chars already found
- call legfc
- rc
- inx d
- jmp setnm3
-
- pad: mvi a,' ' ;pad with B blanks
- pad2: mov m,a ;pad with B instances of char in A
- inx h
- dcr b
- jnz pad2
- pop b
- ret
-
- ;
- ; Process filename having optional user area number prefix of form "<u#>/",
- ; return the effective user area number of the given filename in the upper
- ; 5 bits of A, and also store this value at "usrnum". Note that if no user
- ; number is specified, the current user area is presumed by default. After
- ; the user area prefix is processed, do a regular "setfcb":
- ;
- ; Note: a filename is considered to have a user number if the first char
- ; in the name is a decimal digit and the first non-decimal-digit
- ; character in the name is a slash (/).
-
- vsetfcu:
- push b ;save BC
- push h ;save vcb pointer
- call igwsp ;ignore blanks and tabs
- call isdec ;decimal digit?
- jnc setfc2 ;if so, go process
-
- setfc0: push d ;save text pointer
- mvi c,gsuser ;else get current effective user number
- mvi e,0ffh
-
- call .bdos ;get current user area if implemented
-
- pop d ;restore text pointer
-
- setfc1: rlc ;rotate into upper 5 bits of A
- rlc
- rlc
- sta usrnum ;and save
- pop h ;restore junk
- pop b
- jmp setfcb ;and parse rest of filename
-
- setfc2: mvi b,0 ;clear user number counter
- push d ;save text pointer in case we invalidate user prefix
- setfc3: sui '0' ;save next digit value
- mov c,a ; in C
- mov a,b ;multiply previous sum by 10
- add a ;*2
- add a ;*4
- add a ;*8
- add b ;*9
- add b ;*10
- add c ;add new digit
- mov b,a ;put sum in B
- inx d ;look at next char in text
- ldax d ;is it a digit?
- call isdec
- jnc setfc3 ;if so, go on looping and summing digits
- cpi '/' ;make sure number is terminated by a slash
- jz setfc4
- pop d ;if not, entire number prefix is not really a
- jmp setfc0 ; user number, so just ignore it all.
-
- setfc4: inx d ;ok, allow the user number
- pop h ;get old text pointer off the stack
- mov a,b ;get user number value
- jmp setfc1 ;and go store it and parse rest of filename
-
-
- ;
- ; Test if char in A is legal character to be in a filename:
- ;
-
- legfc: call mapuc
- cpi '.' ; '.' is illegal in a filename or extension
- stc
- rz
- cpi ':' ;so is ':'
- stc
- rz
- cpi 7fh ;delete is no good
- stc
- rz
- cpi '!' ;if less than exclamation pt, not legal char
- ret ;else good enough
-
- ;
- ; Map character in A to upper case if it is lower case:
- ;
-
- mapuc: cpi 'a'
- rc
- cpi 'z'+1
- rnc
- sui 32 ;if lower case, map to upper
- ret
-
- ;
- ; Ignore blanks and tabs at text pointed to by DE:
- ;
-
- igwsp: dcx d
- igwsp1: inx d
- ldax d
- cpi ' '
- jz igwsp1
- cpi 9
- jz igwsp1
- ret
-
- ;
- ; Return Cy if char in A is not a decimal digit:
- ;
-
- isdec: cpi '0'
- rc
- cpi '9'+1
- cmc
- ret
-
-
- ;
- ; This routine does one of two things, depending
- ; on the value passed in A.
- ;
- ; If A is zero, then it finds a free file slot
- ; (if possible), else returns C set.
- ;
- ; If A is non-zero, then it returns the address
- ; of the fcb corresponding to an open file whose
- ; fd happens to be the value in A, or C set if there
- ; is no file associated with fd.
- ;
-
- vfgfcb: push b
- ora a ;look for free slot?
- mov c,a
- jnz fgfc2 ;if not, go away
- mvi b,nfcbs ;yes. do it...
- lxi d,fdt
- ; lxi h,fcbt
- lhld .fcbt
- mvi c,8
- fgfc1: ldax d
- ani 1
- mov a,c
- jnz fgfc1a ;found free slot?
- pop b ;yes. all done.
- ret
-
- fgfc1a: push d
- lxi d,36 ;fcb length to accommodate random I/O
- dad d
- pop d
- inx d ;bump to next 3-byte table entry
- inx d
- inx d
- inr c
- dcr b
- jnz fgfc1
- fgfc1b: stc
- pop b
- ret ;return C if no more free slots
-
- fgfc2: call vfgfd ;compute fcb address for fd in A:
- jc fgfc1b ;return C if file isn't open
-
- sui 8
- mov l,a ;put (fd-8) in HL
- mvi h,0
- dad h ;double it
- dad h ;4*a
- mov d,h ;save 4*a in DE
- mov e,l
- dad h ;8*a
- dad h ;16*a
- dad h ;32*a
- dad d ;36*a
- xchg ;put 36*a in DE
- ;- lxi h,fcbt ;add to base of table
- lhld .fcbt
- dad d ;result in HL
- mov a,c ;and return original fd in A
- pop b
- ret
-
- ;
- ; The following two subroutines change the current CP/M user area for
- ; user with file I/O:
- ;
-
- vsetusr:
- push b ;SET user number to upper bits of A, save current:
- push h
- push d
- push psw ;save A
- mvi c,gsuser ;get user code
- mvi e,0ffh
- call .bdos
- sta curusr ;save current user number
- pop psw ;get new user number byte
- push psw
- rar ;shift user number down to low bits
- rar
- rar
- ani 1fh ;and mask off high order garbage
- setu0: mov e,a
- mvi c,gsuser ;set user code
- call .bdos
- pop psw
- pop d
- pop h
- pop b
- ret
-
- vrstusr:
- push b
- push h
- push d
- push psw
- lda curusr ;get last saved user number
- jmp setu0 ;and go set current user area to that
-
- ;
- ; Ram area:
- ;
-
- ram equ $
-
- ;no ds 20 ;reserved by BDS
-
- errnum: ds 1 ;error code from file I/O operations
- ;pbase: ds 2 ;screen-DMA address
- ;ysize: ds 2 ;screen width
- ;xsize: ds 2 ;screen height
- ;psize: ds 2 ;screen length
-
- ;rseed: ds 8 ;the random generator seed
-
-
- args:
- arg1: ds 2
- arg2: ds 2
- arg3: ds 2
- arg4: ds 2
- arg5: ds 2
- arg6: ds 2
- arg7: ds 2
- ;"arghak" puts args passed on stack here.
-
- ;iohack: ds 6 ;room for I/O subroutines for use by "inp"
- ;and "outp" library routines
-
- allocp: ds 2 ;pointer to free storage for use by "sbrk" func
- alocmx: dw 100H ;highest location to be made available to the
- ;storage allocator
-
- ;room: ds 30 ;reserved for use by BDS C system code
- ;uroom: ds 20 ;available for use by user
-
- .comment `
- (Too much garbage here)
- tmp equ room ;this is misc. garbage space
- tmp1 equ room+1
- tmp2 equ room+2
- tmp2a equ room+4
- ungetl equ room+6 ;where characters are "ungotten"
- unused equ room+7
- curusr equ room+8 ;used to save current user number during file I/O
- usrnum equ room+9 ;set by "setfcu" to user number of given filename
-
- .mode equ room+10 ;tty mode
- freeze equ room+11 ;true if output frozen (^S)
- pending equ room+12 ;true if input character waiting
- pendch equ room+13 ;if pending true, this is the character
- quitc equ room+14 ;the general system abort character (^C usually)
- spsav equ room+15 ;saved SP value from CCP
- ; equ room+17 ;where next thing goes
-
- `
-
-
- tmp: ds 1 ;this is misc. garbage space
- tmp1: ds 1
- tmp2: ds 2
- tmp2a: ds 2
- ungetl: ds 1 ;where characters are "ungotten"
- ;unused
- curusr: ds 1 ;used to save current user number during file I/O
- usrnum: ds 1 ;set by "setfcu" to user number of given filename
-
- .mode: ds 1 ;tty mode
- freeze: ds 1 ;true if output frozen (^S)
- pending:ds 1 ;true if input character waiting
- pendch: ds 1 ;if pending true, this is the character
- quitc: ds 1 ;the general system abort character (^C usually)
- spsav: ds 2 ;saved SP value from CCP
-
-
- echo equ 1 ;masks for "mode" byte...echo mode
- quit equ 2 ;quit enabled
- flow equ 4 ;^S/^Q protocol honored
- strip equ 8 ;strip parity
- expand equ 16 ;expand '\n' into CR-LF on output
-
- ;
- ;--------------------------------------------------------------------------
- ;
- ; The fcb table (fcbt): 36 bytes per file control block
- ;
-
- ;fcbt: ds 36*nfcbs ;reserve room for fcb's (extra byte for IMDOS)
-
-
- ;
- ; The fd table: one byte per file specifying r/w/open as follows:
- ; bit 0 is high if open, low if closed
- ; bit 1 is high if open for read
- ; bit 2 is high if open for write (both b1 and b2 may be high)
- ; bits 3-7 contain the user number in which the file is active (0-31)
- ;
-
- fdt: ds 3*nfcbs ;3 bytes per fcb: 1 for active, r/w, etc., and
- ; 2 to specify highest sector num seen
-
- ;
- ; The command line is copied here by init:
- ;
-
- ;
- ; End of CP/M-only data area
- ;---------------------------------------------------------------------------
-
- .fcbt: ds 2
- .comlin: ds 2
- .arglst: ds 2
-
- _main equ $ ;where "main" program will be loaded under CP/M
-
- .list
-
- end
-