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

View File

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

View File

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