/**
 * @file   mescc.h
 * @brief  Runtime library.
 * @author Miguel I. Garcia Lopez / FloppySoftware
 *
 * Runtime library for MESCC (Mike's Enhanced
 * Small C Compiler for Z80 & CP/M).
 *
 * This library file must be included first!
 *
 * Need following EQU's (generated by the compiler):
 *  - ccSTACKSIZE : Stack size in bytes.
 *
 * Supports following #defs:
 *  - #define CC_STDIO     Support for stdin, stdout & stderr.
 *  - #define CC_REDIR     Support for stdin & stdout redirection
 *                         in command line (needs CC_STDIO).
 *  - #define CC_NO_MUL    To exclude MULTIPLICATION code.
 *  - #define CC_NO_DIV    To exclude DIVISION & MODULUS code.
 *  - #define CC_NO_SWITCH To exclude SWITCH code.
 *  - #define CC_NO_ARGS   To exclude ARGC & ARGV code.
 *  - #define CC_NO_ORG    To exclude ORG 0100H code.
 *
 * Sets the following #defines:
 *
 *  - BYTE
 *  - WORD
 *  - BOOL
 *  - NULL
 *  - TRUE
 *  - FALSE
 *  - SIZEOF_CHAR
 *  - SIZEOF_INT
 *  - SIZEOF_PTR
 *
 * Revisions:
 *  - 16 Jan 2001 : Last revision.
 *  - 23 Mar 2007 : Expand ccladr1 and ccladr2 for more speed.
 *  - 16 Apr 2007 : GPL'd.
 *  - 26 Aug 2012 : Added standard defs.
 *  - 08 Dec 2014 : Minor changes.
 *  - 09 Dec 2014 : Added support for stdin, stdout & stderr with CC_STDIO.
 *  - 12 Dec 2014 : Added support for stdin & stdout redirection in command line with CC_REDIR.
 *  - 16 Jan 2015 : Added SIZEOF_??? definitions.
 *  - 16 Feb 2015 : Modified / added code in cctmpw, ccxpb2, ccxpb, ccxpb3, ccxpw2
 *                  ccxpw, ccxpw3, ccladr2sv, ccladr2, ccladr1sv, ccladr1,
 *                  to avoid use of IX register.
 *  - 20 Mar 2015 : Added support for CC_NO_MUL, CC_NO_DIV, CC_NO_SWITCH, CC_NO_ARGS.
 *  - 12 Apr 2015 : Removed ccDEFARGS code.
 *  - 14 Jul 2015 : Modified code for << and >>, because a shift of 0 positions,
 *                  resulted in a wrong value (they assumed a shift > 0) - ie: 128 >> 0 resulted in 0.
 *  - 19 Oct 2015 : Improved multiplication algorithm (ccmul & ccumul).
 *  - 05 Nov 2015 : Modified ccsxt.
 *  - 30 Nov 2015 : Added support for atexit().
 *  - 24 Jan 2016 : Added support for CC_NO_ORG.
 *  - 10 Dec 2016 : Documented. GPL v3.
 *
 * Copyright (c) 1999-2016 Miguel I. Garcia Lopez / FloppySoftware.
 *
 * Licensed under the GNU General Public License v3.
 *
 * http://www.floppysoftware.es
 * floppysoftware@gmail.com
 */

/* STANDARD DEFs
   -------------
*/

#define BYTE	unsigned char
#define WORD	unsigned int
#define BOOL	char
#define NULL	0
#define TRUE	1
#define FALSE	0

#define SIZEOF_CHAR 1 /* [unsigned] char */
#define SIZEOF_INT  2 /* [unsigned] int */
#define SIZEOF_PTR  2 /* pointer */

/* RUNTIME CODE
   ------------
*/

#ifndef CC_NO_ORG

#asm
;	Start at TPA

	ORG	0100H

#endasm

#endif

#asm
;	Runtime address

ccrtadr:

;	Set stack under BDOS (xx00h)

	LD	HL,(6)
	LD	L,0
	LD	SP,HL

;	Leave space for stack and init. variables

	LD	DE,ccSTACKSIZE
	OR	A
	SBC	HL,DE
	DEC	HL
	LD	(ccfreelast),HL
	LD	DE,ccfreemem
	LD	(ccfreefirst),DE
	OR	A
	SBC	HL,DE
	INC	HL
	LD	(ccfreebytes),HL
	JR	NC,ccargs

;	Error, no memory for stack

	LD	C,9
	LD	DE,ccerrstack
	CALL	5
	JP	0

ccerrstack
	DEFB	'Runtime Error - No stack$'

; Setup command line arguments

ccargs

#endasm

#ifndef CC_NO_ARGS

#asm
;	Copy command line

	LD	HL,81H
	LD	DE,ccmdbuf
	LD	BC,127
	LDIR

	LD	A,(80H)
	LD	B,0
	LD	C,A
	LD	HL,ccmdbuf
	ADD	HL,BC
	LD	(HL),0

;	Init. argc & argv

	LD	DE,cchptr
	LD	HL,ccmdbuf - 1
	LD	BC,1 
ccspc
	INC	HL
	LD	A,(HL)
	OR	A
	JR	Z,ccarg
	CP	' '
	JR	Z,ccspc
	LD	A,L 
	LD	(DE),A
	LD	A,H
	INC	DE
	LD	(DE),A
	INC	DE
	INC	C
ccpar
	INC	HL
	LD	A,(HL)
	OR	A
	JR	Z,ccarg 
	CP	' ' 
	JR	NZ,ccpar
	LD	(HL),0
	JR	ccspc

ccarg
	LD	HL,cchptr - 2
	PUSH	BC		;argc
	PUSH	HL		;argv
#endasm

#endif

#ifdef CC_REDIR

#asm
	CALL	redir		;FIXME - Check errors
	POP	DE
	POP	BC
	PUSH	HL		;argc
	PUSH	DE		;argv
#endasm

#endif

#asm

;	Execute program

	CALL main
#endasm

/**
 * @fn     void exit(int code)
 * @brief  Exit to CP/M.
 *
 * FixMe: Return code is lost!
 */
#asm

;	Exit to CP/M

exit
	NOP	; Patch for atexit() -- 3 bytes.
	NOP
	NOP
#endasm

#ifdef CC_STDIO

BYTE *stdin, *stdout, *stderr;   /* Sorry, no available FILE here */

#asm
	LD	HL,(stdin)
	CALL	ccflush
	LD	HL,(stdout)
	CALL	ccflush

	JP	0

ccflush
	LD	A,H
	OR	L
	RET	Z
	PUSH	HL
	CALL	fclose
	POP	BC
	RET
#endasm

#else

#asm
	JP	0
#endasm

#endif

#asm

;	Variables for memory functions

ccfreefirst
	DEFW	0	;Adr. first free byte
ccfreelast
	DEFW	0	;Adr. last free byte
ccfreebytes
	DEFW	0	;Number of free bytes

#endasm

#ifndef CC_NO_ARGS

#asm
;	Variables for command line arguments

ccmdbuf
	DEFS	128	;Command line buffer

	DEFW ccNULL	;Pointers table for argv
cchptr
	DEFW ccNULL,ccNULL,ccNULL,ccNULL,ccNULL
	DEFW ccNULL,ccNULL,ccNULL,ccNULL,ccNULL
	DEFW ccNULL,ccNULL,ccNULL,ccNULL,ccNULL
	DEFW ccNULL,ccNULL,ccNULL,ccNULL,ccNULL
	DEFW ccNULL,ccNULL,ccNULL,ccNULL,ccNULL

ccNULL
	DEFB	0 	;Null pointer
#endasm

#endif

#asm

;	Basic routines

;	Call formats to access locals:
;
;	Format 1:	CALL routine
;			DEFB SpOffset
;
;	Format 2:	CALL routine
;			DEFW SpOffset

;	HL = unsigned char from local (format 2)

ccxgb2
	CALL	ccladr2
	JR	ccxgb3

;	HL = unsigned char from local (format 1)

ccxgb
	CALL	ccladr1
ccxgb3
	LD	L,(HL)
	LD	H,0
	RET

;	HL = signed char from local (format 2)

ccxgc2
	CALL	ccladr2
	JR	ccgc

;	HL = signed char from local (format 1)

ccxgc
	CALL	ccladr1
	
;	HL = signed char from (HL)

ccgc
	LD	A,(HL)

;	HL = signed char from A

ccsxt
	LD	L,A
	RLCA
	SBC	A
	LD	H,A
	RET

;	LD	H,0
;	LD	L,A
;	AND	128
;	RET	Z
;	DEC	H
;	RET

;	HL = word from local (format 2)

ccxgw2
	CALL	ccladr2
	JR	ccgw

;	HL = word from local (format 1)

ccxgw
	CALL	ccladr1

;	HL = word from (HL)

ccgw
	LD	A,(HL)
	INC	HL
	LD	H,(HL)
	LD	L,A
	RET

;	char local = HL (format 2)

ccxpb2
	CALL	ccladr2sv
	JR	ccxpb3

;	char local = HL (format 1)

ccxpb
	CALL	ccladr1sv
ccxpb3
	LD	DE,(cctmpw)
	LD	(HL),E
	EX	DE,HL
	RET

;	int/ptr local = HL (format 2)

ccxpw2
	CALL	ccladr2sv
	JR	ccxpw3

;	int/ptr local = HL (format 1)

ccxpw
	CALL	ccladr1sv
ccxpw3
	LD	DE,(cctmpw)
	LD	(HL),E
	INC	HL
	LD	(HL),D
	EX	DE,HL
	RET

;	Copy 1 word from HL to (DE)

ccpw
	LD	A,L
	LD	(DE),A
	INC	DE
	LD	A,H
	LD	(DE),A
	RET

;	Calc. local adress

cctmpw	DEFW	0

ccladr2sv
	LD	(cctmpw),HL

ccladr2
	POP	DE
	POP	HL
	LD	C,(HL)
	INC	HL
	LD	B,(HL)
	INC	HL
	PUSH	HL
	PUSH	DE
	LD	HL,4
	ADD	HL,BC
	ADD	HL,SP
	RET

ccladr1sv
	LD	(cctmpw),HL

ccladr1
	POP	DE
	POP	HL
	LD	B,0
	LD	C,(HL)
	INC	HL
	PUSH	HL
	PUSH	DE
	LD	HL,4
	ADD	HL,BC
	ADD	HL,SP
	RET

;	OR	HL = HL | DE

ccor
	LD	A,L
	OR	E
	LD	L,A
	LD	A,H
	OR	D
	LD	H,A
	RET

;	XOR	HL = HL ^ DE

ccxor
	LD	A,L
	XOR	E
	LD	L,A
	LD	A,H
	XOR	D
	LD	H,A
	RET

;	AND	HL = HL & DE

ccand
	LD	A,L
	AND	E
	LD	L,A
	LD	A,H
	AND	D
	LD	H,A
	RET

;	LOGIC OR	HL = DE || HL

cclgor
	LD	A,H
	OR	L
	OR	D
	OR	E
	LD	L,A
	RET

	;LD	A,H
	;OR	L
	;RET	NZ
	;LD	A,D
	;OR	E
	;RET	Z
	;INC	L
	;RET

;	LOGIC AND	HL = DE && HL

cclgand
	LD	A,H
	OR	L
	RET	Z
	LD	A,D
	OR	E
	RET	NZ
	JP	ccfalse

;	HL = HL == DE

cceq
	OR	A
	SBC	HL,DE

;	LOGIC NOT	HL = !HL

cclgnot
	LD	A,H
	OR	L
	JP	NZ,ccfalse
	INC	L
	RET

;	HL = HL != DE

ccne
	OR	A
	SBC	HL,DE
	RET

;	HL = DE > HL	(SIGNED)

ccgt
	EX	DE,HL

;	HL = DE < HL	(SIGNED)

cclt
	CALL	cccmp
	RET	C
	DEC	L
	RET

;	HL = DE <= HL	(SIGNED)

ccle
	CALL	cccmp
	RET	Z
	RET	C
	DEC	L
	RET

;	HL = DE >= HL	(SIGNED)

ccge
	CALL	cccmp
	RET	NC
	DEC	L
	RET

;	Compare DE with HL, and return: (SIGNED)
;
;	CARRY if DE < HL
;	ZERO if DE == HL
;	HL = 1

cccmp
	LD	A,E
	SUB	L
	LD	E,A
	LD	A,D
	SBC	H
	LD	HL,1
	JP	M,cccmp1
	OR	E
	RET

cccmp1
	OR	E
	SCF
	RET

;	HL = DE <= HL	(UNSIGNED)

ccule
	CALL	ccucmp
	RET	Z
	RET	C
	DEC	L
	RET

;	HL = DE >= HL	(UNSIGNED)

ccuge
	CALL	ccucmp
	RET	NC
	DEC	L
	RET

;	HL = DE > HL	(UNSIGNED)

ccugt
	EX	DE,HL

;	HL = DE < HL	(UNSIGNED)

ccult
	CALL	ccucmp
	RET	C
	DEC	L
	RET

;	Compare DE with HL, and return: (UNSIGNED)
;
;	CARRY if DE < HL
;	ZERO if DE == HL
;	HL = 1

ccucmp
	LD	A,D
	CP	H
	JR	NZ,ccucmp1
	LD	A,E
	CP	L

ccucmp1
	LD	HL,1
	RET

;	HL = DE >> HL	(UNSIGNED)

ccuasr
	EX	DE,HL
	LD	A,E
ccuasr1
	OR	A
	RET	Z
	DEC	A
	SRL	H
	RR	L
	JR	ccuasr1

;	HL = DE >> HL	(ARITMETIC)

ccasr
	EX	DE,HL
	LD	A,E
ccasr1
	OR	A
	RET	Z
	DEC	A
	SRA	H
	RR	L
	JR	ccasr1

;	HL = DE << HL	(UNSIGNED)

ccuasl

;	HL = DE << HL	(ARITMETIC)

ccasl
	EX	DE,HL
	LD	A,E
ccasl1
	OR	A
	RET	Z
	DEC	A
	ADD	HL,HL
	JR	ccasl1

;	HL = DE - HL

ccsub
	EX	DE,HL
	OR	A
	SBC	HL,DE
	RET

;	HL = ~HL	(1 COMPLEMENT)

cccom
	LD	A,H
	CPL
	LD	H,A
	LD	A,L
	CPL
	LD	L,A
	RET

;	HL = -HL	(2 COMPLEMENT)

ccneg
	LD	A,H
	CPL
	LD	H,A
	LD	A,L
	CPL
	LD	L,A
	INC	HL
	RET

#endasm

#ifndef CC_NO_MUL

#asm

;	HL = DE * HL	(UNSIGNED)

ccumul

;	HL = DE * HL	(SIGNED)

ccmul
	LD	A,H
	LD	C,L
	LD	HL,0
	LD	B,16
ccmul0
	ADD	HL,HL
	SLA	C
	RL	A
	JR	NC,ccmul1
	ADD	HL,DE
ccmul1
	DJNZ	ccmul0
	RET

#endasm

#endif

#ifndef CC_NO_DIV

#asm

;	HL = DE % HL	(SIGNED)

ccmod
	CALL	ccdiv
	EX	DE,HL
	RET

;	HL = DE / HL	(SIGNED)
;	DE = DE % HL	(SIGNED)

ccdiv
	LD	B,H
	LD	C,L
	LD	A,D
	XOR	B
	PUSH	AF
	LD	A,D
	OR	A
	CALL	M,ccdivdeneg
	LD	A,B
	OR	A

	JP	P,ccdiv0

	LD	A,B
	CPL
	LD	B,A
	LD	A,C
	CPL
	LD	C,A
	INC	BC

ccdiv0
	EX	DE,HL
	LD	DE,0
	LD	A,16

ccdiv1
	PUSH	AF

	ADD	HL,HL

	RL	E
	RL	D
	LD	A,D
	OR	E

	JR	Z,ccdiv2

	LD	A,E
	SUB	C
	LD	A,D
	SBC	B

	JP	M,ccdiv2
	LD	A,L
	OR	1
	LD	L,A
	LD	A,E
	SUB	C
	LD	E,A
	LD	A,D
	SBC	B
	LD	D,A

ccdiv2
	POP	AF
	DEC	A
	JR	NZ,ccdiv1
	POP	AF
	RET	P

	CALL	ccneg

ccdivdeneg
	LD	A,D
	CPL
	LD	D,A
	LD	A,E
	CPL
	LD	E,A
	INC	DE
	RET

;	HL = DE % HL	(UNSIGNED)

ccumod
	CALL	ccudiv
	EX	DE,HL
	RET

;	HL = DE / HL	(UNSIGNED)
;	DE = DE % HL	(UNSIGNED)

ccudiv
	LD	(ccudiv_tmp),HL
	LD	HL,ccudiv_cnt
	LD	(HL),17
	LD	BC,0
	PUSH	BC
	XOR	A

ccudiv0
	RL	E
	RL	D
	DEC	(HL)
	POP	HL
	JR	Z,ccudiv2
	LD	A,0
	ADC	0
	ADD	HL,HL
	LD	B,H
	ADD	L
	LD	HL,(ccudiv_tmp)
	SUB	L
	LD	C,A
	LD	A,B
	SBC	H
	LD	B,A
	PUSH	BC
	JR	NC,ccudiv1
	ADD	HL,BC
	EX	(SP),HL

ccudiv1
	LD	HL,ccudiv_cnt
	CCF
	JR	ccudiv0

ccudiv2
	EX	DE,HL
	RET

ccudiv_tmp
	DEFW	0
ccudiv_cnt
	DEFB	0

#endasm

#endif

#ifndef CC_NO_SWITCH

#asm

;	Switch, on entry:
;
;	DE = Table address
;	HL = Where to go if value was not found in table
;	B  = Number of entries in table

ccswtch
	EX	(SP),HL
	EX	DE,HL

ccswch1
	LD	A,E
	CP	(HL)
	INC	HL
	JR	NZ,ccswch2
	LD	A,D
	CP	(HL)
	JR	NZ,ccswch2
	INC	HL
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	EX	DE,HL
	POP	BC
	JP	(HL)

ccswch2
	INC	HL
	INC	HL
	INC	HL
	DJNZ	ccswch1
	EX	(SP),HL
	POP	BC
	JP	(HL)

#endasm

#endif

#asm

;	HL = TRUE

cctrue
	LD	L,1
	RET

;	HL = FALSE

ccfalse
	LD	HL,0
	RET

#endasm

