From 6cbf7f87547910e8de7774739dc2c8ed1302c986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Wed, 2 Dec 2015 22:16:31 -0800 Subject: [PATCH] x65macro.i added - Adding standard macros with for loops, memory copy, add, subtract, move and set - macros can be named with dots - double negatives won't cause errors in expressions - vice output will convert labels named "debugbreak" to vice breakpoints rather than vice labels - fixed issues with mixing conditional operators with math operators in expressions --- README.md | 26 ++- macros/x65macro.i | 516 ++++++++++++++++++++++++++++++++++++++++++++++ x65.cpp | 79 +++---- x65.txt | 112 ++++++++++ 4 files changed, 690 insertions(+), 43 deletions(-) create mode 100644 macros/x65macro.i diff --git a/README.md b/README.md index 5089bc2..50f9490 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,6 @@ Noteworthy features: x65.cpp requires struse.h which is a single file text parsing library that can be retrieved from https://github.com/Sakrac/struse. -## Additional x65 Tools - -* **dump_x65**: Inspect the contents of .x65 object files generated by x65 to track down linking issues -* **x65dsasm**: Disassemble assembled binary code for review - ### References * [6502 opcodes](http://www.6502.org/tutorials/6502opcodes.html) @@ -68,6 +63,22 @@ x65.cpp requires struse.h which is a single file text parsing library that can b * [Windows x64 binaries](../..//raw/master/bin/x65_x64.zip) * [Windows x86 binaries](../..//raw/master/bin/x65_win32.zip) +### x65 + +x65 is the assembler + +### x65macro.i + +x65macro.i is a 6502 include file that defines a number of standard macros that can assign values, move values, copy values and loop constructs, see x65.txt for details. + +### dump_x65 + +dump_x65 is a tool to inspect the contents of .x65 object files generated by x65 to track down linking issues + +### x65dsasm + +x65dsasm is a tool to disassemble assembled binary code for review, it will perform a basic analysis and assign labels where appropriate and treats unreferenced bytes as data rather than code. It can also export assemblable code from a binary. + ### Acknowledgments This project would not be completed without the direct or indirect support of great people, some which I can currently remember: @@ -89,6 +100,11 @@ Primarily tested with personal archive of sources written for Kick assmebler, DA * irp (indefinite repeat) **FIXED** +* Adding x65macro.i +* Vice symbols will generate breakpoints whenever label 'debugbreak' is encountered +* Evaluating '==' was broken +* Macros can have dots in their names +* Handling double negative in expressions (--35 == 35) * Macros works within conditionals (if/else/endif, etc) * String symbols broke late evaluation resulting in garbage references, this has been fixed * Added string symbols diff --git a/macros/x65macro.i b/macros/x65macro.i new file mode 100644 index 0000000..fd0143e --- /dev/null +++ b/macros/x65macro.i @@ -0,0 +1,516 @@ +;x65macros.i + +; +; set.b / .w / .t / .l Trg, Value +; - set the contents of an 1-4 byte location to a value +; - uses accumulator +; +; move.b / .w / .t / .l / .n Src,Trg +; - copy 1-4 (or n) bytes from Src location to Trg location +; - uses accumulator +; +; add.n Address1, Address2, Target, Bytes +; - add contents of two memory locations into a target lcoation +; - uses accumulator +; +; sub.n Address1, Address2, Target, Bytes +; - Target = Address1 - Address2 +; - uses accumulator +; +; add.ni Address, Value, Target, Bytes +; - add a fixed value to a memory location into a target +; - uses accumulator +; +; sub.ni Address, Value, Target, Bytes +; - Target = Address - Value +; - uses accumulator +; +; addw.i Address, Value, Target +; - Subtract 16 bit Value from contents of Address and store at Target +; - uses accumulator +; +; subw.i Address1, Address2, Target +; - add contents of two 16 bit addresses into a target 16 bit location +; - uses accumulator +; +; mnop Count +; - add Count nops +; +; copy.x Source, Target, Size +; - copy up to 256 bytes using the x register as a counter +; - uses accumulator and x register +; +; copy.y Source, Target, Size +; - copy up to 256 bytes using the y register as a counter +; - uses accumulator and y register +; +; copy.p Src,Trg,Size,PoolZP +; - copy more than 256 bytes using zero page label pool addresses +; - uses accumulator, x and y register +; +; copy.a Src,Trg,Size +; - copy more than 256 bytes using absolute indexed in a loop +; - uses accumulator, x and y register +; +; copy.zp Src,Trg,Size,zpTmp1,zpTmp2 +; - copy more than 256 bytes using two pairs of zero page values +; - uses accumulator, x and y register +; +; for.x Start, End +; - iterate using the x register from Start to End, End is not inclusive +; so to iterate from 31 to 0 use for.x 31, -1 +; - uses x register +; - end for loop with forend macro +; +; for.y Start, End +; - same as for.x but with the y register +; - uses y register +; - end for loop with forend macro +; +; for.w Start, End, Counter +; - for loop for 16 bit counter +; - uses accumulator +; - end for loop with forend macro +; +; for.ws Start, End, Counter, Step +; - for loop for 16 bit counter with a step value +; - uses accumulator +; - end for loop with forend macro +; +; forend +; - terminates for loops +; + +macro set.b Value,Trg +{ + lda #Value + sta Trg +} + +; Set two bytes to a 16 bit value +macro set.w Value,Trg +{ + lda #Value + sta Trg+1 +} + +; Set three bytes to a 24 bit value +macro set.t Value,Trg +{ + rept 3 { + lda #Value>>(rept*8) + sta Trg+rept + } +} + +; Set three bytes to a 24 bit value +macro set.l Value,Trg +{ + rept 4 { + lda #Value>>(rept*8) + sta Trg+rept + } +} + +macro move.b Src,Trg +{ + lda Src + sta Trg +} + +macro move.w Src,Trg +{ + rept 2 { + lda Src + rept + sta Trg + rept + } +} + +macro move.t Src,Trg +{ + rept 3 { + lda Src + rept + sta Trg + rept + } +} + +macro move.l Src,Trg +{ + rept 4 { + lda Src + rept + sta Trg + rept + } +} + +macro move.n Src,Trg,Size +{ + rept Size { + lda Src + rept + sta Trg + rept + } +} + +; add two numbers together (A and B and Trg are addresses) +macro add.n A,B,Trg,NumSize +{ + clc + rept NumSize { + lda A+rept + adc B+rept + sta Trg+rept + } +} + +; add two numbers together (A and B and Trg are addresses) +macro sub.n A,B,Trg,NumSize +{ + sec + rept NumSize { + lda A+rept + sbc B+rept + sta Trg+rept + } +} + +; add a fixed value to an N byte number and store at Trg +macro add.ni Src,Value,Trg,NumSize +{ + clc + rept NumSize { + lda Src+rept + adc #Value>>(8*rept) + sta Trg+rept + } +} + +; add a fixed value to an N byte number and store at Trg +macro sub.ni Src,Value,Trg,NumSize +{ + sec + rept NumSize { + lda Src+rept + sbc #Value>>(8*rept) + sta Trg+rept + } +} + +; add a fixed value to a two byte number and store at Trg +macro addw.i Src,Value,Trg +{ + clc + lda #Value + adc Src+1 + sta Trg+1 +} + +; add a fixed value to a two byte number and store at Trg +macro subw.i Src,Value,Trg +{ + sec + lda Src + sbc #Value + sta Trg+1 +} + +; insert multiple nops +macro mnop Count { + rept Count { + nop + } +} + +; copy a fixed length buffer from one place to another +; size is up to a page, changing X and A +macro copy.x Src,Trg,Size +{ + if Size==0 + elif Size==1 + lda Src + sta Trg + elif Size<129 + ldx #Size-1 + { + lda Src,x + sta Trg,x + dex + bpl ! + } + elif Size<256 + ldx #0 + { + lda Src,x + sta Trg,x + inx + cpx #size + bne ! + } + else + error copy.x can only copy up to 256 bytes, use copy.p to copy Size bytes + endif +} + +; copy a fixed length buffer from one place to another +; size is up to a page, changing Y and A +macro copy.y Src,Trg,Size +{ + if Size==0 + elif Size==1 + lda Src + sta Trg + elif Size<129 + ldy #Size-1 + { + lda Src,y + sta Trg,y + dey + bpl ! + } + elif Size<256 + ldy #0 + { + lda Src,y + sta Trg,y + iny + cpy #size + bne ! + } + else + error copy.x can only copy up to 256 bytes, use copy.p to copy Size bytes + endif +} + +; copy a fixed length buffer using relative zp y indexing +; size is up to a page, changing Y and A +macro copy.ry zpSrcPtr,zpTrgPtr,Size +{ + if (Size) > 256 + error copy.ry can only copy up to 256 bytes + elif (Size) > 0 + ldy #Size-1 + { + lda (zpSrcPtr),y + sta (zpTrgPtr),y + dey + if (Size) > 128 + cpy #$ff + bne ! + else + bpl ! + endif + } + endif +} + +; copy pages using temp zero page registers +; falls back on CopyF if less than or equal to a page +; changes x, y and A +macro copy.p Src,Trg,Size,PoolZP +{ + if (Size<256) + copy.x Src,Trg,Size + else + { + PoolZP zpSrc.w + PoolZP zpTrg.w + set.w zpSrc,Src + set.w zpTrg,Trg + ldx #>Size + ldy #0 + { + { + lda (zpSrc),y + sta (zpTrg),y + iny + bne ! + } + inc zpSrc+1 + inc zpTrg+1 + dex + bne ! + } + if Size & $ff + ldy #Size-1 + { + lda (zpSrc),y + sta (zpTrg),y + dey + if (Size & $ff)<129 + bpl ! + else + cpy #$ff + bne ! + endif + } + endif + } + endif +} + +macro copy.zp Src,Trg,Size,zpTmp1,zpTmp2 +{ + if (Size<256) + copy.x Src,Trg,Size + else + { + set.w zpTmp1,Src + set.w zpTmp2,Trg + ldx #>Size + ldy #0 + { + { + lda (zpTmp1),y + sta (zpTmp2),y + iny + bne ! + } + inc zpTmp1+1 + inc zpTmp2+1 + dex + bne ! + } + if Size & $ff + ldy #Size-1 + { + lda (zpTmp1),y + sta (zpTmp2),y + dey + if (Size & $ff)<129 + bpl ! + else + cpy #$ff + bne ! + endif + } + endif + } + endif +} + +macro copy.a Src,Trg,Size +{ + if (Size<256) + copy.x Src,Trg,Size + else + { + set.b >Src, ._addr+2 + set.b >Trg, ._addr+5 + ldy #>Size + ldx #0 +._addr + { + { + lda Src,x + sta Trg,x + inx + bne ! + } + inc ._addr+2 + inc ._addr+5 + dey + bne ! + } + if Size & $ff + ldx #(Size&$ff)-1 + { + lda Src+(Size & $ff00),x + sta Trg+(Size & $ff00),x + dex + if (Size & $ff)<129 + bpl ! + else + cpx #$ff + bne ! + endif + } + endif + } + endif +} + +; for (x=start; x End + { + if (-1 == End) & (Start<129) + string _ForEnd = "dex\nbpl _ForLoop" + else + string _ForEnd = "dex\ncpx #End\nbne _ForLoop" + endif + } + else + string _ForEnd = "" + endif +_ForLoop +} + +; for (y=start; y End + { + if (-1 == End) & (Start<129) + string _ForEnd = "dey\nbpl _ForLoop" + else + string _ForEnd = "dey\ncpy #End\nbne _ForLoop" + endif + } + else + string _ForEnd = "" + endif +_ForLoop +} + +; for (Counter=start; Counter (End) + string _ForEnd = "{\ndec Counter\nbne %\ndec Counter+1\n}\nlda Counter+1\ncmp #>End\nbne _ForLoop\nlda Counter\ncmp # End + if ((Step)>-1) + error Step is not a valid iterator for range Start to End + endif + string _ForEnd = "sec\nlda Counter\n sbc #<(-Step)\nsta Counter\nlda Counter+1\nsbc #>(-Step)\nsta Counter+1\ncmp #(>End)+1\nbcs _ForLoop\nlda Counter\ncmp #(> - EVOP_STP, // u, Unexpected input, should stop and evaluate what we have - EVOP_NRY, // v, Not ready yet - EVOP_XRF, // w, value from XREF label - EVOP_EXP, // x, sub expression - EVOP_ERR, // y, Error + EVOP_NEG, // u, negate value + EVOP_STP, // v, Unexpected input, should stop and evaluate what we have + EVOP_NRY, // w, Not ready yet + EVOP_XRF, // x, value from XREF label + EVOP_EXP, // y, sub expression + EVOP_ERR, // z, Error }; // Opcode encoding @@ -2757,7 +2758,8 @@ StatusCode Asm::AddMacro(strref macro, strref source_name, strref source_file, s } else return ERROR_BAD_MACRO_FORMAT; } else { - name = macro.split_label(); + name = macro.split_range(label_end_char_range); + macro.skip_whitespace(); strref left_line = macro.get_line(); left_line.skip_whitespace(); left_line = left_line.before_or_full(';').before_or_full(c_comment); @@ -3360,14 +3362,25 @@ StatusCode Asm::EvalExpression(strref expression, const struct EvalContext &etx, } else if (op == EVOP_STP) { break; } else { - while (sp) { - EvalOperator p = (EvalOperator)op_stack[sp-1]; - if (p==EVOP_LPR || op>p) - break; - ops[numOps++] = p; - sp--; + bool skip = false; + if ((prev_op >= EVOP_EQU && prev_op <= EVOP_GTE) || (prev_op==EVOP_HIB || prev_op==EVOP_LOB)) { + if (op==EVOP_SUB) + op = EVOP_NEG; + else if (op == EVOP_ADD) + skip = true; + } + if (op == EVOP_SUB && sp && op_stack[sp-1]== EVOP_SUB) + sp--; + else { + while (sp && !skip) { + EvalOperator p = (EvalOperator)op_stack[sp-1]; + if (p==EVOP_LPR || op>p) + break; + ops[numOps++] = p; + sp--; + } + op_stack[sp++] = op; } - op_stack[sp++] = op; } // check for out of bounds or unexpected input if (numValues==MAX_EVAL_VALUES) @@ -3442,6 +3455,10 @@ StatusCode Asm::EvalExpression(strref expression, const struct EvalContext &etx, section_counts[i][ri-1] -= section_counts[i][ri]; values[ri-1] -= values[ri]; } break; + case EVOP_NEG: + if (ri>=1) + values[ri-1] = -values[ri-1]; + break; case EVOP_MUL: // * ri--; for (int i = 0; i= 0) { - // (EXP) == (EXP) - strref left = line.get_clipped(equ); - bool equal = left.get_last()!='!'; - left.trim_whitespace(); - strref right = line + equ + 1; - if (right.get_first()=='=') - ++right; - right.trim_whitespace(); - int value_left, value_right; - if (STATUS_OK != EvalExpression(left, etx, value_left)) - return ERROR_CONDITION_COULD_NOT_BE_RESOLVED; - if (STATUS_OK != EvalExpression(right, etx, value_right)) - return ERROR_CONDITION_COULD_NOT_BE_RESOLVED; - result = (value_left==value_right && equal) || (value_left!=value_right && !equal); - } else { - bool invert = line.get_first()=='!'; - if (invert) - ++line; - int value; - if (STATUS_OK != EvalExpression(line, etx, value)) - return ERROR_CONDITION_COULD_NOT_BE_RESOLVED; - result = (value!=0 && !invert) || (value==0 && invert); - } + bool invert = line.get_first()=='!'; + if (invert) + ++line; + int value; + if (STATUS_OK != EvalExpression(line, etx, value)) + return ERROR_CONDITION_COULD_NOT_BE_RESOLVED; + result = (value!=0 && !invert) || (value==0 && invert); return STATUS_OK; } @@ -7163,7 +7163,10 @@ int main(int argc, char **argv) break; } } - fprintf(f, "al $%04x %s" STRREF_FMT "\n", value, i->name[0]=='.' ? "" : ".", + if (i->name.same_str("debugbreak")) + fprintf(f, "break $%04x\n", value); + else + fprintf(f, "al $%04x %s" STRREF_FMT "\n", value, i->name[0]=='.' ? "" : ".", STRREF_ARG(i->name)); } fclose(f); diff --git a/x65.txt b/x65.txt index 61498f1..f65a72c 100644 --- a/x65.txt +++ b/x65.txt @@ -1155,3 +1155,115 @@ All Directives -0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0--0- + + +x65macro.i +---------- + +A collection of macros to simplify common tasks such as assigning values +to addresses, moving values, addition and subtraction, memory copy +options and a variety of C-like for loops. + +This is an example of how macros can be used: + + jmp CodeEnd +CopyCode: + { + inx + beq ! + txa + bne % + tay + dey + nop + } +CodeEnd: + sei + + set.w CopyCode, $fc + + for.ws $2000, $4000, $fe, CodeEnd-CopyCode + copy.ry $fc, $fe, CodeEnd - CopyCode + forend + cli + +Included macros: + +set.b / .w / .t / .l Trg, Value + - set the contents of an 1-4 byte location to a value + - uses accumulator + +move.b / .w / .t / .l / .n Src,Trg + - copy 1-4 (or n) bytes from Src location to Trg location + - uses accumulator + +add.n Address1, Address2, Target, Bytes + - add contents of two memory locations into a target lcoation + - uses accumulator + +sub.n Address1, Address2, Target, Bytes + - Target = Address1 - Address2 + - uses accumulator + +add.ni Address, Value, Target, Bytes + - add a fixed value to a memory location into a target + - uses accumulator + +sub.ni Address, Value, Target, Bytes + - Target = Address - Value + - uses accumulator + +addw.i Address, Value, Target + - Subtract 16 bit Value from contents of Address and store at Target + - uses accumulator + +subw.i Address1, Address2, Target + - add contents of two 16 bit addresses into a target 16 bit location + - uses accumulator + +mnop Count + - add Count nops + +copy.x Source, Target, Size + - copy up to 256 bytes using the x register as a counter + - uses accumulator and x register + +copy.y Source, Target, Size + - copy up to 256 bytes using the y register as a counter + - uses accumulator and y register + +copy.p Src,Trg,Size,PoolZP + - copy more than 256 bytes using zero page label pool addresses + - uses accumulator, x and y register + +copy.a Src,Trg,Size + - copy more than 256 bytes using absolute indexed in a loop + - uses accumulator, x and y register + +copy.zp Src,Trg,Size,zpTmp1,zpTmp2 + - copy more than 256 bytes using two pairs of zero page values + - uses accumulator, x and y register + +for.x Start, End + - iterate using the x register from Start to End, End is not inclusive + so to iterate from 31 to 0 use for.x 31, -1 + - uses x register + - end for loop with forend macro + +for.y Start, End + - same as for.x but with the y register + - uses y register + - end for loop with forend macro + +for.w Start, End, Counter + - for loop for 16 bit counter + - uses accumulator + - end for loop with forend macro + +for.ws Start, End, Counter, Step + - for loop for 16 bit counter with a step value + - uses accumulator + - end for loop with forend macro + +forend + - terminates for-loops \ No newline at end of file