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