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 0000000..7b858bc
Binary files /dev/null and b/test/compare/8BitDiff_6502_cmp.prg differ
diff --git a/test/compare/alias_test_cmp.prg b/test/compare/alias_test_cmp.prg
new file mode 100644
index 0000000..09f8958
Binary files /dev/null and b/test/compare/alias_test_cmp.prg differ
diff --git a/test/compare/huffman_cmp.prg b/test/compare/huffman_cmp.prg
new file mode 100644
index 0000000..024ebc1
Binary files /dev/null and b/test/compare/huffman_cmp.prg differ
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 0000000..189ba67
Binary files /dev/null and b/test/compare/merlin_macro_cmp.bin differ
diff --git a/test/compare/x65macro_test_cmp.prg b/test/compare/x65macro_test_cmp.prg
new file mode 100644
index 0000000..b1a1a68
Binary files /dev/null and b/test/compare/x65macro_test_cmp.prg differ
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;