1
0
mirror of https://github.com/ksherlock/x65.git synced 2024-06-12 07:29:28 +00:00

Starting WDC syntax support, adding unit test to repo.

This commit is contained in:
Carl-Henrik Skårstedt 2019-09-19 18:50:30 -07:00
parent 23aedcf686
commit 482470b410
18 changed files with 993 additions and 36 deletions

View File

@ -239,7 +239,7 @@ macro asrm.n Trg,Size
lda Trg+Size-1 lda Trg+Size-1
asl asl
rept Size { 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 lda Trg+Size-1,x
asl asl
rept Size { 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) if (Size<256)
copy.x Src,Trg,Size copy.x Src,Trg,Size
else else
{ {
PoolZP zpSrc.w PoolZP zpSrc.w
PoolZP zpTrg.w PoolZP zpTrg.w
@ -535,7 +535,7 @@ macro copy.zp Src,Trg,Size,zpTmp1,zpTmp2
{ {
if (Size<256) if (Size<256)
copy.x Src,Trg,Size copy.x Src,Trg,Size
else else
{ {
set.w zpTmp1,Src set.w zpTmp1,Src
set.w zpTmp2,Trg set.w zpTmp2,Trg
@ -575,7 +575,7 @@ macro copy.a Src,Trg,Size
{ {
if (Size<256) if (Size<256)
copy.x Src,Trg,Size copy.x Src,Trg,Size
else else
{ {
set.b >Src, ._addr+2 set.b >Src, ._addr+2
set.b >Trg, ._addr+5 set.b >Trg, ._addr+5

View File

@ -72,22 +72,22 @@
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -72,23 +72,23 @@
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)$(Configuration)\</OutDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir> <OutDir>$(SolutionDir)..\bin\$(Platform)\</OutDir>
<IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\obj\$(Platform)$(Configuration)$(ProjectName)\</IntDir>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>

293
test/8BitDiff_6502.s Normal file
View File

@ -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<<X, does not change Y
ldx #z8BDiff // add A to z8BDiff
jsr ApplyOffsetA // does not change Y
lda z8BDiff // store off address to offset bit sizes
sta GetOffsBits+1
lda z8BDiff+1
sta GetOffsBits+2
ldx z8BOffsSize // prefix bits for offset
jsr BitX // A = 1<<X, does not change Y
ldx #z8BDiff // add A to z8BDiff
jsr ApplyOffsetA // does not change Y
// read out injected bytes size (only up to $7fff supported)
lda (z8BDiff),y
pha
iny
lda (z8BDiff),y
pha
iny
ldx #z8BDiff // skip inject size bytes (2)
jsr ApplyOffsetY
clc
lda z8BDiff // store off inject buffer address high
sta z8BInj
pla
adc z8BDiff
sta DiffPtr+1 // store off instruction start low
sta z8BInjEnd // store off inject buffer end low
lda z8BDiff+1
sta z8BInj+1 // store off inject buffer address high
pla
adc z8BDiff+1
sta DiffPtr+2 // store off instruction start high
sta z8BInjEnd+1 // store off inject buffer end high
lda z8BDst // store off target buffer
sta z8BTrg
lda z8BDst+1
sta z8BTrg+1
// read instructions!
lda #0 // clear bit shift byte => 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<<X
BitX: // does not change Y
lda #0
sec
BitXShift:
rol
dex
bpl BitXShift
rts
// Apply an offset to a zero page indirect address in X
ApplyOffsetY:
tya
ApplyOffsetA:
clc
adc $00,x
sta $00,x
bcc ApplyLow
inc $01,x
ApplyLow:
rts

42
test/AddrMode_65816.s Normal file
View File

@ -0,0 +1,42 @@
cpu 65816
Test65816_ForceAddrMode:
{
jmp >$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

301
test/Test65816_OpCodes.s Normal file
View File

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

4
test/alias_test.s Normal file
View File

@ -0,0 +1,4 @@
Again
rol
bge Again
blt Again

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
¡OîOÐîPÈ¡OîOÐîPÈ¡OîOÐîPÈ¡OîOÐîPÈ¡OîOÐîPÈ¡OîOÐîPÈ`

Binary file not shown.

Binary file not shown.

13
test/merlin_lup.s Normal file
View File

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

60
test/merlin_macro.s Normal file
View File

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

83
test/unittest.bat Normal file
View File

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

53
test/x65macro_test.s Normal file
View File

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

153
x65.cpp
View File

@ -74,6 +74,7 @@
// ruleset can be specified on the command line. // ruleset can be specified on the command line.
enum AsmSyntax { enum AsmSyntax {
SYNTAX_SANE, SYNTAX_SANE,
SYNTAX_KICKASM,
SYNTAX_MERLIN SYNTAX_MERLIN
}; };
@ -114,6 +115,7 @@ enum StatusCode {
ERROR_DS_MUST_EVALUATE_IMMEDIATELY, ERROR_DS_MUST_EVALUATE_IMMEDIATELY,
ERROR_NOT_AN_X65_OBJECT_FILE, ERROR_NOT_AN_X65_OBJECT_FILE,
ERROR_COULD_NOT_INCLUDE_FILE, ERROR_COULD_NOT_INCLUDE_FILE,
ERROR_USER,
ERROR_STOP_PROCESSING_ON_HIGHER, // errors greater than this will stop execution 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", "DS directive failed to evaluate immediately",
"File is not a valid x65 object file", "File is not a valid x65 object file",
"Failed to read include file", "Failed to read include file",
"User invoked error",
"Errors after this point will stop execution", "Errors after this point will stop execution",
@ -276,6 +279,7 @@ enum AssemblerDirective {
AD_ENT, // ENT: MERLIN extern this address label AD_ENT, // ENT: MERLIN extern this address label
AD_EXT, // EXT: MERLIN reference this address label from a different file AD_EXT, // EXT: MERLIN reference this address label from a different file
AD_CYC, // CYC: MERLIN start / stop cycle timer AD_CYC, // CYC: MERLIN start / stop cycle timer
AD_ERROR,
}; };
// Operators are either instructions or directives // Operators are either instructions or directives
@ -864,7 +868,7 @@ static const int nCPUs = sizeof(aCPUs) / sizeof(aCPUs[0]);
// hardtexted strings // hardtexted strings
static const strref c_comment("//"); static const strref c_comment("//");
static const strref word_char_range("!0-9a-zA-Z_@$!#"); 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 label_end_char_range_merlin("!0-9a-zA-Z_@$]:?");
static const strref filename_end_char_range("!0-9a-zA-Z_!@#$%&()/\\-."); static const strref filename_end_char_range("!0-9a-zA-Z_!@#$%&()/\\-.");
static const strref keyword_equ("equ"); static const strref keyword_equ("equ");
@ -908,7 +912,7 @@ static const char *str_section_type[] = {
"CODE", // default type "CODE", // default type
"DATA", // data section (matters for GS/OS OMF) "DATA", // data section (matters for GS/OS OMF)
"BSS", // uninitialized data section "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]); static const int num_section_type_str = sizeof(str_section_type) / sizeof(str_section_type[0]);
@ -1686,6 +1690,7 @@ public:
// Syntax // Syntax
bool Merlin() const { return syntax == SYNTAX_MERLIN; } bool Merlin() const { return syntax == SYNTAX_MERLIN; }
bool KickAsm() const { return syntax == SYNTAX_KICKASM; }
// constructor // constructor
Asm() : opcode_table(opcodes_6502), opcode_count(num_opcodes_6502), num_instructions(0), 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_24 = false;
bool force_abs = false; bool force_abs = false;
bool need_more = true; bool need_more = true;
bool first = true;
strref arg, deco; strref arg, deco;
len = 0; len = 0;
while (need_more) { while (need_more) {
need_more = false; need_more = false;
line.skip_whitespace();
uint8_t c = line.get_first(); uint8_t c = line.get_first();
if (!c) if (!c) { addrMode = AMB_NON; }
addrMode = AMB_NON; if( c == '[' || c == '(' ) {
else if (!force_abs && (c == '[' || (c == '(' && strref block_suffix( line.get(), line.scoped_block_comment_len() );
(validModes&(AMM_REL | AMM_REL_X | AMM_ZP_REL | AMM_ZP_REL_X | AMM_ZP_Y_REL))))) { strref suffix = line.get_skipped( block_suffix.get_len() );
deco = line.scoped_block_skip(); suffix.trim_whitespace();
line.skip_whitespace(); if( suffix.get_first() == ',' ) { ++suffix; suffix.skip_whitespace(); }
expression = deco.split_token_trim(','); else { suffix.clear(); }
addrMode = c == '[' ? (force_zp ? AMB_ZP_REL_L : AMB_REL_L) : (force_zp ? AMB_ZP_REL : AMB_REL); ++block_suffix; block_suffix.clip(1); block_suffix.trim_whitespace();
if (strref::tolower(deco[0]) == 'x') ++line; line.skip_whitespace();
addrMode = c == '[' ? AMB_ILL : AMB_ZP_REL_X; strref block = block_suffix.split_token_trim( ',' );
else if (line[0] == ',') { 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;
++line; if( line.get_first() == '>' ) { // [>$aaaa]
line.skip_whitespace(); if( c == '[' ) { addrMode = AMB_REL_L; validModes &= AMM_REL_L; expression = block+1; }
if (strref::tolower(line[0]) == 'y') { } else if( line.get_first() == '|' || line.get_first() == '!' && c == '(' ) { // (|$aaaa) or (|$aaaa,x)
if (strref::tolower(deco[0]) == 's') strref arg = block.after( ',' ); arg.skip_whitespace();
addrMode = AMB_STK_REL_Y; if( arg && ( arg.get_first() == 'x' || arg.get_first() == 'X' ) ) {
else addrMode = AMB_REL_X; validModes &= AMM_REL_X; expression = block.before( ',' ); }
addrMode = c == '[' ? AMB_ZP_REL_Y_L : AMB_ZP_Y_REL; else { addrMode = AMB_REL; validModes &= AMM_REL; expression = block; }
++line; } 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 == '#') { } else if (c == '#') {
++line; ++line;
addrMode = AMB_IMM; addrMode = AMB_IMM;
validModes &= AMM_IMM;
expression = line; 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) { } else if (line) {
if (line[0]=='.' && strref::is_ws(line[2])) { if (line[0]=='.' && strref::is_ws(line[2])) {
switch (strref::tolower(line[1])) { 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); addrMode = force_24 ? AMB_ABS_L : (force_zp ? AMB_ZP : AMB_ABS);
expression = line.split_token_trim(','); expression = line.split_token_trim(',');
if( force_abs ) { validModes &= AMM_ABS | AMM_ABS_X | AMM_ABS_Y | AMM_REL | AMM_REL_X; } 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; } if( line && (line[ 0 ] == 's' || line[ 0 ] == 'S') ) { addrMode = AMB_STK; }
else { else {
bool relX = line && (line[0]=='x' || line[0]=='X'); 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; 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(';'); // clip any line comments
line = line.before_or_full(c_comment); line = line.before_or_full(c_comment);
line.clip_trailing_whitespace(); 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 line_nocom = line;
strref operation = line.split_range(Merlin() ? label_end_char_range_merlin : label_end_char_range); 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 char1 = operation[0]; // first char of first word
char charE = operation.get_last(); // end char of first word char charE = operation.get_last(); // end char of first word
line.trim_whitespace(); line.trim_whitespace();
bool force_label = charE==':' || charE=='$'; bool force_label = charE==':' || charE=='$';
if (!force_label && Merlin()&&(line||operation)) { // MERLIN fixes and PoP does some naughty stuff like 'and = 0' 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=='?'; 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 (!operation && !force_label) {
if (ConditionalAsm()) { if (ConditionalAsm()) {
// scope open / close // scope open / close
@ -7023,6 +7129,7 @@ int main(int argc, char **argv) {
if (argv[a][0]=='-') { if (argv[a][0]=='-') {
strref arg(argv[a]+1); strref arg(argv[a]+1);
if (arg.get_first()=='i') { assembler.AddIncludeFolder(arg+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.same_str("merlin")) { assembler.syntax = SYNTAX_MERLIN; }
else if (arg.get_first()=='D'||arg.get_first()=='d') { else if (arg.get_first()=='D'||arg.get_first()=='d') {
++arg; ++arg;