From 482470b41033e2ae2c50644db06699d2aa456f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Thu, 19 Sep 2019 18:50:30 -0700 Subject: [PATCH] Starting WDC syntax support, adding unit test to repo. --- macros/x65macro.i | 10 +- sln/dump_x65/dump_x65.vcxproj | 8 +- sln/x65.vcxproj | 8 +- test/8BitDiff_6502.s | 293 ++++++++++++++++++++++++++++ test/AddrMode_65816.s | 42 ++++ test/Test65816_OpCodes.s | 301 +++++++++++++++++++++++++++++ test/alias_test.s | 4 + test/compare/8BitDiff_6502_cmp.prg | Bin 0 -> 349 bytes test/compare/alias_test_cmp.prg | Bin 0 -> 7 bytes test/compare/huffman_cmp.prg | Bin 0 -> 1819 bytes test/compare/merlin_lup_cmp.bin | 1 + test/compare/merlin_macro_cmp.bin | Bin 0 -> 48 bytes test/compare/x65macro_test_cmp.prg | Bin 0 -> 143 bytes test/merlin_lup.s | 13 ++ test/merlin_macro.s | 60 ++++++ test/unittest.bat | 83 ++++++++ test/x65macro_test.s | 53 +++++ x65.cpp | 153 ++++++++++++--- 18 files changed, 993 insertions(+), 36 deletions(-) create mode 100644 test/8BitDiff_6502.s create mode 100644 test/AddrMode_65816.s create mode 100644 test/Test65816_OpCodes.s create mode 100644 test/alias_test.s create mode 100644 test/compare/8BitDiff_6502_cmp.prg create mode 100644 test/compare/alias_test_cmp.prg create mode 100644 test/compare/huffman_cmp.prg create mode 100644 test/compare/merlin_lup_cmp.bin create mode 100644 test/compare/merlin_macro_cmp.bin create mode 100644 test/compare/x65macro_test_cmp.prg create mode 100644 test/merlin_lup.s create mode 100644 test/merlin_macro.s create mode 100644 test/unittest.bat create mode 100644 test/x65macro_test.s diff --git a/macros/x65macro.i b/macros/x65macro.i index b70eae3..49f3d53 100644 --- a/macros/x65macro.i +++ b/macros/x65macro.i @@ -239,7 +239,7 @@ macro asrm.n Trg,Size lda Trg+Size-1 asl rept Size { - ror (Trg - 1 + Size - rept) + ror 0 + (Trg - 1 + Size - rept) } } @@ -249,7 +249,7 @@ macro asrm.nx Trg,Size lda Trg+Size-1,x asl rept Size { - ror (Trg + Size - 1 - rept), x + ror 0 + (Trg + Size - 1 - rept), x } } @@ -493,7 +493,7 @@ macro copy.o Src,Trg,Size,PoolZP { if (Size<256) copy.x Src,Trg,Size - else + else { PoolZP zpSrc.w PoolZP zpTrg.w @@ -535,7 +535,7 @@ macro copy.zp Src,Trg,Size,zpTmp1,zpTmp2 { if (Size<256) copy.x Src,Trg,Size - else + else { set.w zpTmp1,Src set.w zpTmp2,Trg @@ -575,7 +575,7 @@ macro copy.a Src,Trg,Size { if (Size<256) copy.x Src,Trg,Size - else + else { set.b >Src, ._addr+2 set.b >Trg, ._addr+5 diff --git a/sln/dump_x65/dump_x65.vcxproj b/sln/dump_x65/dump_x65.vcxproj index 5bab23b..1da3962 100644 --- a/sln/dump_x65/dump_x65.vcxproj +++ b/sln/dump_x65/dump_x65.vcxproj @@ -72,22 +72,22 @@ false - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ $(SolutionDir)..\bin\$(Platform)\ true - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ $(SolutionDir)..\bin\$(Platform)$(Configuration)\ true - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ $(SolutionDir)..\bin\$(Platform)$(Configuration)\ false - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ $(SolutionDir)..\bin\$(Platform)\ diff --git a/sln/x65.vcxproj b/sln/x65.vcxproj index 1a08de8..7cced22 100644 --- a/sln/x65.vcxproj +++ b/sln/x65.vcxproj @@ -72,23 +72,23 @@ true - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ $(SolutionDir)..\bin\$(Platform)$(Configuration)\ true - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ $(SolutionDir)..\bin\$(Platform)$(Configuration)\ false - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ $(SolutionDir)..\bin\$(Platform)\ false $(SolutionDir)..\bin\$(Platform)\ - $(SolutionDir)..\obj\$(Platform)$(Configuration)\ + $(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\ diff --git a/test/8BitDiff_6502.s b/test/8BitDiff_6502.s new file mode 100644 index 0000000..a4275f7 --- /dev/null +++ b/test/8BitDiff_6502.s @@ -0,0 +1,293 @@ +// EXAMPLE KICK ASSEMBLER FILE + +// +// 6502 Decoder for 8BitDiff +// +// Copyright 2015 Carl-Henrik SkÃ¥rstedt. All rights reserved. +// https://github.com/sakrac/8BitDiff/ +// +// 8BDIFF FORMAT +// ------------- +// 4 bits: size of offset bit sizes +// 4 bits: size of length bit sizes +// 1 byte length bit sizes +// 1 byte offset bit sizes +// 2 bytes size of injected bytes (8 bit specific) +// injected bytes +// loop until end of inject buffer: +// bit: 0=inject, 1=source or target +// length bit cnt+length bits +// if source or target: +// buffer offset bit cnt + buffer offset bits +// sign of offset (not instead of negate) +// bit: 0=source, 1=target +// repeat loop +// +// target/source/inject source pointers start +// at the start of each buffer. +// any pointer is set to the end of the run after +// copy and the offset increments/decrements for source +// for a negative offset, invert the number, don't negate. +// +// USAGE +// ----- +// Use the 8BDIFF tool (https://github.com/sakrac/8BitDiff) +// to generate a patch between two files. Load the +// original file and the patch file and assign them as parameters: +// z8BDiff = Address of patch +// z8BSrc = Address of original data +// z8BDst = Address to decode updated data +// jsr Patch_8BDiff +// + +.label zParam8B = $f0 // 6 bytes +.label zWork8B = $f6 // 10 bytes + +// input (will be changed) +.label z8BDiff = zParam8B // start of diff +.label z8BSrc = z8BDiff+2 // start of source +.label z8BDst = z8BSrc+2 // start of destination + +// work (will be changed) +.label z8BOff = zWork8B // 2 bytes current instruction offset +.label z8BTemp = z8BOff // 1 byte temporary address +.label z8BLen = z8BOff+2 // 2 bytes current instruction length +.label z8BOffsSize = z8BLen+2 // 1 byte hot many bits to read for offset +.label z8BLenSize = z8BOffsSize+1 // 1 byte how many bits to read for length +.label z8BInj = z8BLenSize+1 // 2 bytes current injection address +.label z8BTrg = z8BInj+2 // start of target 2 bytes + +.label z8BInjSize = z8BTrg // 2 bytes size of injection buffer +.label z8BInjEnd = z8BDiff // 2 bytes keeps track of end condition + +// macro for reading one bit +.macro GetBit() { + asl + bne !BitOk+ + jsr GetByte +!BitOk: +} + +Patch_8BDiff: + ldy #0 // clear Y + lda (z8BDiff),y // first byte is prefix length bits | offset bits<<4 + tax + lsr + lsr + lsr + lsr + sta z8BOffsSize + txa + and #$0f + sta z8BLenSize + inc z8BDiff + bne NotLastByte + inc z8BDiff+1 +NotLastByte: + lda z8BDiff // store off address to length bit sizes + sta GetLenBits+1 + lda z8BDiff+1 + sta GetLenBits+2 + ldx z8BLenSize // prefix bits for length + jsr BitX // A = 1< force fetch +NextInstruction: + :GetBit() // bit is 0 => inject, otherwise source or target buffer + bcs SrcTrg + ldx z8BInj // check if complete + cpx z8BInjEnd + bcc NotEnd + ldx z8BInj+1 + cpx z8BInjEnd+1 + bcc NotEnd + rts // patching is complete, return to caller +NotEnd: + jsr GetLen // number of bytes to inject y = 0 here + pha // save bit shift byte + ldx #z8BInj // use inject buffer + bne MoveToDest // always branch! + // PC will not cross this line +SrcTrg: + jsr GetLen // number of bytes to copy + jsr GetOffs // offset in buffer, y = 0 here + :GetBit() // check sign bit + bcc PositiveOffs + pha + lda #$ff // apply negativity to offset + eor z8BOff + sta z8BOff + lda #$ff + eor z8BOff+1 + sta z8BOff+1 + pla +PositiveOffs: + :GetBit() + ldx #z8BSrc // use source buffer + bcc Src + ldx #z8BTrg // use target buffer +Src: + pha // save bit shift byte + clc + lda z8BOff // apply offset to current buffer + adc $00,x + sta $00,x + lda z8BOff+1 + adc $01,x + sta $01,x + +MoveToDest: + stx BufferCopyTrg+1 // x = zero page source buffer to copy from + +PageCopy: // copy pages (256 bytes) at a time + dec z8BLen+1 + bmi PageCopyDone + jsr BufferCopy // y is 0 here so copy 256 bytes + inc $01,x + inc z8BDst+1 + bne PageCopy + // PC will not cross this line +PageCopyDone: + ldy z8BLen // get remaining number of bytes in y + beq NoLowLength + jsr BufferCopy + lda z8BLen // apply remainder of bytes to buffer + jsr ApplyOffsetA + lda z8BLen + ldx #z8BDst // apply remainder of bytes to destination + jsr ApplyOffsetA +NoLowLength: + pla // retrieve bit shift byte to parse next bit + bne NextInstruction + // PC will not cross this line + +// BufferCopy copies bytes forward from 0 to y (256 if y is 0) +BufferCopy: + sty BufferCopyLength+1 + ldy #0 +BufferCopyTrg: + lda (z8BInj),y + sta (z8BDst),y + iny +BufferCopyLength: + cpy #0 + bne BufferCopyTrg // Y returns unchanged + rts + +// Gets Y number of bits and return the value in Y +GetLenOffBits: + :GetBit() + rol z8BTemp + dey + bne GetLenOffBits + ldy z8BTemp + rts + +// Gets the next length +GetLen: + ldy #0 + sty z8BTemp + ldy z8BLenSize + jsr GetLenOffBits + pha // save bit shift byte +GetLenBits: + lda $1234,y + ldx #z8BLen // X is the zero page address to store in +GetLenOffValue: + tay + lda #0 // clear target indirect address + sta $00,x + sta $01,x + pla // retrieve bit shift byte +GetLenOffLoop: + :GetBit() + rol $00,x + rol $01,x + dey + bne GetLenOffLoop // y returns 0 + rts + +// Gets the next buffer offset +GetOffs: + ldy #0 + sty z8BTemp + ldy z8BOffsSize + jsr GetLenOffBits + pha // save bit shift byte +GetOffsBits: + lda $1234,y + ldx #z8BOff // X is the zero page address to store in + bne GetLenOffValue + +// Gets one byte of bits and returns top bit in C +GetByte: + sec +DiffPtr: + lda $1234 + rol + inc DiffPtr+1 + bne DiffPage + inc DiffPtr+2 +DiffPage: + rts + +// Returns 1<$123456 + lda >$123456,x + lda (<$1234,s),y + ora ($21,x) + jsr ($2120,x) + + lda [<$1234],y + lda (<$1234,x) + + lda <$101030 + } + + { + lda.z $101030 // .z zero page + if !((*-!) == 2) + err Expected zero page instruction + endif + } + + { + lda.a $101030 // .a force absolute + if !((*-!) == 3) + err Expected zero page instruction + endif + } + { + lda.l $30 // .l force long + if !((*-!) == 4) + err Expected zero page instruction + endif + } + + + { + jmp [$1010] + } + + rts + diff --git a/test/Test65816_OpCodes.s b/test/Test65816_OpCodes.s new file mode 100644 index 0000000..f924b11 --- /dev/null +++ b/test/Test65816_OpCodes.s @@ -0,0 +1,301 @@ +cpu 65816 + +TestOpcodes: + + brk + jsr $2120 + jsr ($2120,x) + jsr.l $222120 + jsl $222120 + rti + rts + rtl + ora ($21,x) + ora $21 + ora.b #$21 + ora.w #$2322 + ora $2120 + ora ($21),y + ora $21,x + ora $2120,y + ora $2120,x + ora ($21) + ora [$21] + ora [$21],y + ora.l $222120 + ora.l $222120,x + ora $21,s + ora ($21,s),y + and ($21,x) + and $21 + and.b #$21 + and.w #$2322 + and $2120 + and ($21),y + and $21,x + and $2120,y + and $2120,x + and ($21) + and [$21] + and [$21],y + and.l $222120 + and.l $222120,x + and $21,s + and ($21,s),y + eor ($21,x) + eor $21 + eor.b #$21 + eor.w #$2322 + eor $2120 + eor ($21),y + eor $21,x + eor $2120,y + eor $2120,x + eor ($21) + eor [$21] + eor [$21],y + eor.l $222120 + eor.l $222120,x + eor $21,s + eor ($21,s),y + adc ($21,x) + adc $21 + adc.b #$21 + adc.w #$2322 + adc $2120 + adc ($21),y + adc $21,x + adc $2120,y + adc $2120,x + adc ($21) + adc [$21] + adc [$21],y + adc.l $222120 + adc.l $222120,x + adc $21,s + adc ($21,s),y + sta ($21,x) + sta $21 + sta $2120 + sta ($21),y + sta $21,x + sta $2120,y + sta $2120,x + sta ($21) + sta [$21] + sta [$21],y + sta.l $222120 + sta.l $222120,x + sta $21,s + sta ($21,s),y + lda ($21,x) + lda $21 + lda.b #$21 + lda.w #$2322 + lda $2120 + lda ($21),y + lda $21,x + lda $2120,y + lda $2120,x + lda ($21) + lda [$21] + lda [$21],y + lda.l $222120 + lda.l $222120,x + lda $21,s + lda ($21,s),y + cmp ($21,x) + cmp $21 + cmp.b #$21 + cmp.w #$2322 + cmp $2120 + cmp ($21),y + cmp $21,x + cmp $2120,y + cmp $2120,x + cmp ($21) + cmp [$21] + cmp [$21],y + cmp.l $222120 + cmp.l $222120,x + cmp $21,s + cmp ($21,s),y + sbc ($21,x) + sbc $21 + sbc.b #$21 + sbc.w #$2322 + sbc $2120 + sbc ($21),y + sbc $21,x + sbc $2120,y + sbc $2120,x + sbc ($21) + sbc [$21] + sbc [$21],y + sbc.l $222120 + sbc.l $222120,x + sbc $21,s + sbc ($21,s),y + oral $222120 + oral $222120,x + andl $222120 + andl $222120,x + eorl $222120 + eorl $222120,x + adcl $222120 + adcl $222120,x + stal $222120 + stal $222120,x + ldal $222120 + ldal $222120,x + cmpl $222120 + cmpl $222120,x + sbcl $222120 + sbcl $222120,x + asl $21 + asl $2120 + asl $21,x + asl $2120,x + asl A + asl + rol $21 + rol $2120 + rol $21,x + rol $2120,x + rol A + rol + lsr $21 + lsr $2120 + lsr $21,x + lsr $2120,x + lsr A + lsr + ror $21 + ror $2120 + ror $21,x + ror $2120,x + ror A + ror + stx $21 + stx $2120 + stx $21,y + ldx $21 + ldx.b #$21 + ldx.w #$2322 + ldx $2120 + ldx $21,y + ldx $2120,y + dec $21 + dec $2120 + dec $21,x + dec $2120,x + dec A + dec + inc $21 + inc $2120 + inc $21,x + inc $2120,x + inc A + inc + dea + ina + php + plp + pha + pla + phy + ply + phx + plx + dey + tay + iny + inx + bpl *+5 + bmi *+5 + bvc *+5 + bvs *+5 + bra *+5 + brl $2120 + bcc *+5 + bcs *+5 + bne *+5 + beq *+5 + clc + sec + cli + sei + tya + clv + cld + sed + bit $21 + bit.b #$21 + bit.w #$2322 + bit $2120 + bit $21,x + bit $2120,x + stz $21 + stz $2120 + stz $21,x + stz $2120,x + trb $21 + trb $2120 + tsb $21 + tsb $2120 + jmp $2120 + jmp ($2120) + jmp ($2120,x) + jmp.l $222120 + jmp [$2120] + jml.l $222120 + jml [$2120] + sty $21 + sty $2120 + sty $21,x + ldy $21 + ldy.b #$21 + ldy.w #$2322 + ldy $2120 + ldy $21,x + ldy $2120,x + cpy $21 + cpy.b #$21 + cpy.w #$2322 + cpy $2120 + cpx $21 + cpx.b #$21 + cpx.w #$2322 + cpx $2120 + txa + txs + tax + tsx + dex + nop + cop + wdm + mvp $21,$20 + mvn $21,$20 + pea $2120 + pei ($21) + per $2120 + rep $21 + rep #$21 + sep $21 + sep #$21 + phd + tcs + pld + tsc + phk + tcd + tdc + phb + txy + plb + tyx + wai + stp + xba + xce diff --git a/test/alias_test.s b/test/alias_test.s new file mode 100644 index 0000000..3074124 --- /dev/null +++ b/test/alias_test.s @@ -0,0 +1,4 @@ +Again + rol + bge Again + blt Again \ No newline at end of file diff --git a/test/compare/8BitDiff_6502_cmp.prg b/test/compare/8BitDiff_6502_cmp.prg new file mode 100644 index 0000000000000000000000000000000000000000..7b858bcc87bb602ed834a2720c80c1a8bb364da8 GIT binary patch literal 349 zcmXv}Jxjw-6n(E1L!JwDGx$xbBsSM#-Au1#COi_ literal 0 HcmV?d00001 diff --git a/test/compare/alias_test_cmp.prg b/test/compare/alias_test_cmp.prg new file mode 100644 index 0000000000000000000000000000000000000000..09f8958bd5a9899568ef056606fdcb0912f16c87 GIT binary patch literal 7 OcmZP&(Aw~K!fyZx3j=ci literal 0 HcmV?d00001 diff --git a/test/compare/huffman_cmp.prg b/test/compare/huffman_cmp.prg new file mode 100644 index 0000000000000000000000000000000000000000..024ebc1e796a4d2f5b70d3169cf113889e2b2827 GIT binary patch literal 1819 zcmZ8heQZ-z6usxZB2~Og zS$0`x5Qk=3o84u%W@h`tk%a)0k5L*Ht}zi32O$c}rU`Ug>Ds=&_dIVfqu%7)chB#f zch0%z<04lRdlV~d9ab5_MJB{YVqV7@uVco;gjq}hT!wRz>9E8cI1lH7c}wvckp%%qw#L>$`o0C)7e*e0;M2dj>YZpY&uh7wq9@j1g_pj6Y+fz%3?MX+oE z%Vu1ls?WI(ez@rhYOXb{(UA(yrC?&b<%J!tYhiv2d>ZHC>S6l0p*Pl^&|%`xw?y~ zPINgXz=fSrb;K`Vo1aa zRYM4?S!>gddwlH=FRJj+nr)VYj9(EcX2kJv|v{mRO64wdh< zkM`QHur^Y`*+-<(KopgENR@%8D$|jLciUq%)@|~Cck}{5U4whtq8;Iguf^Bl`}5x` z7Kyj9E~4Yaz)1-wCQdAz%;Ut)Nf~d2P+Idq1t1Bi2qXh3rh}>}hLVFjJ-j4_dUtwS zlOBj>@B3_7v#xY&nk(fJ|2#lfP1pyrn#OQZd&S7 zd@3T`njMEqXi$XH>^?jhL$uhQ9-GuMR-|%!8geENPbP;p@G;O)v~h`{&kFrY63hLH zFWIk%fdVeTH~Cq%r$3EzW0SDwhbMyxTEH1-5E7}U8K(su+S=e?9B95{5y9C^z7u!_@##0cKQ!Z;n;seB9Wq#?8%2 zyefvo{#-jm^Up?ep{hAoDgHYxgj_I!5_pzqZsJExG@--!ZY9M}h@gCJR#`(}*n0e7 zmqRcc^}6n2bCIc7ilKgm&!ZB|;2D`ur)zxOQd+-W#f4zpWQ~^jjvsFDLO!Sjm=sJ2 zf6>An-E$N&CC}XrCV}_*bp}(}{m<=Fsba+$rmMlXX$DU8_J2>Z4{kcm!gvZ&pX+d% z@FX<)j7X;ib3pF1G88Qh$Wc90&rUs@#nW{+rlNX0-90nj;xzGJyC!M2J<}nE%y{y8 zJ~}K?IUr%VE0+XQ{0EWd0~yUJV>mAy@<$PR z-L=_a(<4PT456ZAu#~)@N3Q4AqG#3s@brp4ved$H7UZ*}{XkWT5cR@INpls~j>&fY>GhM%&J4DNu zjFerlJ+bYnk+1je-KU=1@87cUFef(ZzH*S1qV6|faA>AkYZ8qc9 z+8-N{ZPlu+;i2KfUy#$)Z!BI=-87ns?LT09Y-{sk%bMqxekwljso9~nzWw>Y_U%vT z!U%2HeBH9NqSn{`BL01lJ JXdh{Q=5NbhqtyTa literal 0 HcmV?d00001 diff --git a/test/compare/merlin_lup_cmp.bin b/test/compare/merlin_lup_cmp.bin new file mode 100644 index 0000000..815f411 --- /dev/null +++ b/test/compare/merlin_lup_cmp.bin @@ -0,0 +1 @@ +¡O‘îOÐîPÈ¡O‘îOÐîPÈ¡O‘îOÐîPÈ¡O‘îOÐîPÈ¡O‘îOÐîPÈ¡O‘îOÐîPÈ` \ No newline at end of file diff --git a/test/compare/merlin_macro_cmp.bin b/test/compare/merlin_macro_cmp.bin new file mode 100644 index 0000000000000000000000000000000000000000..189ba676e251f6b6285c4bb8393e99ebe72dfb41 GIT binary patch literal 48 tcmZ2!-^*}g0<#a7KmfCkrhtO3z^hj)8D3ogaS)7NJpl!MfrJEx1OS&Q5JUg~ literal 0 HcmV?d00001 diff --git a/test/compare/x65macro_test_cmp.prg b/test/compare/x65macro_test_cmp.prg new file mode 100644 index 0000000000000000000000000000000000000000..b1a1a6886cbd8eb46c93e31bd732d0801d1b949b GIT binary patch literal 143 zcmZP&u-L%-qT|&{=GH$e1zP{EWN7`jQla(#0;Y|BCjRRX_$jfHIrSe;IDu z6P_*ocamYkgIBL!E&b1x_CM`k+TXN4K;9xAu@7b+%|4lZ268M`GCchQG?3xxUyzYc Q!3I198UWTNAc<2R0N)2$NdN!< literal 0 HcmV?d00001 diff --git a/test/merlin_lup.s b/test/merlin_lup.s new file mode 100644 index 0000000..6b50971 --- /dev/null +++ b/test/merlin_lup.s @@ -0,0 +1,13 @@ +; merlin_lup.s + + lup 6 + lda (sprt,x) + sta ($06),y + inc sprt + bne :c0 + inc sprt+1 +:c0 iny + --^ + rts + +sprt \ No newline at end of file diff --git a/test/merlin_macro.s b/test/merlin_macro.s new file mode 100644 index 0000000..a8c6568 --- /dev/null +++ b/test/merlin_macro.s @@ -0,0 +1,60 @@ + +RAGDAG MAC + lda #]1 + sta ]2 + bcc :CONT_B + jmp ]3 +:CONT_B + EOM + +BEQ_FAR MAC ]1 + nop + { + BNE % + JMP ]1 + } + nop + EOM + +MOCMAC MAC +{ + bvc % + jmp ]1 +} + EOM + + +JUST_NOPNOP MAC + nop + nop + EOM + + RAGDAG 63; $d800; START + +START + MOCMAC :NEXT + + JSR INIT + + JUST_NOPNOP + +:CONT LDA #00 + BEQ_FAR :NEXT + + LDA #00 + BEQ_FAR :NEXT + + STA TEMP +:NEXT + JSR REDUCESTARSPEED + + rts + +INIT + rts +TEMP + dc.b 0 +REDUCESTARSPEED: + rts + + diff --git a/test/unittest.bat b/test/unittest.bat new file mode 100644 index 0000000..281248d --- /dev/null +++ b/test/unittest.bat @@ -0,0 +1,83 @@ +@echo off + +echo Unit tests for x65 assembler >unittest.txt +echo >>unittest.txt + +echo 65816 OpCodes Test >>unittest.txt +..\bin\x64\x65 Test65816_OpCodes.s -cpu=65816 -lst >> unittest.txt +if %errorlevel% EQU 0 goto opcodes_65816_pass +:opcodes_65816_fail +echo 65816 OpCodes failed +goto exit +:opcodes_65816_pass + + +echo 65816 Force Addressing Mode Test >>unittest.txt +..\bin\x64\x65 AddrMode_65816.s -cpu=65816 -lst >> unittest.txt +if %errorlevel% EQU 0 goto addrmode_pass +:addrmode_fail +echo Force Addressing Mode failed +goto exit +:addrmode_pass + +echo Merlin Macro Test >>unittest.txt +echo ----------------- >>unittest.txt +..\bin\x64\x65 merlin_macro.s merlin_macro.bin -bin -org=$1000 -merlin -lst >>unittest.txt +if %errorlevel% GTR 0 goto mermac_fail +fc /B compare\merlin_macro_cmp.bin merlin_macro.bin >>unittest.txt +if %errorlevel% EQU 0 goto mermac_pass +:mermac_fail +echo Merlin macro test failed +goto exit +:mermac_pass + +echo 8BitDiff Test >>unittest.txt +echo ------------- >>unittest.txt +..\bin\x64\x65 8BitDiff_6502.s 8BitDiff_6502.prg -kickasm -sym 8BitDiff_6502.sym -lst >>unittest.txt +if %errorlevel% GTR 0 goto 8BitDiff_6502_fail +fc /B compare\8BitDiff_6502_cmp.prg 8BitDiff_6502.prg >>unittest.txt +if %errorlevel% EQU 0 goto 8BitDiff_6502_pass +:8BitDiff_6502_fail +echo 8BitDiff_6502 test failed +goto exit +:8BitDiff_6502_pass + +echo Alias Test >>unittest.txt +echo ---------- >>unittest.txt +..\bin\x64\x65 alias_test.s alias_test.prg -sym alias_test.sym -lst >>unittest.txt +if %errorlevel% GTR 0 goto alias_test_fail +fc /B compare\alias_test_cmp.prg alias_test.prg >>unittest.txt +if %errorlevel% EQU 0 goto alias_test_pass +:alias_test_fail +echo Alias test failed +goto exit +:alias_test_pass + +echo x65macro.i Test >>unittest.txt +echo --------------- >>unittest.txt +..\bin\x64\x65 x65macro_test.s x65macro_test.prg -org=$1000 -sym x65macro_test.sym -lst >>unittest.txt +if %errorlevel% GTR 0 goto x65macro_test_fail +fc /B compare\x65macro_test_cmp.prg x65macro_test.prg >>unittest.txt +if %errorlevel% EQU 0 goto x65macro_test_pass +:x65macro_test_fail +echo x65macro.i test failed +goto exit +:x65macro_test_pass + +echo Merlin LUP Test >>unittest.txt +echo --------------- >>unittest.txt +..\bin\x64\x65 merlin_lup.s merlin_lup.bin -bin -org=$1000 -merlin -lst >>unittest.txt +if %errorlevel% GTR 0 goto merlup_fail +fc /B compare\merlin_lup_cmp.bin merlin_lup.bin >>unittest.txt +if %errorlevel% EQU 0 goto merlup_pass +:merlup_fail +echo Merlin LUP test failed +goto exit +:merlup_pass + + +echo All Tests Passed +goto exit + + +:exit diff --git a/test/x65macro_test.s b/test/x65macro_test.s new file mode 100644 index 0000000..2d2f8dc --- /dev/null +++ b/test/x65macro_test.s @@ -0,0 +1,53 @@ + include "../macros/x65macro.i" + + sec + bcs Begin + +CopyCode + inx + dey + nop +CodeEnd + +CodeSegLen = CodeEnd-CopyCode + ; $fc = CopyCode + ; for ($fe=$2000; $fe<$4000; $fe += (CodeEnd-CopyCode)) { + ; memcpy($fe, $fc, CodeEnd-CopyCode) + ; } +Begin: + set.w CopyCode, $fc + for.wsp $2000, $4000, $fe, CodeSegLen + copy.ry128 $fc, $fe, CodeSegLen + forend + + nop + nop + nop + + ; int $fc + ; $fc >>= 1 + asrm.n $fc,4 + + nop + nop + nop + + ldx #$0c + aslm.nx $f0,4 + + nop + nop + nop + + ; int $fc + ; $fc = -$fc + + neg.n $fc,4 + + nop + nop + nop + + ; int $fc = abs($fc) + abs.n $fc, 4 + diff --git a/x65.cpp b/x65.cpp index 0799d34..1822fbd 100644 --- a/x65.cpp +++ b/x65.cpp @@ -74,6 +74,7 @@ // ruleset can be specified on the command line. enum AsmSyntax { SYNTAX_SANE, + SYNTAX_KICKASM, SYNTAX_MERLIN }; @@ -114,6 +115,7 @@ enum StatusCode { ERROR_DS_MUST_EVALUATE_IMMEDIATELY, ERROR_NOT_AN_X65_OBJECT_FILE, ERROR_COULD_NOT_INCLUDE_FILE, + ERROR_USER, ERROR_STOP_PROCESSING_ON_HIGHER, // errors greater than this will stop execution @@ -185,6 +187,7 @@ const char *aStatusStrings[STATUSCODE_COUNT] = { "DS directive failed to evaluate immediately", "File is not a valid x65 object file", "Failed to read include file", + "User invoked error", "Errors after this point will stop execution", @@ -276,6 +279,7 @@ enum AssemblerDirective { AD_ENT, // ENT: MERLIN extern this address label AD_EXT, // EXT: MERLIN reference this address label from a different file AD_CYC, // CYC: MERLIN start / stop cycle timer + AD_ERROR, }; // Operators are either instructions or directives @@ -864,7 +868,7 @@ static const int nCPUs = sizeof(aCPUs) / sizeof(aCPUs[0]); // hardtexted strings static const strref c_comment("//"); static const strref word_char_range("!0-9a-zA-Z_@$!#"); -static const strref label_end_char_range("!0-9a-zA-Z_@$!."); +static const strref label_end_char_range("!0-9a-zA-Z_@$!.!:"); static const strref label_end_char_range_merlin("!0-9a-zA-Z_@$]:?"); static const strref filename_end_char_range("!0-9a-zA-Z_!@#$%&()/\\-."); static const strref keyword_equ("equ"); @@ -908,7 +912,7 @@ static const char *str_section_type[] = { "CODE", // default type "DATA", // data section (matters for GS/OS OMF) "BSS", // uninitialized data section - "ZEROPAGE" // ununitialized data section in zero page / direct page + "ZEROPAGE" // uninitialized data section in zero page / direct page }; static const int num_section_type_str = sizeof(str_section_type) / sizeof(str_section_type[0]); @@ -1686,6 +1690,7 @@ public: // Syntax bool Merlin() const { return syntax == SYNTAX_MERLIN; } + bool KickAsm() const { return syntax == SYNTAX_KICKASM; } // constructor Asm() : opcode_table(opcodes_6502), opcode_count(num_opcodes_6502), num_instructions(0), @@ -5261,37 +5266,134 @@ StatusCode Asm::GetAddressMode(strref line, bool flipXY, uint32_t &validModes, A bool force_24 = false; bool force_abs = false; bool need_more = true; + bool first = true; strref arg, deco; len = 0; while (need_more) { need_more = false; + line.skip_whitespace(); uint8_t c = line.get_first(); - if (!c) - addrMode = AMB_NON; - else if (!force_abs && (c == '[' || (c == '(' && - (validModes&(AMM_REL | AMM_REL_X | AMM_ZP_REL | AMM_ZP_REL_X | AMM_ZP_Y_REL))))) { - deco = line.scoped_block_skip(); - line.skip_whitespace(); - expression = deco.split_token_trim(','); - addrMode = c == '[' ? (force_zp ? AMB_ZP_REL_L : AMB_REL_L) : (force_zp ? AMB_ZP_REL : AMB_REL); - if (strref::tolower(deco[0]) == 'x') - addrMode = c == '[' ? AMB_ILL : AMB_ZP_REL_X; - else if (line[0] == ',') { - ++line; - line.skip_whitespace(); - if (strref::tolower(line[0]) == 'y') { - if (strref::tolower(deco[0]) == 's') - addrMode = AMB_STK_REL_Y; - else - addrMode = c == '[' ? AMB_ZP_REL_Y_L : AMB_ZP_Y_REL; - ++line; + if (!c) { addrMode = AMB_NON; } + if( c == '[' || c == '(' ) { + strref block_suffix( line.get(), line.scoped_block_comment_len() ); + strref suffix = line.get_skipped( block_suffix.get_len() ); + suffix.trim_whitespace(); + if( suffix.get_first() == ',' ) { ++suffix; suffix.skip_whitespace(); } + else { suffix.clear(); } + ++block_suffix; block_suffix.clip(1); block_suffix.trim_whitespace(); + ++line; line.skip_whitespace(); + strref block = block_suffix.split_token_trim( ',' ); + validModes &= AMM_ZP_REL_X | AMM_ZP_Y_REL | AMM_REL | AMM_ZP_REL | AMM_REL_X | AMM_ZP_REL_L | AMM_ZP_REL_Y_L | AMM_STK_REL_Y | AMM_REL_L; + if( line.get_first() == '>' ) { // [>$aaaa] + if( c == '[' ) { addrMode = AMB_REL_L; validModes &= AMM_REL_L; expression = block+1; } + } else if( line.get_first() == '|' || line.get_first() == '!' && c == '(' ) { // (|$aaaa) or (|$aaaa,x) + strref arg = block.after( ',' ); arg.skip_whitespace(); + if( arg && ( arg.get_first() == 'x' || arg.get_first() == 'X' ) ) { + addrMode = AMB_REL_X; validModes &= AMM_REL_X; expression = block.before( ',' ); } + else { addrMode = AMB_REL; validModes &= AMM_REL; expression = block; } + } else if( line.get_first() == '<' ) { // (<$aa) (<$aa),y (<$aa,x) (<$aa,s),y [<$aa] [<$aa],y + if( suffix ) { + if( suffix.get_first() == 'y' || suffix.get_first() == 'Y' ) { + if( c == '(' ) { // (<$aa),y or (<$aa,s),y + if( block_suffix && ( block_suffix.get_first() == 's' || block_suffix.get_first() == 'S' ) ) { + expression = block+1; + addrMode = AMB_STK_REL_Y; validModes &= AMM_STK_REL_Y; + } else { + expression = block+1; + addrMode = AMB_ZP_Y_REL; validModes &= AMM_ZP_Y_REL; + } + } else { // [<$aa],y + expression = block+1; + addrMode = AMB_ZP_REL_Y_L; validModes &= AMM_ZP_REL_Y_L; + } + } else { return ERROR_BAD_ADDRESSING_MODE; } + } else { // (<$aa) (<$aa,x) [<$aa] + if( c == '[' ) { + if( block.find( ',' ) >= 0 || suffix.get_first() == ',' ) { return ERROR_BAD_ADDRESSING_MODE; } + expression = block+1; + addrMode = AMB_ZP_REL_L; validModes &= AMM_ZP_REL_L; + } else { + if( block_suffix ) { + if( block_suffix.get_first() != 'x' && block_suffix.get_first() != 'X' ) { return ERROR_BAD_ADDRESSING_MODE; } + expression = block+1; + addrMode = AMB_ZP_REL_X; validModes &= AMM_ZP_REL_X; + } else { + expression = block+1; + addrMode = AMB_ZP_REL; validModes &= AMM_ZP_REL; + } + } + } + } else { // no <, |, ! or > decorator inside (...) or [...] + if( c == '[' && ( block_suffix.get_first() == 's' || block_suffix.get_first() == 'S' ) ) { + if( suffix.get_first() == 'y' || suffix.get_first() == 'Y' ) { + expression = block; + addrMode = AMB_STK_REL_Y; validModes &= AMM_STK_REL_Y; + } else { return ERROR_BAD_ADDRESSING_MODE; } + } else if( block_suffix.get_first() == 'x' || block_suffix.get_first() == 'X' ) { // ($aa,x) ($aaaa,x) + if( c == '[' ) { return ERROR_BAD_ADDRESSING_MODE; } + expression = block; + switch( validModes & ( AMM_ZP_REL_X | AMM_REL_X ) ) { + case AMM_ZP_REL_X: addrMode = AMB_ZP_REL_X; validModes = AMM_ZP_REL_X; break; + case AMM_REL_X: addrMode = AMB_REL_X; validModes = AMM_REL_X; break; + default: addrMode = force_zp ? AMB_ZP_REL_X : AMB_REL_X; validModes &= force_zp ? AMM_ZP_REL_X : ( force_abs ? AMM_ZP_REL_X : ( AMM_ZP_REL_X | AMM_REL_X ) ); + break; + } + } else if( suffix && ( suffix.get_first() == 'y' || suffix.get_first() == 'Y' ) ) { + if( c == '[' ) { + expression = block; + addrMode = AMB_ZP_REL_Y_L; validModes &= AMM_ZP_REL_Y_L; + } else { // ($aa),y + expression = block; + addrMode = AMB_ZP_Y_REL; validModes &= AMM_ZP_Y_REL; + } + } else { // ($aa), ($aaaa), [$aa], [$aaaa] + if( c == '[' ) { // [$aa], [$aaaa] + expression = block; + addrMode = force_zp ? AMB_ZP_REL_L : AMB_REL_L; validModes &= force_zp ? AMM_ZP_REL_L : ( force_abs ? AMM_REL_L : ( AMM_ZP_REL_L | AMM_REL_L ) ); + } + else { // ($aa), ($aaaa) + expression = block; + addrMode = force_zp ? AMB_ZP_REL : AMB_REL; validModes &= force_zp ? AMM_ZP_REL : ( force_abs ? AMM_REL : ( AMM_ZP_REL | AMM_REL ) ); + } } } + expression.trim_whitespace(); + } else if (c == '<' ) { // force zero page not indirect + ++line; line.trim_whitespace(); + strref suffix = line.after(','); suffix.skip_whitespace(); + expression = line.before_or_full(','); expression.trim_whitespace(); + if( suffix ) { + if( suffix.get_first() == 's' || suffix.get_first() == 'S' ) { + addrMode = AMB_STK; validModes &= AMM_STK; // not correct usage of < but I'll allow it. + } else if( suffix.get_first() == 'x' || suffix.get_first() == 'X' ) { + addrMode = AMB_ZP_X; validModes &= AMM_ZP_X; + } else { return ERROR_BAD_ADDRESSING_MODE; } + } else { + addrMode = AMB_ZP; validModes &= AMM_ZP; + } + } else if( c == '>' ) { + ++line; line.trim_whitespace(); + strref suffix = line.after( ',' ); suffix.skip_whitespace(); + expression = line.before_or_full( ',' ); expression.trim_whitespace(); + if( suffix ) { + if( suffix.get_first() == 'x' || suffix.get_first() == 'X' ) { + addrMode = AMB_ABS_L_X; validModes &= AMM_ABS_L_X; + } + else { return ERROR_BAD_ADDRESSING_MODE; } + } + else { + addrMode = AMB_ABS_L; validModes &= AMM_ABS_L; + } + } else if (c == '#') { ++line; addrMode = AMB_IMM; + validModes &= AMM_IMM; expression = line; + } else if (c == '<') { + validModes &= AMM_ZP | AMM_ZP_X | AMM_ZP_REL_X | AMM_ZP_Y_REL | + AMM_ZP_REL | AMM_ZP_ABS | AMM_ZP_REL_L | AMM_ZP_REL_Y_L | AMM_FLIPXY; } else if (line) { if (line[0]=='.' && strref::is_ws(line[2])) { switch (strref::tolower(line[1])) { @@ -5309,6 +5411,8 @@ StatusCode Asm::GetAddressMode(strref line, bool flipXY, uint32_t &validModes, A addrMode = force_24 ? AMB_ABS_L : (force_zp ? AMB_ZP : AMB_ABS); expression = line.split_token_trim(','); if( force_abs ) { validModes &= AMM_ABS | AMM_ABS_X | AMM_ABS_Y | AMM_REL | AMM_REL_X; } + if( force_zp ) { validModes &= AMM_ZP | AMM_ZP_X | AMM_ZP_REL_X | AMM_ZP_Y_REL | + AMM_ZP_REL | AMM_ZP_ABS | AMM_ZP_REL_L | AMM_ZP_REL_Y_L | AMM_FLIPXY; } if( line && (line[ 0 ] == 's' || line[ 0 ] == 'S') ) { addrMode = AMB_STK; } else { bool relX = line && (line[0]=='x' || line[0]=='X'); @@ -5323,6 +5427,7 @@ StatusCode Asm::GetAddressMode(strref line, bool flipXY, uint32_t &validModes, A } } } + first = false; } return STATUS_OK; } @@ -5629,16 +5734,17 @@ StatusCode Asm::BuildLine(strref line) { line = line.before_or_full(';'); // clip any line comments line = line.before_or_full(c_comment); line.clip_trailing_whitespace(); - if (line[0]==':'&&!Merlin()) { ++line; } // Kick Assembler macro prefix (incompatible with merlin) + if (KickAsm()&&line.get_first()==':') { ++line; } // Kick Assembler macro prefix (incompatible with merlin and sane syntax) strref line_nocom = line; strref operation = line.split_range(Merlin() ? label_end_char_range_merlin : label_end_char_range); + if( operation.get_last() == ':' ) { operation.clip( 1 ); } char char1 = operation[0]; // first char of first word char charE = operation.get_last(); // end char of first word line.trim_whitespace(); bool force_label = charE==':' || charE=='$'; if (!force_label && Merlin()&&(line||operation)) { // MERLIN fixes and PoP does some naughty stuff like 'and = 0' force_label = (!strref::is_ws(char0)&&char0!='{' && char0!='}')||char1==']'||charE=='?'; - } else if (!Merlin()&&line[0]==':') { force_label = true; } + } /*else if (!Merlin()&&line[0]==':') { force_label = true; }*/ if (!operation && !force_label) { if (ConditionalAsm()) { // scope open / close @@ -7023,6 +7129,7 @@ int main(int argc, char **argv) { if (argv[a][0]=='-') { strref arg(argv[a]+1); if (arg.get_first()=='i') { assembler.AddIncludeFolder(arg+1); } + else if (arg.same_str("kickasm") ) { assembler.syntax = SYNTAX_KICKASM; } else if (arg.same_str("merlin")) { assembler.syntax = SYNTAX_MERLIN; } else if (arg.get_first()=='D'||arg.get_first()=='d') { ++arg;