/** * @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