smallC V2 CP/M runtime support - (nf)
utzoo!decvax!harpo!npoiv!npois!wbux5!wb2!houxz!ihnp4!ixn5c!inuxc!pur-ee!uiucdcs!schrein
utzoo!decvax!harpo!npoiv!npois!wbux5!wb2!houxz!ihnp4!ixn5c!inuxc!pur-ee!uiucdcs!schrein
Sun Mar 13 22:45:03 AEST 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; i<FCB_; i++)
fcb[i] = NUL;
return mkfilename(fcb,fnm);
}
/****
**** UN*X compatible file management for CP/M
****/
/*
* file management routines:
*
* fopen connect to file or device, return fp, or NULL
* freopen change connection, return given fp, or NULL
* fclose disconnect, return NULL, or EOF
*
* BUG: < 1<<16 sectors(records) per file
*/
FILE_P fopen(fnm,mode)
char *fnm; /* CP/M logical device or file name */
char *mode; /* "r", "w", or "a" */
{ FILE *fp;
int *wp; /* for casting */
if ((fp = calloc(1, FB_)) == NULL)
return NULL;
wp = fp+FB_NXT; /* link new block in */
*wp = _fblocks;
_fblocks = fp;
return freopen(fnm, mode, fp);
}
FILE_P freopen(fnm,mode,fp)
char *fnm; /* CP/M logical device or file name */
char *mode; /* "r", "w", or "a" */
FILE *fp; /* -> FB, 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 %%%%%%%%%%
More information about the Comp.sources.unix
mailing list