diff --git a/doc/c02.txt b/doc/c02.txt index 6ff3576..40a5079 100644 --- a/doc/c02.txt +++ b/doc/c02.txt @@ -141,15 +141,41 @@ Examples: #pragma padding 1 //Add one empty byte to end of code #pragma padding $FF //Add 255 empty bytes to end of code +PRAGMA RAMBASE + +The #pragma rambase directive sets the base address for variables in RAM +(not declared const). This is normally used when the compiled code will be +stored in ROM (such as in an EPROM or Cartridge), but can be used any time +variables should be in a specific area of RAM. + +Examples: + #pragma rambase $0200 //Define Variable RAM base address for NES + #pragma rambase 828 //Define Variable RAM in C64 Tape Buffer + #pragma rambase 4096 //Define RAM base for Vic 20 ROM cartridge + PRAGMA VARTABLE The #pragma vartable directive forces the variable table to be written. It should be used before any #include directives that need to generate code following the table. +PRAGMA WRITEBASE + +The #pragma writebase directive sets the base address for writing to variables +in RAM. This is used when target system uses different addresses for reading +and writing the same memory locations. This directive must be preceded by +#pragma rambase directive with a non-zero argument. + +Examples: + #pragma rambase $F080 //Define Superchip RAM Read Base for Atari 2600 + #pragma writebase $F000 //Define Superchip RAM Write Base for Atari 2600 + +Note: Setting a RAM write base causes the compiler to generate a write offset +which is concatenated to the variable name on all assignments. + PRAGMA ZEROPAGE -The #pragma zeropage variable sets the base address for variables declared +The #pragma zeropage directive sets the base address for variables declared as zeropage. Example: @@ -189,7 +215,7 @@ The " character and a subset of ASCII control characters can be specified in a string literal by using escape sequences prefixed with the \ symbol: \b $08 Backspace - \e $08 Escape + \e $1B Escape \f $0C Form Feed \n $0A Line Feed \r $0D Carriage Return diff --git a/doc/design.txt b/doc/design.txt index bb2e676..cf663e3 100644 --- a/doc/design.txt +++ b/doc/design.txt @@ -1,5 +1,78 @@ C02 Design Considerations +Variable Storage +================ + +Zero Page +--------- + +The 6502 has a Zero Page addressing mode. This differs from the absolute +addressing mode in that the address operand is only one byte instead of +two and the instructions take less cycles to execute. + +Also, some systems have very limited RAM, requiring all variables to be +stored in zero page. One example is the Atari 2600, which maps the 128 +bytes of RAM in the RIOT chip to the range $80-$8F and mirrors it at +$180-$18F (for the machine stack). + +To allow the use of zero page variables, the compiler recognizes the +"zeropage" declaration modifier and the "#pragma zeropage" directive, +the latter of which specifies a base address for allocating zero page +variables. + +I may add a -Z command line option to allow the specification of the +zero page variable base address. + +ROM Based Code +-------------- + +For compiled programs that will reside in ROM, such as an EPROM or a +cartridge, variables will need to reside in a separate memory area than +the program. + +The compiler normally allocates all variables directly after the generated +code, const variables first and regular variables afterward. In addition, +the regular variables are all all allocated as zero bytes in the assembled +object code, which can unnecesarilly inflate the size of the generated +binary file. + +The compiler allows the location of non-const variables to be specified +using the "#pragma rambase" directive. + +I may add a -R command line option to allow the specification of the RAM +variable base address. + +Read/Write Variables +-------------------- + +Some systems used different memory addresses for reading and writing to +RAM. One example is Atari 2600 catridges with RAM. The SARA Superchip +contained 128 bytes of RAM, which were written at addresses $F000 through +$F01F and read from addresses $F080 through $F0FF, while CBS RAM+ had 256 +bytes which were written from $F000 though $F0FF and read from $F100 +through $F1FF. + +To allow for this, the compiler recognizes an additional directive, +"#pragma writebase" directive. In this case the "#pragma rambase" +will specify the base address for reads. When a write base address is +specified, the compiler will allocate all variables using the read base +address and generate an offset, which will be included in all variable +assignments. Thus the code + + #pragma rambase $F100 + #pragma writebase $F000 + char b,c; + c = b; + +would generate the assembly code + + LDA B + STA C-256 + B EQU $F100 + C EQU $F101 + +I may add a -W command line option to allow the specification of the write +base address. Variable Types ============== @@ -217,6 +290,7 @@ whereas the equivalent C02 code generates much simpler machine code: + Shift Operators ---------------- diff --git a/src/c02.c b/src/c02.c index 91e0fbf..9a86ffc 100644 --- a/src/c02.c +++ b/src/c02.c @@ -42,10 +42,13 @@ void init(void) { nxtwrd[0] = 0; //Next Word (from DEFINE lookup) nxtptr = 0; //Pointer to next character in nxtwrd vrwrtn = FALSE; //Variables Written Flag + rambas = 0; //RAM Base Address + wrtbas = 0; //Write Base Address zpaddr = 0; //Current Zero-Page Address invasc = FALSE; //Invert ASCII Flag mskasc = FALSE; //Set High Bit Flag fcase = FALSE; //First Case Statement Flag + wrtofs[0] = 0; //Write Offset xsnvar[0] = 0; //Assigned X Variable Name ysnvar[0] = 0; //Assigned Y Variable Name strcpy(incdir, "../include/"); diff --git a/src/common.h b/src/common.h index d27ad56..8fcb40a 100644 --- a/src/common.h +++ b/src/common.h @@ -28,6 +28,7 @@ #define BYTEOP "DC" //Define Byte Pseudo-Op #define STROP "DS" //Define String Pseudo-Op #define ALNOP "ALIGN" //Align Pseudo-Op +#define USEGOP "SEG.U" //Uninitalized Segment Pseudo-Op #define LOCOP "SUBROUTINE" //Local Variable Boundary Pseudo-Op #define ASMFMT "%-7s %-3s %-12s %s\n" //Assembly Language Line printf Format diff --git a/src/include.c b/src/include.c index 22512f6..4aa0886 100644 --- a/src/include.c +++ b/src/include.c @@ -90,8 +90,24 @@ void ppddng(void) { DEBUG("Set padding to %d\n", padcnt) } +/* Parse RamBase Subdirective */ +void prambs(void) { + rambas = prsnum(0xFFFF); //Set Ram Base Address to Literal + DEBUG("Set ram base address to %d\n", rambas) +} + +/* Parse WriteBase Subdirective */ +void pwrtbs(void) { + if (!rambas) ERROR("RAM Base must be set prior to Write Base\n", 0, EXIT_FAILURE); + wrtbas = prsnum(0xFFFF); //Set Ram Base Address to Literal + DEBUG("Set write base address to %d ", wrtbas) + if (rambas && wrtbas) sprintf(wrtofs, "%+d", wrtbas - rambas); + else wrtofs[0] = 0; + DETAIL("and write offset to '%s'\n", wrtofs) +} + /* Parse Zeropage Subdirective */ -void prszpg(void) { +void pzropg(void) { zpaddr = prsnum(0xFF); //Set Zero Page Address to Literal DEBUG("Set zero page address to %d\n", zpaddr) } @@ -112,12 +128,17 @@ void pprgma(void) { porign(); //Parse Origin else if (wordis("PADDING")) ppddng(); //Parse Origin + else if (wordis("RAMBASE")) + prambs(); //Parse RamBase else if (wordis("VARTABLE")) - pvrtbl(); //Parse Vartable + pvrtbl(); //Parse VarTable + else if (wordis("WRITEBASE")) + pwrtbs(); //Parse RamBase else if (wordis("ZEROPAGE")) - prszpg(); //Parse Origin + pzropg(); //Parse ZeroPage else ERROR("Illegal pragma subdirective '%s'\n", word, EXIT_FAILURE) + cmtlin(); //Write Comment Line } /* Process Include File Directive */ diff --git a/src/stmnt.c b/src/stmnt.c index deaa2f7..d5fcc51 100644 --- a/src/stmnt.c +++ b/src/stmnt.c @@ -70,6 +70,13 @@ void prcidx(int idxtyp, char *name, char *index) } } +/* Set word to assignment variable * + * adding write offset (if set) */ +void setasn(char *name) { + strcpy(word, name); + if (wrtofs[0]) strcat(word, wrtofs); +} + /* Process Assignment */ void prcasn(char trmntr) { expect('='); @@ -77,20 +84,27 @@ void prcasn(char trmntr) { else prsxpr(trmntr); //Parse Expression DEBUG("Processing X assignment variable '%s'\n", xsnvar) if (xsnvar[0]) { + setasn(xsnvar); if (strlen(xsnidx)) { //Process X variable Index - asmlin("PHA", ""); //Save Accumulator - asmlin("TXA", ""); //Transfer Return Value to Accumulator - prcidx(xsnivt, xsnvar, xsnidx); //Process Index - asmlin("STA", xsnvar); //Store Return Value - asmlin("PLA", ""); //Restore Accumulator + if (xsnivt != LITERAL) { + asmlin("PHA", ""); //Save Accumulator + asmlin("TXA", ""); //Transfer Return Value to Accumulator + prcidx(xsnivt, word, xsnidx); //Process Index + asmlin("STA", word); //Store Return Value + asmlin("PLA", ""); //Restore Accumulator + } else { + prcidx(xsnivt, word, xsnidx); //Process Index + asmlin("STX", word); //Store Return Value + } } - else asmlin("STX", xsnvar); //Store Return Value + else asmlin("STX", word); //Store Return Value xsnvar[0] = 0; } DEBUG("Processing Y assignment variable '%s'\n", ysnvar) if (ysnvar[0]) { - if (strlen(ysnidx)) prcidx(ysnivt, ysnvar, ysnidx); //Process Index - asmlin("STY", ysnvar); //Store Return Value + setasn(ysnvar); + if (strlen(ysnidx)) prcidx(ysnivt, word, ysnidx); //Process Index + asmlin("STY", word); //Store Return Value ysnvar[0] = 0; } DEBUG("Checking if '%s' is a register\n", asnvar) @@ -98,8 +112,9 @@ void prcasn(char trmntr) { else if (strcmp(asnvar, "Y")==0) asmlin("TAY", ""); else if (strcmp(asnvar, "A")==0) return; DEBUG("Processing assignment variable '%s'\n", asnvar) - if (strlen(asnidx)) prcidx(asnivt, asnvar, asnidx); //Process Index - asmlin("STA", asnvar); //Store Return Value + setasn(asnvar); + if (asnidx[0]) prcidx(asnivt, word, asnidx); //Process Index + asmlin("STA", word); //Store Return Value } /* Parse and Return Array Index and Type */ diff --git a/src/vars.c b/src/vars.c index 351be94..5d237e1 100644 --- a/src/vars.c +++ b/src/vars.c @@ -318,6 +318,11 @@ void vartbl(void) { vardef(MTCONST); //Write CONST Definitions //Emit Segment Mnemonic for RAM Variables here LCMNT("Writable Variables") + if (rambas) { + asmlin(USEGOP,"RAMVARS"); //Create Uninitialized Segment + sprintf(word, "$%X", rambas); + asmlin(ORGOP, word); //Set Origin to RAM Base Address + } vardef(0); //Write All Other Variables } diff --git a/src/vars.h b/src/vars.h index bb729e9..c31f470 100644 --- a/src/vars.h +++ b/src/vars.h @@ -56,7 +56,10 @@ enum dtypes {DTBYTE, DTSTR, DTARRY}; //Variable Data Types #define MTALGN 128 //Aligned int symdef(char *name); //Is Variable defined (TRUE or FALSE) +int rambas; //RAM Base Address (0=None) +int wrtbas; //Write Base Address (0=None) int zpaddr; //Current Zero-Page Address +char wrtofs[6]; //Write Address Offset void addvar(int m, int t); //Parse and Compile Variable Declaration void addstc(); //Parse and Compile Structure Declaration diff --git a/work/arrays.c02 b/work/arrays.c02 index 8d17444..a34f0bf 100644 --- a/work/arrays.c02 +++ b/work/arrays.c02 @@ -23,4 +23,7 @@ b = s[func()]; b = s[0] & d[c-1] | n[i+2]; b = s[A] + b[X] + c[Y]; +r[0], r[1], r[2] = func(); +r[b], r[c], r[i] = func(); + pop b, d[i], n[func()], s[c-n[i+3]]; diff --git a/work/rambase.c02 b/work/rambase.c02 new file mode 100644 index 0000000..62ba3ce --- /dev/null +++ b/work/rambase.c02 @@ -0,0 +1,13 @@ +/* Test RamBase Directive */ + +#pragma origin $F200 +#pragma rambase $F000 + +char t[128]; +const char s = "String"; + +strdst(&s); +strcpy(&t); + +void strcpy() {} +void strdst() {} diff --git a/work/wrtbase.c02 b/work/wrtbase.c02 new file mode 100644 index 0000000..5800cca --- /dev/null +++ b/work/wrtbase.c02 @@ -0,0 +1,13 @@ +/* Test WriteBase Directive */ + +#pragma origin $F100 +#pragma rambase $F080 +#pragma writebase $F000 + +char r[7]; +char b,d,i; +const char c = 0; + +b = d; +for (i=0;i<7;i++) r[i] = r[i] + i; +r[7]=7;