From: utzoo!decvax!harpo!npoiv!npois!wbux5!wb2!houxz!ihnp4!ixn5c!inuxc!pur-ee!uiucdcs!schrein Newsgroups: net.sources Title: Re: smallC V2 CP/M runtime support - (nf) Article-I.D.: uiucdcs.1670 Posted: Sun Mar 13 22:45:03 1983 Received: Wed Mar 16 00:51:47 1983 #R:uiucdcs:12600001:uiucdcs:12600002:000:51453 uiucdcs!schrein Mar 12 09:22:00 1983 (smallC V2 CP/M runtime support continued) (part 2) %%%%%%%%%% scc/rtl/crtl.mac %%%%%%%%%% ; crtl.mac -- smallC runtime environment module ; for CP/M ; for MACRO/80 ; ats 2/83 ; in part adapted from Jim Hendrix' code ; global name conventions: ; ======================== ; ; ? starts an internal routine name ; _ starts an internal C-callable name ; other starts a published C-callable name ; ; This file is organized so that all references ; to global symbols are forward. ; smallC CP/M environment: ; ======================== ; ; Set up stack to run from top of memory downward, ; and call smallC environment routine _shell(). ; ; Upon return, connect to BIOS warm start. ; ; _exit entry point to BIOS warm start, ; i.e., no file management wrapup. ; ; If the END module is linked last (and it must be): ; ; _edata follows the last static data area, ; is preceded by the 6 character production date mmddyy ; ; _eprog follows the last code area ; is preceded by the 6 character compiler logo ; ; _end follows the end of code and data ; ; The smallC compiler is expected to supply a reference ; to the ?smallC routine to arrange for proper library search. ENTRY ?smallC ENTRY _exit EXTRN _shell ; outermost C runtime routine EXTRN ?30217 ; version reference V.BIOS EQU 0 ; entry vector for BIOS warm start V.BDOS EQU 5 ; entry vector for BDOS CSEG ?smallC: LHLD V.BDOS+1 ; stack starts at top of memory SPHL CALL _shell ; call C environment routine _exit: JMP V.BIOS ; return to system ; BDOS calls: ; =========== ; ; BDOS C call return entry description ; code value value ; ------------------------------------------------------------- ; 0 abort() system reset ; 1 i = _getchar() char console read ; 2 _putchar(c) char console write ; 3 i = _rgetchar() char reader read ; 4 _pputchar(c) char punch write ; 5 _lputchar(c) char list write ; 6 i = _dirio(c) char 0xFF direct input ; 0=busy char direct output ; 7 i = _giob() byte get i/o byte ; 8 _siob(c) byte set i/o byte ; 9 _puts(&c) address print string to next $ ; 10 _gets(&buf) address read console buffer ; 11 i = _cstat() 0=busy get console status ; 12 i = _vers() word get version number (in hex) ; 13 _reset() reset disk ; 14 i = _mount(c) 0=ok drive# select disk ; 15 i = _open(&f) dir [1] address open file ; 16 i = _close(&f) dir address close file ; 17 i = _glob(&f) dir [2] address search for first file name ; 18 i = _nglob() dir [2] search for next file name ; 19 i = _delete(&f) dir address delete file ; 20 i = _read(&f) err [3] address read next record ; 21 i = _write(&f) err [3] address write next record ; 22 i = _create(&f) dir [1] address create file ; 23 i = _renam(&fn) dir address rename file ; 24 i = _login() vector get login vector ; 25 i = _drive() drive# get disk number ; 26 _setbuf(&c) address set DMA address (of 128 bytes) ; 27 i = _bitmap() bitmap get allocate vector ; 28 _protect() write protect ; 29 i = romap() vector get R/O vector ; 30 i = _chmod(&f) dir address set file attributes ; 31 i = _diskmap() diskmap get disk header address ; 32 i = _uid(c) user# 0xFF get user number ; 0=ok user# set user number ; 33 i = _rread(&f) err [4] address read random ; 34 i = _rwrite(&f) err [4] address write random ; 35 _stat(&f) [5] address compute file size ; 36 _record(&f) [5] address set random record ; 37 i = _umount(i) 0=ok vector reset selected drives ; 40 i = _rzwrite(&f) err[4] address write random zero fill ; ------------------------------------------------------------- ; ; bitmap from left to right, set bits indicate allocated ; reservation blocks ; ; buf console buffer has the following format: ; ; byte (in) maximum length available for text ; byte (out) length actually filled ; byte... (out) text read, without trailing newline ; ; c character (byte) parameter ; ; dir position in directory sector, 0..3 ; not found: 0xFF ; ; diskmap CP/M disk description ; ; drive# disk drive number, 0==A, 1==B, ... ; ; err error code: ; ; 0 ok ; 1 reading unwritten data (end of file) ; ; f file control block ; can usually contain wildcard file name ; ; fn file control block, ; new name begins at offset 17 ; ; i integer (word) result, possibly byte sign-extended ; ; vector bit vector indicating disk drives, ; least significant bit is drive 0 ; ; [1] modifies argument file control block ; ; [2] requires _setbuf(), ; result indicates directory entry in this buffer ; ; [3] requires _setbuf(), ; i/o happens from the DMA area set up by _setbuf() ; ; [4] [3], additionally, the random record position ; must have been set in the argument file control block ; ; [5] result is returned to the random record position ; in the file control block ; macro to dispatch BDOS calls: ; ----------------------------- ; ; t action ; ------------------------------------------------------------- ; 0 jump to BDOS ; 1 call BDOS, return HL = (int) A ; 2 DE = parm, call BDOS ; 3 DE = parm, call BDOS, return HL = (int) A BDOS MACRO func,t,code &func:: ;; entry point from C MVI C,&code ;; set BDOS function code JMP ?BD&t ;; goto executor ENDM BDOS abort,0,0 BDOS _getch,1,1 BDOS _putch,2,2 BDOS _rgetc,1,3 BDOS _pputc,2,4 BDOS _lputc,2,5 BDOS _dirio,3,6 BDOS _giob,1,7 BDOS _siob,2,8 BDOS _puts,2,9 BDOS _gets,2,10 BDOS _cstat,1,11 BDOS _vers,0,12 BDOS _reset,0,13 BDOS _mount,3,14 BDOS _open,3,15 BDOS _close,3,16 BDOS _glob,3,17 BDOS _nglob,1,18 BDOS _delete,3,19 BDOS _read,3,20 BDOS _write,3,21 BDOS _create,3,22 BDOS _rename,3,23 BDOS _login,0,24 BDOS _drive,1,25 BDOS _setbuf,2,26 BDOS _bitmap,0,27 BDOS _protect,0,28 BDOS _romap,0,29 BDOS _chmod,3,30 BDOS _diskmap,0,31 BDOS _uid,3,32 BDOS _rread,3,33 BDOS _rwrite,3,34 BDOS _stat,2,35 BDOS _record,2,36 BDOS _umount,3,37 BDOS _rzwrite,3,40 ; BDOS interface: ; --------------- ; type 0: ; ; jump to BDOS ; i.e., either no return, or return HL ?BD0 EQU V.BDOS ; type 2: ; ; C in BDOS function ; DE local int parameter ; HL local ?BD2: POP H ; return POP D ; int parameter PUSH D PUSH H JMP V.BDOS ; return through BDOS ; type 3: ; ; A local from BDOS ; C in BDOS function ; DE local int parameter ; HL out = (int) A ?BD3: POP H ; return POP D ; int parameter PUSH D PUSH H ; JMP ?BD1 ; BDOS, return HL = (int) A ; type 1: ; ; A local from BDOS ; C in BDOS function ; HL out = (int) A ?BD1: CALL V.BDOS JMP ?SXT ; HL = (int) A ; BIOS calls: ; =========== ; ; BIOS C call return entry description ; offset value value ; ------------------------------------------------------------- ; 0 complete cold start ; 3 _wboot() warm start ; 6 i = _const() ff=ready console status ; 9 c = _conin() char console input (no echo) ; 12 _conout(c) char console output ; 15 _lstout(c) char printer output ; 18 _punout(c) char punch output ; 21 c = _rdrin() char reader input ; 24 _home() set track zero ; 27 i = _seldsk(c,b) 0=no drive#, select disk ; diskmap first ; 30 _settrk(i) track select track ; 33 _setsec(i) sector set sector ; 36 _setdma(&c) address set DMA address ; 39 i = _sread() 0=ok read CP/M sector ; 42 i = _swrite(c) 0=ok all write CP/M sector ; 45 i = _lstst() ff=ready printer status ; 48 i = _sectran(i,&c) phys log, translate sector ; ttable ; ------------------------------------------------------------- ; ; all 0: write to previously allocated block ; 1: write to directory (always to disk) ; 2: write to first sector of unallocated data block ; ; b bit parameter ; ; c character (byte) parameter ; ; diskmap CP/M disk description (0==no such drive) ; ; drive# disk drive number, 0==A, 1==B, ... ; ; first bit 0: 0==first call for this disk ; ; i integer (word) result, possibly byte sign-extended ; ; ttable translation table address (0==none) ; macro to dispatch BIOS calls: ; ----------------------------- ; ; t action ; ------------------------------------------------------------- ; 0 jump to BIOS ; 1 call BIOS, return HL = (int) A ; 2 BC = parm, call BIOS ; 3 BC = parm, DE = parm, call BIOS ; 4 BC = parm, call BIOS, return HL = (int) A BIOS MACRO func,t,offset &func:: ;; entry point from C MVI A,&offset ;; set BIOS offset JMP ?BI&t ;; goto executor ENDM BIOS _wboot,0,3 BIOS _const,1,6 BIOS _conin,1,9 BIOS _conou,2,12 BIOS _lstou,2,15 BIOS _punou,2,18 BIOS _rdrin,1,21 BIOS _home,0,24 BIOS _selds,3,27 BIOS _settr,2,30 BIOS _setse,2,33 BIOS _setdm,2,36 BIOS _sread,1,39 BIOS _swrit,4,42 BIOS _lstst,1,45 BIOS _sectr,3,48 ; BIOS interface: ; --------------- ; type 1: ; ; A in offset in BIOS page ; local BIOS return ; HL out = (int) A ?BI1: LHLD V.BIOS+1 ; H = BIOS page number MOV L,A ; L = BIOS offset PUSH H LXI H,$+5 ; return address XTHL ; to stack PCHL JMP ?SXT ; HL = (int) A ; type 2: ; ; A in offset in BIOS page ; BC local int parameter ; HL local ?BI2: POP H ; return POP B ; parameter PUSH B PUSH H JMP ?BI0 ; go and return through BIOS ; type 3: ; ; A in offset in BIOS page ; BC local int parameter (first) ; DE local int parameter (second) ; HL local ?BI3: POP H ; return POP D ; second parameter POP B ; first parameter PUSH B PUSH D PUSH H ; JMP ?BI0 ; go and return through BIOS ; type 0: ; ; A in offset in BIOS page ; HL local ?BI0: LHLD V.BIOS+1 ; H = BIOS page number MOV L,A ; L = BIOS offset PCHL ; type 4: ; ; A in offset in BIOS page ; local BIOS return ; BC local int parameter ; HL out = (int) A ?BI4: POP H ; return POP B ; parameter PUSH B PUSH H LHLD V.BIOS+1 ; H = BIOS page number MOV L,A ; L = BIOS offset PUSH H LXI H,$+5 ; return address XTHL ; to stack PCHL JMP ?SXT ; HL = (int) A ; Jim Hendrix' arithmetic and logic library: ; ========================================== ; routine headers: ; ---------------- ENTRY ?OR ; hl |= de ENTRY ?XOR ; hl ^= de ENTRY ?AND ; hl &= de ENTRY ?EQ ; hl = hl == de ENTRY ?NE ; hl = hl != de ENTRY ?GT ; hl = (int) de > hl ENTRY ?LE ; hl = (int) de <= hl ENTRY ?GE ; hl = (int) de >= hl ENTRY ?LT ; hl = (int) de < hl ENTRY ?UGE ; hl = (unsigned) de >= hl ENTRY ?ULT ; hl = (unsigned) de < hl ENTRY ?UGT ; hl = (unsigned) de > hl ENTRY ?ULE ; hl = (unsigned) de <= hl ENTRY ?ASR ; hl = de >> hl ENTRY ?ASL ; hl = de << hl ENTRY ?SUB ; hl = de - hl ENTRY ?NEG ; hl = - hl ENTRY ?COM ; hl = ~ hl ENTRY ?MULT ; hl *= de ENTRY ?DIV ; hl = de / hl, de %= hl ENTRY ?LNEG ; hl = ! hl ENTRY ?DECC ; (byte) *(hl+top()) -- ENTRY ?INCC ; (byte) *(hl+top()) ++ ENTRY ?DECI ; (word) *(hl+top()) -- ENTRY ?INCI ; (word) *(hl+top()) ++ ENTRY ?DDGC ; hl = (int) (byte) *(hl+de) ENTRY ?DSGC ; hl = (int) (byte) *(hl+top()) ENTRY ?GCHAR ; hl = (int) (byte) *hl ENTRY ?SXT ; hl = (int) a ENTRY _narg ; hl = (int) a /* number of arguments */ ENTRY ?DDGI ; hl = (word) *(hl+de) ENTRY ?DSGI ; hl = (word) *(hl+top()) ENTRY ?GINT ; hl = (word) *hl ENTRY ?DDPPC ; (byte) *(pop()) = de+hl ENTRY ?PDPC ; (byte) *(pop()) = hl ENTRY ?DDPPI ; (word) *(pop()) = de+hl ENTRY ?PDPI ; (word) *(pop()) = hl ENTRY ?PINT ; (word) *de = hl ENTRY ?SWITCH ; switch selector execution ; code region: ; ------------ ; ; Blank lines separate potential modules. ?OR: MOV A,L ORA E MOV L,A MOV A,H ORA D MOV H,A RET ?XOR: MOV A,L XRA E MOV L,A MOV A,H XRA D MOV H,A RET ?AND: MOV A,L ANA E MOV L,A MOV A,H ANA D MOV H,A RET ?EQ: CALL CMP0 RZ DCX H RET ?NE: CALL CMP0 RNZ DCX H RET ?GT: XCHG CALL CMP0 RC DCX H RET ?LE: CALL CMP0 RZ RC DCX H RET ?GE: CALL CMP0 RNC DCX H RET ?LT: CALL CMP0 RC DCX H RET CMP0: MOV A,H ; INVERT SIGN OF HL XRI 80H MOV H,A MOV A,D ; INVERT SIGN OF DE XRI 80H CMP H ; COMPARE MSBS JNZ CMP1 ; DONE IF NEQ MOV A,E ; COMPARE LSBS CMP L CMP1: LXI H,1 ; PRESET TRUE COND RET ?UGE: CALL UCMP0 RNC DCX H RET ?ULT: CALL UCMP0 RC DCX H RET ?UGT: XCHG CALL UCMP0 RC DCX H RET ?ULE: CALL UCMP0 RZ RC DCX H RET UCMP0: MOV A,D CMP H JNZ UCMP1 MOV A,E CMP L UCMP1: LXI H,1 RET ?ASR: XCHG DCR E RM MOV A,H RAL MOV A,H RAR MOV H,A MOV A,L RAR MOV L,A JMP ?ASR+1 ?ASL: XCHG DCR E RM DAD H JMP ?ASL+1 ?SUB: MOV A,E SUB L MOV L,A MOV A,D SBB H MOV H,A RET ?NEG: CALL ?COM INX H RET ?COM: MOV A,H CMA MOV H,A MOV A,L CMA MOV L,A RET ?MULT: MOV B,H MOV C,L LXI H,0 MULT1: MOV A,C RRC JNC MULT2 DAD D MULT2: XRA A MOV A,B RAR MOV B,A MOV A,C RAR MOV C,A ORA B RZ XRA A MOV A,E RAL MOV E,A MOV A,D RAL MOV D,A ORA E RZ JMP MULT1 ?DIV: MOV B,H MOV C,L MOV A,D XRA B PUSH PSW MOV A,D ORA A CM DENEG MOV A,B ORA A CM BCNEG MVI A,16 PUSH PSW XCHG LXI D,0 DIV1: DAD H CALL RDEL JZ DIV2 CALL CMPBCDE JM DIV2 MOV A,L ORI 1 MOV L,A MOV A,E SUB C MOV E,A MOV A,D SBB B MOV D,A DIV2: POP PSW DCR A JZ DIV3 PUSH PSW JMP DIV1 DIV3: POP PSW RP CALL DENEG XCHG CALL DENEG XCHG RET DENEG: ;NEGATE THE INTEGER IN DE MOV A,D CMA MOV D,A MOV A,E CMA MOV E,A INX D RET BCNEG: ;NEGATE THE INTEGER IN BC MOV A,B CMA MOV B,A MOV A,C CMA MOV C,A INX B RET RDEL: ;ROTATE DE LEFT ONE BIT MOV A,E RAL MOV E,A MOV A,D RAL MOV D,A ORA E RET CMPBCDE: ;COMPARE BC TO DE MOV A,E SUB C MOV A,D SBB B RET ?LNEG: MOV A,H ORA L JNZ $+6 MVI L,1 RET LXI H,0 RET ?DECC: INX H INX H DAD SP MOV D,H MOV E,L CALL ?GCHAR DCX H MOV A,L STAX D RET ?INCC: INX H INX H DAD SP MOV D,H MOV E,L CALL ?GCHAR INX H MOV A,L STAX D RET ?DECI: INX H INX H DAD SP MOV D,H MOV E,L CALL ?GINT DCX H JMP ?PINT ?INCI: INX H INX H DAD SP MOV D,H MOV E,L CALL ?GINT INX H JMP ?PINT ?DDGC: DAD D JMP ?GCHAR ?DSGC: INX H INX H DAD SP ?GCHAR: MOV A,M _narg: ?SXT: MOV L,A RLC SBB A MOV H,A RET ?DDGI: DAD D JMP ?GINT ?DSGI: INX H INX H DAD SP ?GINT: MOV A,M INX H MOV H,M MOV L,A RET ?DDPPC: DAD D ?PDPC: POP B ; RET ADDR POP D PUSH B ;PCHAR: MOV A,L STAX D RET ?DDPPI: DAD D ?PDPI: POP B ; RET ADDR POP D PUSH B ?PINT: MOV A,L STAX D INX D MOV A,H STAX D RET ; EXECUTE "SWITCH" STATEMENT ; ; HL = SWITCH VALUE ; (SP) -> SWITCH TABLE ; DW ADDR1, VALUE1 ; DW ADDR2, VALUE2 ; ... ; DW 0 ; [JMP default] ; continuation ; ?SWITCH: XCHG ; DE = SWITCH VALUE POP H ; HL -> SWITCH TABLE SWLOOP: MOV C,M INX H MOV B,M ; BC -> CASE ADDR, ELSE 0 INX H MOV A,B ORA C JZ SWEND ; DEFAULT OR CONTINUATION CODE MOV A,M INX H CMP E MOV A,M INX H JNZ SWLOOP CMP D JNZ SWLOOP MOV H,B ; CASE MATCHED MOV L,C SWEND: PCHL END ?smallC %%%%%%%%%% scc/rtl/csh.c %%%%%%%%%% /* * csh.c -- smallC runtime environment for CP/M and MACRO-80 * ats 2/83, in part adapted from Jim Hendrix' code */ #define NOCCARGC #include def.h /* TO BE FIXED */ /* * external references */ extern _exit(); /* termination point in ?smallC */ extern main(); /* user's main program */ extern char _end[]; /* begin of heap */ extern _dnm[], _dop[], /* CP/M driver table */ _drd[], _dwr[], _dbr[], _dbw[], _dsk[], _dcl[]; extern _drive(); /* BDOS /* current drive number */ /**** **** global data regions ****/ /* * character classification table */ /* 128 64 32 16 8 4 2 1 */ /* special upper lower num hex space punct cntrl */ #define C_PRINT (128+64+32+16 +4 ) /* printing character */ #define C_CNTRL ( 1) /* control character */ #define C_ALPHA ( 64+32 ) /* alphabetic */ #define C_UPPER ( 64 ) /* upper case */ #define C_LOWER ( 32 ) /* lower case */ #define C_DIGIT ( 16 ) /* digit */ #define C_XDIGI ( 16+8 ) /* base 16 digit */ #define C_ALNUM ( 64+32+16 ) /* alpha or numeric */ #define C_SPACE ( 4 ) /* white space */ #define C_PUNCT ( 2 ) /* punctuation */ char _ctype[128] = { 1, 1, 1, 1, 1, 1, 1, 1, /* nul soh stx etx eot enq ack bel */ 1, 5, 5, 1, 1, 5, 1, 1, /* es ht lf vt ff cr so si */ 1, 1, 1, 1, 1, 1, 1, 1, /* dle ^q ^r ^s ^t nak syn etb */ 1, 1, 1, 1, 1, 1, 1, 1, /* can em sub esc fs gs rs us */ 4,130,130,128,128,128,128,130, /* sp ! " # $ % & ' */ 130,130,128,128,130,130,130,128, /* ( ) * + , - . / */ 16, 16, 16, 16, 16, 16, 16, 16, /* 0 1 2 3 4 5 6 7 */ 16, 16,130,130,128,128,128,130, /* 8 9 : ; < = > ? */ 128, 72, 72, 72, 72, 72, 72, 64, /* \@ A B C D E F G */ 64, 64, 64, 64, 64, 64, 64, 64, /* H I J K L M N O */ 64, 64, 64, 64, 64, 64, 64, 64, /* P Q R S T U V W */ 64, 64, 64,128,128,128,128,128, /* X Y Z [ \ ] ^ _ */ 130, 40, 40, 40, 40, 40, 40, 32, /* ` a b c d e f g */ 32, 32, 32, 32, 32, 32, 32, 32, /* h i j k l m n o */ 32, 32, 32, 32, 32, 32, 32, 32, /* p q r s t u v w */ 32, 32, 32,128,128,128,128, 1}; /* x y z { | } ~ del */ /* * file blocks: * * These are the center of file activity. * 'stdin', 'stdout', and 'stderr' are dedicated. */ char _fbin[FB_]; /* standard input (first!!) */ char _fbout[FB_]; /* standard output */ char _fberr[FB_]; /* diagnostic output */ STATIC FILE *_fblocks; /* -> chain of open file blocks */ STATIC FILE *_cfp; /* i/o transfer: -> current file block */ /**** **** smallC MACRO-80 CP/M runtime environment ****/ /* * _shell called from the ?smallC code * sets up environment and calls main() * * exit close all files and terminate program execution. * * BUG: more than 19 arguments cause great trouble... * fewer waste space. */ _shell() { char *cp, *fnp, ch; int argc, argv[20]; int *wp; /* for casting */ wp = _end+1 & ~1; /* even */ *wp = wp+1; /* blind heap element */ *++wp = NULL; /* terminal heap element */ _fblocks = NULL; /* no open file block */ _fbin[FB_FLG] = 0; /* stdio closed */ _fbout[FB_FLG] = 0; _fberr[FB_FLG] = 0; freopen("con:", "r", stdin); freopen("con:", "w", stdout); freopen("con:", "w", stderr); argv[0] = "*"; /* argument vector passed at 0x81 */ for (cp = 129, argc=1; ch = *cp++; ) switch(ch) { case '\'': cp = mkarg(argv[argc++] = cp, '\''); continue; case '\"': cp = mkarg(argv[argc++] = cp, '\"'); continue; case '>': if (*cp == '>') { ch = '+'; /* append */ cp++; } case '<': while (isspace(*cp)) cp++; if (! *cp) { fputs("bad redirect", stderr); _exit(); } cp = mkarg(fnp = cp, 0); switch (ch) { case '<': if (freopen(fnp, "r", stdin) == stdin) continue; break; case '>': if (freopen(fnp, "w", stdout) == stdout) continue; break; case '+': if (freopen(fnp, "a", stdout) == stdout) continue; } fputs("cannot access ", stderr); fputs(fnp, stderr); _exit(); default: if (! isspace(ch)) cp = mkarg(argv[argc++] = cp-1, 0); continue; } argv[argc] = NULL; main(argc,argv); exit(); } exit() { while (_fblocks) fclose(_fblocks); fclose(stdin); fclose(stdout); fclose(stderr); _exit(); } /**** **** UN*X compatible dynamic memory allocation ****/ /* * calloc return pointer to vector of 0, or NULL * cfree free previously allocated area * * The heap starts at _end and runs upward toward the stack. * Each area in the heap is preceded by a word at an even address; * a pointer chain runs from _end through these words to NULL; * The low bit in each word is 1 if the following area is free. * There is a blind, allocated element at the front of the chain. * * BUG: very unreasonable demands (e.g., wraparound) * will corrupt memory. */ #define SLACK 1024 /* at least 1KB stack to be free */ CHAR_P calloc(n,len) int n; /* number of elements */ int len; /* length of element */ { int cell; /* current allocation chain cell */ char *p; /* -> cell */ char *np; /* pointer in cell */ int *ip, *wp; /* for casting */ len = (len*n + 1) & ~1; /* even */ if (len == 0) return NULL; for (ip = p = word(_end+1 & ~1) & ~1; np = (cell = *ip) & ~1; ip = p = np) if (cell & 1) /* lowbit == 1 means free */ { if ((n = np-p - 2) > len+2) { wp = p + len+2; *wp = cell; *ip = wp; } else if (n >= len) *ip = np; else continue; for (wp = p+2; len; len -= 2) *wp++ = 0; return p+2; } if ((wp = p + len+2) > &n - SLACK) return NULL; *ip = wp; *wp = NULL; for (wp = p+2; len; len -= 2) *wp++ = 0; return p+2; } cfree(fp) int *fp; /* to be freed */ { int *p, *np; --fp; /* to cell */ for (p = _end+1 & ~1; np = word(p) & ~1; p = np) /* p-> previous cell */ if (np == fp) /* fp-> cell to free */ { np = *fp; /* np-> following cell */ if ((*fp & 1) || np == NULL) break; /* he does not own it */ if (*p & 1) if (*np & 1) *p = *np; else if (*np == NULL) *p = NULL; else { *p = np; *p |= 1; } else if (*np & 1) *fp = *np; else if (*np == NULL) *fp = NULL; else *fp |= 1; return; } fputs("cfree botch", stderr); _exit(); } /**** **** UN*X compatible character functions ****/ /* * character type functions: * * isascii(i) i is ASCII character * isupper(c) c is upper case * islower(c) c is lower case * isalnum(c) c is alphabetic or digit * isspace(c) c is white space */ INT isascii(i) int i; { return i >= 0 && i < 128; } INT isupper(c) char c; { return _ctype[c] & C_UPPER; } INT islower(c) char c; { return _ctype[c] & C_LOWER; } INT isalnum(c) char c; { return _ctype[c] & C_ALNUM; } INT isspace(c) char c; { return _ctype[c] & C_SPACE; } /* * character conversion functions: * * tolower(u) return lower case version of u * toupper(l) return upper case version of l */ CHAR tolower(u) char u; { return u + 'a'-'A'; } CHAR toupper(l) char l; { return l + 'A'-'a'; } /**** **** UN*X compatible string functions ****/ /* * strcmp 0 if `a' == `b' * <0 if `a' < `b' * >0 if `a' > `b' */ INT strcmp(a,b) char *a, *b; { while (*a == *b && *a) a++, b++; return *a - *b; } /**** **** routines to approximate C features ****/ /* * word(&i) return word */ INT word(wp) int *wp; { return *wp; } /**** **** CP/M utility routines ****/ /* * mkarg massage argument text, return -> unused char * * convert upper case to lower, unless preceded by \ * terminate on stop character or white space, * append NUL to resulting text. * * mkdrive(&c) return 0.. for A.. * mkfield(&c,i,&c) upper-case, copy, pad, return -> next * mkfilename(&c,&c) fix-format filename, return success * mkfcb(&c,&c) init fcb with file name, return success */ CHAR_P mkarg(str, stop) char *str; /* -> (first) result character */ char stop; /* 0: space, other: terminator */ { char *cp, ch; for(cp = str; ; ) { switch (ch = *cp++) { case NUL: if (stop == 0) { *str = NUL; return cp-1; } fputs("missing ", stderr); fputc(stop, stderr); _exit(); case '\\': if (*cp) { *str++ = *cp++; continue; } fputs("trailing \\", stderr); _exit(); } if (ch == stop || isspace(ch) && stop == 0) { *str = NUL; return cp; } if (isupper(ch)) *str++ = tolower(ch); else *str++ = ch; } } INT mkdrive(cp) char *cp; { if (isascii(*cp)) if (isupper(*cp)) return *cp - 'A'; else if (islower(*cp)) return *cp - 'a'; return ERR; } CHAR_P mkfield(f,l,s) /* copy isalnum() */ char *f; /* upper-cased to this buffer */ int l; /* blank padded to this length */ char *s; /* from this string */ { do if (isascii(*s) && isalnum(*s)) if (islower(*s)) *f++ = toupper(*s++); else *f++ = *s++; else break; while (--l); while (l--) *f++ = ' '; return s; } INT mkfilename(fnb, fnm) char fnb[16]; /* to be filled */ char *fnm; /* with this file name */ { int i; if (fnm[1] == ':') { if ((i = mkdrive(fnm)) == ERR) return ERR; fnb[FCB_ET] = i+1; fnm += 2; } else /* make sure to set current drive */ fnb[FCB_ET] = _drive()+1; fnm = mkfield(fnb+FCB_FN,8,fnm); if (*fnm == '.') fnm = mkfield(fnb+FCB_FT,3,fnm+1); else for (i=0; i<3; i++) fnb[FCB_FT+i] = ' '; if (*fnm != NUL) return ERR; return NULL; } INT mkfcb(fcb,fnm) char fcb[FCB_]; /* to be initialized */ char *fnm; /* with this file name */ { int i; for (i=0; iFB, need not be open */ { int i; /* # in driver table */ int f; /* for function call */ int *wp; /* for casting */ if (fp[FB_FLG] & FB_OPF && fclose(fp) == EOF) return NULL; /* cannot close */ if (fnm[0] && fnm[1] && fnm[2] && fnm[3] == ':' && fnm[4] == NUL) { mkfield(fp+FCB_FN,3,fnm); /* upper-case */ fp[FCB_FN+3] = NUL; /* terminate */ for(i=1; ;i++) if (_dnm[i] == 0) return NULL; /* unknown device */ else if (strcmp(fp+FCB_FN, _dnm[i]) == 0) break; } else if (mkfcb(fp, fnm) == ERR) return NULL; /* bad file name */ else i = 0; fp[FB_DRV] = i; /* point to driver */ fp[FCB_OV] = 0; /* set record 0 */ wp = fp + FCB_RR; *wp = 0; wp = fp + FB_NCP; /* set buffer empty */ *wp = fp + FB_BUF; switch (*mode) { case 'r': if (_drd[i] == NULL) return NULL; /* illegal to read */ fp[FB_FLG] = FB_OPF; break; case 'a': case 'w': if (_dwr[i] == NULL) return NULL; /* illegal to write */ fp[FB_FLG] = FB_OPF|FB_OUF; break; default: return NULL; /* illegal mode */ } if (f = _dop[i]) return (f)(fp,mode); return fp; /* no special open */ } INT fclose(fp) FILE *fp; { int f; /* for function call */ FILE *p; int *wp; /* for casting */ if ((fp[FB_FLG] & FB_OPF) == 0) return EOF; /* not open */ fp[FB_FLG] &= ~FB_OPF; /* reset open flag */ if (f = _dcl[fp[FB_DRV]]) f = (f)(fp); /* f is NULL or return */ if (fp == _fblocks) _fblocks = word(fp+FB_NXT); else { p = _fblocks; while (p) { wp = p+FB_NXT; if (fp == *wp) { *wp = word(fp+FB_NXT); break; } p = *wp; } if (p == NULL) return f; /* stdio ?? */ } cfree(fp); return f; } /**** **** UN*X compatible i/o transfer routines for CP/M ****/ /* * character i/o: * * fputc(ch,fp) to fp * map '\n' to RETURN LINEFEED, * return EOF on hard error. */ INT fputc(ch,fp) char ch; FILE *fp; { int f; /* to cast a function call */ if ((fp[FB_FLG] & FB_OUF) == 0 || (fp[FB_FLG] & FB_OPF) == 0) { fputs("writing bad file", stderr); _exit(); } if (fp[FB_FLG] & FB_EOF) return EOF; /* hard end of file */ _cfp = fp; /* pass to _fputchar */ f = _dwr[fp[FB_DRV]]; if (ch == '\n') /* map '\n' to RETURN LINEFEED */ { (f)(CR); (f)(LF); } else (f)(ch); if (fp[FB_FLG] & (FB_EOF | FB_ERM)) return EOF; return ch; } /* * other i/o: * * fputs string to fp * * return NULL on error, else string. */ CHAR_P fputs(s, fp) char *s; FILE *fp; { char ch; char *str; str = s; while (ch = *s++) if (fputc(ch, fp) == EOF) return NULL; return str; } /**** **** CP/M drivers to execute i/o operations ****/ /* * general routines: * * _binit clear buffer * _bgetchar return one character from buffer * _bputchar enter one character to buffer * _ngetchar return EOF * _nop do nothing (accept anything) * * BUG: assumes at most 16-bit record address * i.e., FCB_OV is not used. */ _binit(cp) /* initialize buffer */ char *cp; { int len; for (len = SLEN; len--; ) *cp++ = SUB; /* to end of file character */ } INT _bgetchar() /* use _cfp */ { int *wp; /* for casting */ char *cp; /* for casting */ int f; /* for function call */ wp = _cfp+FB_NCP; if (*wp >= _cfp + FB_BUF+SLEN) { wp = _cfp+FCB_RR; ++*wp; /* next record */ f = _dbr[_cfp[FB_DRV]]; if ((f)(_cfp)) return EOF; wp = _cfp+FB_NCP; *wp = _cfp+FB_BUF; /* at begin */ } cp = (*wp)++; return *cp & 255; } _bputchar(ch) /* use _cfp */ char ch; { int *wp; /* for casting */ char *cp; /* for casting */ int f; /* for function call */ wp = _cfp+FB_NCP; if (*wp >= _cfp + FB_BUF+SLEN) { f = _dbw[_cfp[FB_DRV]]; if ((f)(_cfp)) return EOF; wp = _cfp+FCB_RR; ++*wp; wp = _cfp+FB_NCP; _binit(*wp = _cfp+FB_BUF); } cp = (*wp)++; *cp = ch; } INT _ngetchar() { _cfp[FB_FLG] |= FB_EOF; return EOF; } _nop() { } %%%%%%%%%% scc/rtl/def.h %%%%%%%%%% /* * def.h -- definitions for smallC runtime support * ats 3/83, in part adapted from Jim Hendrix' code */ /* * return types for functions */ #define STATIC /* object is internal */ #define INT /* int */ #define CHAR /* char */ #define CHAR_P /* char * */ #define FILE_P /* FILE * */ /* * constants */ #define NULL 0 /* null pointer */ #define NUL 0 /* nul character */ #define EOF (-1) /* end of file */ #define ERR (-2) /* error return a la Hendrix */ /* * file management */ #define FILE char /* file pointer is char * */ #define stdin (_fbin) /* elsewhere, must be addresses */ #define stdout (_fbout) #define stderr (_fberr) /* * CP/M related definitions */ #define EOT 4 /* ^D marks end of file at console */ #define LF 10 /* line feed */ #define CR 13 /* carriage return */ #define SUB 26 /* ^Z marks end of text */ #define SLEN 128 /* sector length */ #define LSLEN 7 /* log2 of SLEN */ /* /* offset to... */ #define DHD_ 16 /* next disk header block */ #define DHD_XLT 0 /* -> translate table */ #define DHD_DBF 8 /* -> directory buffer */ #define DHD_DPB 10 /* -> disk parameter block */ #define DHD_CST 12 /* -> directory checksum table */ #define DHD_RBR 14 /* -> allocation table */ #define DPB_ 15 /* next disk parameter block */ #define DPB_SPT 0 /* sectors per track */ #define DPB_BSH 2 /* block shift */ #define DPB_BLM 3 /* block shift mask */ #define DPB_EXM 4 /* extent mask */ #define DPB_DSM 5 /* disk size - 1 in blocks */ #define DPB_DRM 7 /* directory size - 1 in entries */ #define DPB_ALB 9 /* directory allocation */ #define DPB_CKS 11 /* check area size */ #define DPB_OFF 13 /* offset to first track */ #define FCB_ 36 /* next file control block */ #define FCB_ET 0 /* entry type/drive code/user number */ #define FCB_FN 1 /* file name */ #define FCB_FT 9 /* file type (extension) */ #define FCB_RO 9 /* high bit is read only flag */ #define FCB_SY 10 /* high bit is system file flag */ #define FCB_EX 12 /* file extent */ #define FCB_S1 13 /* 00 */ #define FCB_S2 14 /* system use */ #define FCB_RC 15 /* record count in extent */ #define FCB_DM 16 /* 16 record allocation bytes */ #define FCB_NR 32 /* next record number in this extent */ #define FCB_RR 33 /* random record number */ #define FCB_OV 35 /* RR overflow */ /*define FB_FCB 0 /* FCB is first */ #define FB_FLG (FCB_) /* (char) flags */ #define FB_OPF (1<<7) /* 0: closed, 1: open */ #define FB_OUF (1<<6) /* 0: input, 1: output */ #define FB_EOF (1<<5) /* hard end of file */ #define FB_UNF (1<<4) /* ungetc buffer full */ #define FB_ERM (FB_UNF-1) /* mask to get error code */ #define FB_UNC (FCB_+1) /* (char) ungetc buffer */ #define FB_NXT (FCB_+2) /* (FILE *) -> next file block */ #define FB_DRV (FCB_+4) /* (char) index into driver table */ #define FB_NCP (FCB_+5) /* (char *) -> next character */ #define FB_BUF (FB_NCP+2) /* (char[SLEN]) buffer */ #define FB_ (FB_BUF+SLEN) /* next file block */ %%%%%%%%%% scc/rtl/dtab.c %%%%%%%%%% /* * dtab.c -- driver table with raw disk interface * ats 3/83 */ #include def.h /* TO BE FIXED */ %%%%%%%%%% scc/rtl/end.mac %%%%%%%%%% ; end.mac -- smallC runtime library - last module ; for CP/M ; for MACRO/80 ENTRY _edata ; end of all DSEG ENTRY _eprog ; end of all CSEG ENTRY _end ; end of code and data ENTRY ?30217 ; version reference DSEG ?30217: DB "021783" ; production date _edata: CSEG DB 115,109,97,108,108,67 ; smallC _eprog: _end: END %%%%%%%%%% scc/rtl/fio.c %%%%%%%%%% /* * fio.c -- file driver for CP/M * ats 3/83 */ #define NOCCARGC #include def.h /* TO BE FIXED */ /* * external references */ extern word(), /* CSH /* return word at pointer */ _binit(), /* initialize buffer to EOF character */ _open(), /* BDOS /* connect to existing file */ _romap(), /* return r/o vector */ _setbuf(), /* set DMA address */ _glob(), /* search for first name */ _close(), /* disconnect from file */ _delete(), /* remove file */ _create(), /* make file, connect */ _rread(), /* read random record */ _rzwrite(), /* write random record, zero new block */ _stat(); /* determine file size */ /**** **** CP/M file driver ****/ /* * _fopen connect, get buffer, return given fp or NULL * _fblin input one CP/M sector * _fblout output one CP/M sector * _fseek position to offset, return NULL or -1 * _fclose write last buffer, disconnect, return NULL or EOF * * There is a problem in positioning to end of file, * since CP/M marks this using SUB -- see _fseek. */ FILE_P _fopen(fp,mode) FILE *fp; /* available file block */ char *mode; { int i; switch(*mode) { case 'r': if (_open(fp) >> 2) break; _fblin(fp); /* read first buffer */ return fp; case 'a': case 'w': if (_romap() & 1 << fp[FCB_ET]-1) break; /* disk read only */ _setbuf(fp+FB_BUF); if ((i = _glob(fp)) >> 2 == 0) if (fp[FB_BUF + 32*i + FCB_RO] & 128) break; /* file read only */ else if (*mode == 'a') { if (_open(fp) >> 2) break; /* * issue a trick call to position to * char-EOF without reading first */ if (_fseek(fp, 2, -1,-1, 0,0) == -1) { _close(fp); break; } return fp; } else if (_delete(fp) >> 2) break; /* unable to delete */ if (_create(fp) >> 2) break; /* unable to create */ _binit(fp+FB_BUF); return fp; } fp[FB_FLG] = 0; /* nothing */ return NULL; } INT _fblin(fp) FILE *fp; { int code; _setbuf(fp+FB_BUF); switch(code = _rread(fp)) { case 0: /* read ok */ return NULL; case 1: /* reading unwritten data */ fp[FB_FLG] |= FB_EOF; break; default: /* other error */ fp[FB_FLG] &= ~FB_ERM; fp[FB_FLG] |= code; } return EOF; } INT _fblout(fp) FILE *fp; { int code; _setbuf(fp+FB_BUF); switch(code = _rzwrite(fp)) { case 0: return NULL; case 5: /* directory overflow */ fp[FB_FLG] |= FB_EOF; break; default: /* other error */ fp[FB_FLG] &= ~FB_ERM; fp[FB_FLG] |= code; } return EOF; } INT _fseek(fp,mode,csec,cpos,ssec,spos) FILE *fp; /* to position */ int mode; /* checked to be 0,1,2,8,9,10 */ int csec,cpos; /* current position */ int ssec,spos; /* to be attained, relative for modes 2,10 */ { int *wp; /* for casting */ /* * csec/cpos is current position, * current position was flushed, * now position to end of file * * note that mode==2, csec==-1 will * position to char-EOF without worrying * about buffer contents first. */ _setbuf(fp+FB_BUF); _stat(fp); /* file size to RR */ wp = fp+FCB_RR; switch(mode) { case 0: case 1: case 2: if (*wp == 0) { csec = cpos = 0; wp = fp+FB_NCP; _binit(*wp = fp+FB_BUF); break; } if (--(*wp) != csec) if (_rread(fp)) return -1; /* ouch!!! */ csec = *wp; /* last sector is buffered */ for (cpos = 0; cpos < SLEN; cpos++) if (fp[FB_BUF+cpos] == SUB) break; if (cpos >= SLEN) /* last byte found */ { cpos = 0; *wp = ++csec; wp = fp+FB_NCP; _binit(*wp = fp+FB_BUF); } else { wp = fp+FB_NCP; *wp = fp+FB_BUF + cpos; } if (mode != 2) break; ssec += csec; /* make absolute */ if ((spos += cpos) < 0) { spos += SLEN; --ssec; } else if (spos >= SLEN) { spos -= SLEN; ++ssec; } break; case 10: ssec += *wp; /* make absolute */ spos = 0; /* sector-relative! */ case 8: case 9: csec = *wp; /* buffer end of file */ cpos = 0; wp = fp+FB_NCP; _binit(*wp = fp+FB_BUF); } /* * csec/cpos is end of file * buffer contents reflect this * now position to ssec/spos (absolute) */ if (ssec < 0 || ssec > csec || ssec == csec && spos > cpos) return -1; /* not inside file */ if (ssec != csec) /* i.e., before EOF */ { wp = fp+FCB_RR; *wp = ssec; if (_rread(fp)) return -1; /* ouch!!! */ } wp = fp+FB_NCP; *wp = fp+FB_BUF + spos; return 0; } INT _fclose(fp) FILE *fp; { int result; if (fp[FB_FLG] & FB_OUF && word(fp+FB_NCP) != fp+FB_BUF) result = _fblout(fp); else result = 0; if (_close(fp) >> 2 || result) return EOF; return NULL; } %%%%%%%%%% scc/rtl/ftab.c %%%%%%%%%% /* * ftab.c -- driver table without raw disk interface * ats 3/83 */ #include def.h /* TO BE FIXED */ #define FTAB /**/ /* signal to io.h */ %%%%%%%%%% scc/rtl/io.h %%%%%%%%%% /* * io.h -- disk and file driver tables for CP/M * ats 3/83 */ /* * define... * * FTAB to create a driver table without the raw disk interface. */ /* * C.REL: CRTL DTAB CSH FTAB FIO ... END * * The first external reference to the driver table * is (officially) in CSH, thus FTAB is the default table. * If the user's program contains a reference like * `extern _dopen();' DTAB will instead be included. */ #define NOCCARGC /* * external references */ extern mkdrive(), /* CSH /* convert letter to drive # */ word(), /* return word from pointer */ _binit(), /* set buffer to EOF character */ _bgetchar(), /* read byte from buffer */ _bputchar(), /* write byte to buffer */ _ngetchar(), /* set+return end of file */ _nop(), /* do nothing */ _fopen(), /* FIO /* complete open */ _fblin(), /* read buffer */ _fblout(), /* write buffer */ _fseek(), /* seek */ _fclose(), /* complete close */ _getchar(), /* BDOS /* console read (+echo) */ _rgetchar(), /* reader read */ _putchar(), /* console write (+tab) */ _pputchar(), /* punch write */ _lputchar(), /* printer write */ _romap(); /* return r/o vector */ #ifndef FTAB extern _seldsk(), /* BIOS /* select disk drive */ _sectran(), /* translate sector address */ _settrk(), /* set track */ _setsec(), /* set sector */ _setdma(), /* set DMA address */ _sread(), /* read sector */ _swrite(); /* write sector */ /* special argument to... */ #define SELDSK2 0 /* _seldsk: ignored by Osborne CBIOS */ #define SWRITE 0 /* _swrite: regular write */ #endif /**** **** driver table ****/ /* * _dnm permissible device names, 0-terminated * _dop open &func, NULL if none needed * _drd read byte &func, NULL if not allowed * _dwr write byte &func, NULL if not allowed * _dbr block read &func, NULL if read byte unbuffered * _dbw block write &func, NULL if write byte unbuffered * _dsk seek &func, NULL if not permitted * _dcl close &func, NULL if none needed * * File manager is entry 0, other entries are devices. * Device names are known (in freopen) to be 3 letters. * Disk names are known (in _dopen) to have a legal and * existing drive name as third character. * * con: read/write console, as set by i/o byte * rdr: read reader, as set by i/o byte * pun: write punch, as set by i/o byte * lst: write list device, as set by i/o byte * nul: EOF on read, no operation on write * dka: disk A: under BIOS * dkb: disk B: under BIOS */ /* * Due to bugs in smallC, this is done in assembler. char *_dnm[] ={"", "CON", "RDR", "PUN", "LST", "NUL", "DKA", "DKB", 0}; int (*_dop[])() ={_fopen,NULL, NULL, NULL, NULL, NULL, _dopen,_dopen }; int (*_drd[])() ={_bgetc,_getch,_rgetc,NULL, NULL, _ngetc,_bgetc,_bgetc }; int (*_dwr[])() ={_bputc,_putch,NULL, _pputc,_lputc,_nop, _bputc,_bputc }; int (*_dbr[])() ={_fblin,NULL, NULL, NULL, NULL, NULL, _dblin,_dblin }; int (*_dbw[])() ={_fblou,NULL, NULL, NULL, NULL, NULL, _dblou,_dblou }; int (*_dsk[])() ={_fseek,NULL, NULL, NULL, NULL, NULL, _dseek,_dseek }; int (*_dcl[])() ={_fclos,NULL, NULL, NULL, NULL, NULL, NULL, NULL }; * */ STATIC int _dnm[1]; #asm DSEG ORG _dnm DW ..1 DW ..2 DW ..3 DW ..4 DW ..5 DW ..6 #endasm #ifndef FTAB #asm DW ..7 DW ..8 #endasm #endif #asm DW 0 ..2: DB 'CON' ..1: DB 0 ..3: DB 'RDR',0 ..4: DB 'PUN',0 ..5: DB 'LST',0 ..6: DB 'NUL',0 #endasm #ifndef FTAB #asm ..7: DB 'DKA',0 ..8: DB 'DKB',0 #endasm #endif STATIC int _dop[1]; #asm DSEG ORG _dop DW _fopen DW 0 DW 0 DW 0 DW 0 DW 0 #endasm #ifndef FTAB #asm DW _dopen DW _dopen #endasm #endif int _drd[1]; #asm DSEG ORG _drd DW _bgetcha DW _getchar DW _rgetcha DW 0 DW 0 DW _ngetcha #endasm #ifndef FTAB #asm DW _bgetcha DW _bgetcha #endasm #endif int _dwr[1]; #asm DSEG ORG _dwr DW _bputcha DW _putchar DW 0 DW _pputcha DW _lputcha DW _nop #endasm #ifndef FTAB #asm DW _bputcha DW _bputcha #endasm #endif STATIC int _dbr[1]; #asm DSEG ORG _dbr DW _fblin DW 0 DW 0 DW 0 DW 0 DW 0 #endasm #ifndef FTAB #asm DW _dblin DW _dblin #endasm #endif STATIC int _dbw[1]; #asm DSEG ORG _dbw DW _fblout DW 0 DW 0 DW 0 DW 0 DW 0 #endasm #ifndef FTAB #asm DW _dblout DW _dblout #endasm #endif int _dsk[1]; #asm DSEG ORG _dsk DW _fseek DW 0 DW 0 DW 0 DW 0 DW 0 #endasm #ifndef FTAB #asm DW _dseek DW _dseek #endasm #endif STATIC int _dcl[1]; #asm DSEG ORG _dcl DW _fclose DW 0 DW 0 DW 0 DW 0 DW 0 #endasm #ifndef FTAB #asm DW _dclose DW _dclose #endasm #endif #ifndef FTAB /**** **** CP/M raw disk driver ****/ /* * _dopen connect, get buffer, return given fp or NULL * _dblin input one CP/M sector * _dblout output one CP/M sector * _dseek position to offset, return NULL or -1 * _dclose write last buffer, disconnect, return NULL or EOF * * BUGS: <= 127 sectors/track, <=127 first track, * sectors before first directory track are not translated, * <= 32767 sectors/disk, * _dseek only knows sector-EOF */ #define DSK_DR FCB_ET /* drive #, 0==A, ... */ #define DSK_NM (FCB_FN+2) /* (existing) drive name */ #define DSK_ST FCB_S1 /* sectors/track */ #define DSK_FT FCB_S2 /* first directory track */ #define DSK_XL FCB_DM /* (word) -> translate table */ FILE_P _dopen(fp,mode) FILE *fp; /* available file block */ char *mode; { int *wp; /* for casting */ char *cp; /* for casting */ int i; if ((cp = _seldsk(fp[DSK_DR] = mkdrive(fp+DSK_NM), SELDSK2)) != NULL) { wp = fp+DSK_XL; *wp = word(cp+DHD_XLT); cp = word(cp+DHD_DPB); fp[DSK_ST] = word(cp+DPB_SPT); fp[DSK_FT] = word(cp+DPB_OFF); switch(*mode) { case 'r': if (_dblin(fp)) break; return fp; case 'w': if (_romap() & 1 << fp[DSK_DR]) break; _binit(fp+FB_BUF); return fp; } } fp[FB_FLG] = 0; /* nothing */ return NULL; } STATIC INT _dblio(fp) /* setup for disk i/o */ FILE *fp; { int track, sector; if (_seldsk(fp[DSK_DR], SELDSK2) == NULL) { fp[FB_FLG] &= ~FB_ERM; fp[FB_FLG] |= 1; /* BUG, really */ return EOF; } sector = word(fp+FCB_RR); if ((track = sector/fp[DSK_ST]) >= fp[DSK_FT]) sector = _sectran(sector % fp[DSK_ST], word(fp+DSK_XL)); else sector %= fp[DSK_ST]; _settrk(track); _setsec(sector); _setdma(fp+FB_BUF); return NULL; } INT _dblin(fp) FILE *fp; { if (_dblio(fp)) return EOF; if (_sread()) { fp[FB_FLG] |= FB_EOF; return EOF; } return NULL; } INT _dblout(fp) FILE *fp; { if (_dblio(fp)) return EOF; if (_swrite(SWRITE)) { fp[FB_FLG] |= FB_EOF; return EOF; } return NULL; } INT _dseek(fp,mode,csec,cpos,ssec,spos) FILE *fp; /* to position */ int mode; /* checked to be 0,1,2,8,9,10 */ int csec,cpos; /* current position */ int ssec,spos; /* to be attained, relative for modes 2,10 */ { int *wp; /* for casting */ char *cp; /* for casting */ /* * csec/cpos is current position, * current position was flushed, * now position to end of drive */ if ((cp = _seldsk(fp[DSK_DR], SELDSK2)) == NULL) return -1; /* unknown drive */ wp = fp+FCB_RR; /* set RR */ cp = word(cp+DHD_DPB); /* point to DPB */ *wp = csec = (cp[DPB_BLM]+1) /* sectors/block */ * (word(cp+DPB_DSM)+1) /* blocks/disk */ + word(cp+DPB_OFF) /* system tracks */ * word(cp+DPB_SPT); /* sectors/track cpos = 0; wp = fp+FB_NCP; /* empty buffer */ _binit(*wp = fp+FB_BUF); switch(mode) { case 10: spos = 0; /* sector-relative */ case 2: ssec += csec; /* make absolute */ break; } /* * csec/cpos is end of file * buffer contents reflect this * now position to ssec/spos (absolute) */ if (ssec < 0 || ssec > csec || ssec == csec && spos > cpos) return -1; /* not inside file */ if (ssec != csec) /* i.e., before EOF */ { wp = fp+FCB_RR; *wp = ssec; if (_dblin(fp)) return -1; /* ouch!!! */ } wp = fp+FB_NCP; *wp = fp+FB_BUF + spos; return 0; } INT _dclose(fp) FILE *fp; { if (fp[FB_FLG] & FB_OUF && word(fp+FB_NCP) != fp+FB_BUF) return _dblout(fp); return NULL; } #endif /* ndef FTAB */ %%%%%%%%%% scc/rtl/make.sub %%%%%%%%%% ; smallC runtime library ; ; c?: source($1) object($2) ; get($3) smallC($4) ; macro-80($5) lib-80($6) ; ; ats 2/83 ; ; mkwfield needs is* ; $3 #$$a <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp1=$1:tmp ; ; mkwfilename needs mkwfield ; $3 #$$b <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp2=$1:tmp ; ; mkwfcb needs mkwfilename ; $3 #$$c <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp3=$1:tmp ; ; dumpbit needs putbit printf ; $3 #d <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp4=$1:tmp ; ; dumpdpb needs putchar printf byte ; $3 #e <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp5=$1:tmp $6 $2:clib=$2:tmp1,$2:tmp2,$2:tmp3,$2:tmp4,$2:tmp5/e ; ; dumpfcb needs putchar printf byte ; $3 #f <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp1=$1:tmp ; ; feof ; $3 #$$g <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp2=$1:tmp ; ; ferror ; $3 #$$h <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp3=$1:tmp ; ; clearerr ; $3 #$$i <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp4=$1:tmp ; ; fseek ; $3 #$$j <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp5=$1:tmp $6 $2:tmp=$2:clib,$2:tmp1,$2:tmp2,$2:tmp3,$2:tmp4,$2:tmp5/e era $2:clib.rel ren $2:clib.rel=$2:tmp.rel ; ; rewind needs fseek ; $3 #$$k <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp1=$1:tmp ; ; getchar needs fgetc ; $3 #$$m <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp3=$1:tmp ; ; fgetc needs ungetc ; $3 #$$n <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp4=$1:tmp ; ; ungetc ; $3 #$$o <$1:c0.get >$1:tmp.c $4 $1:tmp.c >$1:tmp.mac $5 $2:tmp5=$1:tmp $6 $2:tmp=$2:clib,$2:tmp1,$2:tmp3,$2:tmp4,$2:tmp5/e era $2:clib.rel ren $2:clib.rel=$2:tmp.rel submit 2 $1 $2 $3 $4 $5 $6 %%%%%%%%%% scc/rtl/printf.c %%%%%%%%%% /* * printf.c -- smallC runtime library for CP/M and MACRO-80 * UN*X compatible printf routines * ats 2/83, adapted from Jim Hendrix' code * rev (treat %??c like %??s) ats 3/83 */ #define NOCCARGC #include def.h /* TO BE FIXED */ /* * globally external things (in csh) */ extern char _fbin[]; /* stdin */ extern char _fbout[]; /* stdout */ extern char _fberr[]; /* stderr */ extern _narg(); /* returns # arguments */ extern abort(); /* terminate on output error */ extern fputc(), fputs(); /* output to file */ extern isdigit(); /* character class */ /* * local definitions */ #define SZ 7 /* output item size */ /* * _pfemit emit one character */ STATIC char *_pfstr; /* sprintf: -> string */ STATIC _pfemit(c, fp) char c; FILE *fp; { if (fp == &_pfstr) *_pfstr++ = c; /* sprintf */ else if (fputc(c, fp) == EOF) { fputs("output error", stderr); abort(); } } /* * _itod nbr to signed decimal string * _itou nbr to unsigned decimal string * _itox nbr to hex string * * width SZ, right adjusted, blank filled, terminated with null byte */ STATIC _itod(nbr, str) int nbr; char str[]; { char sgn; int sz; sz = SZ; if (nbr < 0) { nbr = -nbr; sgn = '-'; } else sgn = ' '; str[--sz] = NUL; while(sz) { str[--sz] = nbr % 10 + '0'; if ((nbr /= 10) == 0) break; } if (sz) str[--sz] = sgn; while (sz > 0) str[--sz] = ' '; } STATIC _itou(nbr, str) int nbr; char str[]; { int lowbit; int sz; sz = SZ; str[--sz] = NUL; while (sz) { lowbit = nbr & 1; nbr = (nbr >> 1) & 32767; /* divide by 2 */ str[--sz] = ((nbr % 5) << 1) + lowbit + '0'; if ((nbr /= 5) == 0) break; } while (sz) str[--sz] = ' '; } STATIC _itox(nbr, str) int nbr; char str[]; { int digit, offset; int sz; sz = SZ; str[--sz] = NUL; while (sz) { digit = nbr & 15; nbr = (nbr >> 4) & 4095; if (digit < 10) offset = '0'; else offset = 'A'-10; str[--sz] = digit + offset; if (nbr == 0) break; } while (sz) str[--sz] = ' '; } /* * _utoi convert unsigned decimal string to integer nbr * returns field size, else ERR on error */ STATIC INT _utoi(decstr, nbr) char *decstr; int *nbr; { int d,t; d = 0; *nbr = 0; while (isdigit(*decstr)) { t = *nbr; t = (10*t) + (*decstr++ - '0'); if (t >= 0 && *nbr < 0) return ERR; d++; *nbr = t; } return d; } /* * _printf do the actual formatting */ STATIC _printf(fp, nxtarg) FILE *fp; /* may be &_pfstr */ int *nxtarg; /* -> format */ { int i, width, prec, preclen, len; char *ctl, *cx, c, right, str[SZ], *sptr, pad; ctl = *nxtarg; while (c = *ctl++) { if (c != '%') { _pfemit(c, fp); continue; } if (*ctl == '%') { _pfemit(*ctl++, fp); continue; } cx = ctl; if (*cx == '-') { right = 0; ++cx; } else right = 1; if (*cx == '0') { pad = '0'; ++cx; } else pad = ' '; if ((i = _utoi(cx, &width)) >= 0) cx += i; else continue; if (*cx == '.') { if ((preclen = _utoi(++cx, &prec)) >= 0) cx += preclen; else continue; } else preclen = 0; sptr = str; i = *--nxtarg; switch (c = *cx++) { case 'c': str[0] = i; str[1] = NUL; break; case 'd': _itod(i, str); break; case 's': sptr = i; break; case 'u': _itou(i, str); break; case 'x': _itox(i, str); break; default: continue; } ctl=cx; /* accept conversion spec */ if (c != 's' && c != 'c') while (*sptr == ' ') ++sptr; len = -1; while (sptr[++len]) ; /* get length */ if ((c == 's' || c == 'c') && len > prec && preclen > 0) len = prec; if (right) while (width-- - len > 0) _pfemit(pad, fp); while (len) { _pfemit(*sptr++, fp); --len; --width; } while (width-- - len > 0) _pfemit(pad, fp); } } /* * printf(format [, arg, ...] ) * fprintf(fp, format [, arg, ...] ) * sprintf(string, format [, arg, ...] ) * * as described by Kernighan and Ritchie * support % [-] [0] [width] [. precision] (c|d|s|u|x) * uses _narg() feature */ printf(argc) int argc; { int i; i = _narg(); _printf(stdout, &argc + i-1); } fprintf(argc) int argc; { int i, *wp; i = _narg(); wp = &argc + i-1; _printf(*wp, wp-1); } sprintf(argc) int argc; { int i, *wp; i = _narg(); wp = &argc + i-1; _pfstr = *wp; _printf(&_pfstr, wp-1); } %%%%%%%%%% end of part 2 %%%%%%%%%%