1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-12 06:29:34 +00:00

Improvements for 65CE02 assembly (fixes #116)

This commit is contained in:
Karol Stasiak 2021-06-29 02:29:30 +02:00
parent faf97cee1f
commit 7f0def54bc
9 changed files with 167 additions and 13 deletions

View File

@ -693,6 +693,8 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
parameter match { parameter match {
case StructureConstant(_, List(a,b)) => s" $opcode $a,$b" case StructureConstant(_, List(a,b)) => s" $opcode $a,$b"
} }
} else if (addrMode == LongRelative && opcode != BSR) { // BSR on Hudson is always 8-bit short, and on 65CE02 it's always 16-bit
s" L$opcode ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
} else if (addrMode == ImmediateWithAbsolute || addrMode == ImmediateWithZeroPage) { } else if (addrMode == ImmediateWithAbsolute || addrMode == ImmediateWithZeroPage) {
parameter match { parameter match {
case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b" case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b"

View File

@ -180,20 +180,31 @@ object Opcode extends Enumeration {
case "AXA" => AHX case "AXA" => AHX
case "AXS" => SBX // could mean SAX case "AXS" => SBX // could mean SAX
case "BCC" => BCC case "BCC" => BCC
case "LBCC" => BCC
case "BCS" => BCS case "BCS" => BCS
case "LBCS" => BCS
case "BEQ" => BEQ case "BEQ" => BEQ
case "LBEQ" => BEQ
case "BIT" => BIT case "BIT" => BIT
case "BMI" => BMI case "BMI" => BMI
case "LBMI" => BMI
case "BNE" => BNE case "BNE" => BNE
case "LBNE" => BNE
case "BPL" => BPL case "BPL" => BPL
case "LBPL" => BPL
case "BRA" => BRA case "BRA" => BRA
case "LBRA" => BRA
case "BRK" => BRK case "BRK" => BRK
case "BRL" => BRL case "BRL" => BRL
case "BSR" => BSR case "BSR" => BSR
case "LBSR" => BSR
case "BVC" => BVC case "BVC" => BVC
case "LBVC" => BVC
case "BVS" => BVS case "BVS" => BVS
case "LBVS" => BVS
case "CLC" => CLC case "CLC" => CLC
case "CLD" => CLD case "CLD" => CLD
case "CLE" => CLE
case "CLI" => CLI case "CLI" => CLI
case "CLV" => CLV case "CLV" => CLV
case "CLX" => CLX case "CLX" => CLX
@ -232,6 +243,7 @@ object Opcode extends Enumeration {
case "LSE" => SRE case "LSE" => SRE
case "LSR" => LSR case "LSR" => LSR
case "LXA" => LXA case "LXA" => LXA
case "MAP" => MAP
case "NEG" => NEG case "NEG" => NEG
case "NOP" => NOP case "NOP" => NOP
case "OAL" => LXA case "OAL" => LXA
@ -247,17 +259,19 @@ object Opcode extends Enumeration {
case "PHW" => PHW case "PHW" => PHW
case "PHX" => PHX case "PHX" => PHX
case "PHY" => PHY case "PHY" => PHY
case "PHZ" => PHZ
case "PLA" => PLA case "PLA" => PLA
case "PLB" => PLB case "PLB" => PLB
case "PLD" => PLD case "PLD" => PLD
case "PLP" => PLP case "PLP" => PLP
case "PLX" => PLX case "PLX" => PLX
case "PLY" => PLY case "PLY" => PLY
case "PLZ" => PLZ
case "REP" => REP case "REP" => REP
case "RLA" => RLA case "RLA" => RLA
case "ROL" => ROL case "ROL" => ROL
case "ROR" => ROR case "ROR" => ROR
case "ROW" => ROR_W // TODO: is this correct? case "ROW" => ROL_W
case "RRA" => RRA case "RRA" => RRA
case "RTI" => RTI case "RTI" => RTI
case "RTL" => RTL case "RTL" => RTL
@ -268,6 +282,7 @@ object Opcode extends Enumeration {
case "SBX" => SBX case "SBX" => SBX
case "SEC" => SEC case "SEC" => SEC
case "SED" => SED case "SED" => SED
case "SEE" => SEE
case "SEI" => SEI case "SEI" => SEI
case "SEP" => SEP case "SEP" => SEP
case "SET" => SET case "SET" => SET

View File

@ -164,14 +164,14 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
val c: Constant = compileParameterForAssemblyStatement(env, o, x) val c: Constant = compileParameterForAssemblyStatement(env, o, x)
val actualAddrMode = a match { val actualAddrMode = a match {
case Absolute if OpcodeClasses.ShortBranching(o) => Relative case Absolute if OpcodeClasses.ShortBranching(o) => Relative
case Absolute if (o == ROR_W || o == ASL_W) && ctx.options.flag(CompilationFlag.Emit65CE02Opcodes) => case Absolute if (o == ROL_W || o == ASL_W) && ctx.options.flag(CompilationFlag.Emit65CE02Opcodes) =>
Absolute Absolute
case Absolute if OpcodeClasses.SupportsZeropage(o) && c.fitsProvablyIntoByte => ZeroPage case Absolute if OpcodeClasses.SupportsZeropage(o) && c.fitsProvablyIntoByte => ZeroPage
case ImmediateWithAbsolute if (c match { case ImmediateWithAbsolute if (c match {
case StructureConstant(_, List(a, b)) => b.fitsProvablyIntoByte case StructureConstant(_, List(a, b)) => b.fitsProvablyIntoByte
}) => ImmediateWithZeroPage }) => ImmediateWithZeroPage
case IndexedX if o == JMP => AbsoluteIndexedX case IndexedX if o == JMP || o == JSR => AbsoluteIndexedX
case Indirect if o != JMP => IndexedZ case Indirect if o != JMP && o != JSR => IndexedZ
case _ => a case _ => a
} }
List(AssemblyLine(o, actualAddrMode, c, e)) -> Nil List(AssemblyLine(o, actualAddrMode, c, e)) -> Nil

View File

@ -64,6 +64,11 @@ class MosAssembler(program: Program,
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options)) writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param) writeWord(bank, index + 1, param)
index + 3 index + 3
case AssemblyLine0(op, am@LongRelative, param) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
// TODO:
writeWord(bank, index + 1, param - (index + 3))
index + 3
case AssemblyLine0(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param) => case AssemblyLine0(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options)) writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param) writeWord(bank, index + 1, param)
@ -720,8 +725,7 @@ object MosAssembler {
ce(DEC_W, ZeroPage, 0xC3) ce(DEC_W, ZeroPage, 0xC3)
ce(INC_W, ZeroPage, 0xE3) ce(INC_W, ZeroPage, 0xE3)
ce(ASL_W, Absolute, 0xCB) ce(ASL_W, Absolute, 0xCB)
// TODO: or is it ROL_W? ce(ROL_W, Absolute, 0xEB)
ce(ROR_W, Absolute, 0xEB)
ce(ASR, Implied, 0x43) ce(ASR, Implied, 0x43)
ce(ASR, ZeroPage, 0x44) ce(ASR, ZeroPage, 0x44)
ce(ASR, ZeroPageX, 0x54) ce(ASR, ZeroPageX, 0x54)
@ -738,10 +742,26 @@ object MosAssembler {
ce(PHW, Absolute, 0xFC) ce(PHW, Absolute, 0xFC)
ce(PHZ, Implied, 0xDB) ce(PHZ, Implied, 0xDB)
ce(PLZ, Implied, 0xFB) ce(PLZ, Implied, 0xFB)
// ce(CLE, Implied, ) ce(JSR, Indirect, 0x22)
// ce(SEE, Implied, ) ce(JSR, AbsoluteIndexedX, 0x23)
// ce(BSR, , ) ce(CLE, Implied, 0x02)
ce(SEE, Implied, 0x03)
ce(NEG, Implied, 0x42)
ce(MAP, Implied, 0x5C)
ce(LDA, IndexedSY, 0xE2)
ce(STA, IndexedSY, 0x82)
ce(BSR, LongRelative, 0x63)
ce(BRA, LongRelative, 0x83)
ce(BPL, LongRelative, 0x13)
ce(BMI, LongRelative, 0x33)
ce(BVC, LongRelative, 0x53)
ce(BVS, LongRelative, 0x73)
ce(BCC, LongRelative, 0x93)
ce(BCS, LongRelative, 0xb3)
ce(BNE, LongRelative, 0xd3)
ce(BEQ, LongRelative, 0xf3)
hu(BSR, Relative, 0x44)
hu(CLY, Implied, 0xC2) hu(CLY, Implied, 0xC2)
hu(CLX, Implied, 0x82) hu(CLX, Implied, 0x82)
hu(CLA, Implied, 0x62) hu(CLA, Implied, 0x62)
@ -816,6 +836,8 @@ object MosAssembler {
em(XBA, Implied, 0xEB) em(XBA, Implied, 0xEB)
em(TXY, Implied, 0x9B) em(TXY, Implied, 0x9B)
em(TYX, Implied, 0xBB) em(TYX, Implied, 0xBB)
em(JSR, LongAbsolute, 0x22)
em(JSR, AbsoluteIndexedX, 0xFC)
na(RTL, Implied, 0x6B) na(RTL, Implied, 0x6B)

View File

@ -24,7 +24,11 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
// def zeropageAddrModeHint: P[Option[Boolean]] = Pass // def zeropageAddrModeHint: P[Option[Boolean]] = Pass
val asmOpcode: P[Opcode.Value] = (position() ~ mosOpcodeLetter.rep(exactly = 3).! ~ octalDigit.?.! ~ ("_W" | "_w").?.!).map { case (p, bitNo, suffix, o) => Opcode.lookup(o + bitNo + suffix, Some(p), log) } val asmOpcode: P[(Boolean, Opcode.Value)] = (position() ~
(("l" | "L") ~ ("b" | "B") ~ mosOpcodeLetter.rep(exactly = 2) |
(mosOpcodeLetter.rep(exactly = 3).! ~ octalDigit.?.! ~ ("_W" | "_w").?)).!).map { case (p, o) =>
o.toLowerCase.startsWith("lb") -> Opcode.lookup(o, Some(p), log)
}
private val commaX = HWS ~ "," ~ HWS ~ ("X" | "x") ~ HWS private val commaX = HWS ~ "," ~ HWS ~ ("X" | "x") ~ HWS
private val commaY = HWS ~ "," ~ HWS ~ ("Y" | "y") ~ HWS private val commaY = HWS ~ "," ~ HWS ~ ("Y" | "y") ~ HWS
@ -77,8 +81,8 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
import Opcode._ import Opcode._
for { for {
elid <- !"}" ~ elidable elid <- !"}" ~ elidable
position <- position("assembly statement") pos <- position("assembly statement")
op <- asmOpcode ~/ Pass (longrelative, op) <- asmOpcode ~/ Pass
param <- op match { param <- op match {
case op if OpcodeClasses.SingleBitBranch(op) => case op if OpcodeClasses.SingleBitBranch(op) =>
(HWS ~ asmExpression ~ HWS ~ "," ~/ HWS ~ asmExpression).map{ x => (HWS ~ asmExpression ~ HWS ~ "," ~/ HWS ~ asmExpression).map{ x =>
@ -102,12 +106,23 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
case (Opcode.ASR, AddrMode.Absolute) => MosAssemblyStatement(Opcode.ASR, AddrMode.ZeroPage, param._2, elid) case (Opcode.ASR, AddrMode.Absolute) => MosAssemblyStatement(Opcode.ASR, AddrMode.ZeroPage, param._2, elid)
case (Opcode.ASR, AddrMode.AbsoluteX) => MosAssemblyStatement(Opcode.ASR, AddrMode.ZeroPageX, param._2, elid) case (Opcode.ASR, AddrMode.AbsoluteX) => MosAssemblyStatement(Opcode.ASR, AddrMode.ZeroPageX, param._2, elid)
case (Opcode.SBX, _) => MosAssemblyStatement(Opcode.SAX, param._1, param._2, elid) case (Opcode.SBX, _) => MosAssemblyStatement(Opcode.SAX, param._1, param._2, elid)
case (Opcode.BSR, AddrMode.Absolute) if options.flag(CompilationFlag.Emit65CE02Opcodes) || longrelative =>
MosAssemblyStatement(Opcode.BSR, AddrMode.LongRelative, param._2, elid)
case (Opcode.BSR, AddrMode.Absolute) if options.flag(CompilationFlag.EmitHudsonOpcodes) =>
MosAssemblyStatement(Opcode.BSR, AddrMode.Relative, param._2, elid)
case (Opcode.PHW, AddrMode.Immediate) => MosAssemblyStatement(Opcode.PHW, AddrMode.WordImmediate, param._2, elid)
case (op, AddrMode.AbsoluteX)
if (op == INC_W || op == DEC_W) && !options.flag(CompilationFlag.EmitNative65816Opcodes) =>
MosAssemblyStatement(op, AddrMode.ZeroPageX, param._2, elid)
case (_, AddrMode.Absolute) if OpcodeClasses.ShortBranching(op) =>
MosAssemblyStatement(op, if (longrelative) AddrMode.LongRelative else AddrMode.Relative, param._2, elid)
case (_, AddrMode.ZeroPageX) if !OpcodeClasses.SupportsZeroPageX(op) => MosAssemblyStatement(op, AddrMode.AbsoluteX, param._2, elid) case (_, AddrMode.ZeroPageX) if !OpcodeClasses.SupportsZeroPageX(op) => MosAssemblyStatement(op, AddrMode.AbsoluteX, param._2, elid)
case (_, AddrMode.ZeroPageY) if !OpcodeClasses.SupportsZeroPageY(op) => MosAssemblyStatement(op, AddrMode.AbsoluteY, param._2, elid) case (_, AddrMode.ZeroPageY) if !OpcodeClasses.SupportsZeroPageY(op) => MosAssemblyStatement(op, AddrMode.AbsoluteY, param._2, elid)
case (_, AddrMode.Absolute) if OpcodeClasses.SingleBit(op) => MosAssemblyStatement(op, AddrMode.ZeroPage, param._2, elid) case (_, AddrMode.Absolute) if OpcodeClasses.SingleBit(op) => MosAssemblyStatement(op, AddrMode.ZeroPage, param._2, elid)
case (_, AddrMode.IndexedX) if op == Opcode.JMP || op == Opcode.JSR => MosAssemblyStatement(op, AddrMode.AbsoluteIndexedX, param._2, elid)
case (_, AddrMode.Indirect) if op != Opcode.JMP && op != Opcode.JSR => MosAssemblyStatement(op, AddrMode.IndexedZ, param._2, elid) case (_, AddrMode.Indirect) if op != Opcode.JMP && op != Opcode.JSR => MosAssemblyStatement(op, AddrMode.IndexedZ, param._2, elid)
case _ => MosAssemblyStatement(op, param._1, param._2, elid) case _ => MosAssemblyStatement(op, param._1, param._2, elid)
}).pos(position) }).pos(pos)
} }
} }

View File

@ -572,4 +572,91 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues {
|""".stripMargin) |""".stripMargin)
m.readByte(0xc000) should equal(0x12) m.readByte(0xc000) should equal(0x12)
} }
test("65CE02 opcodes") {
EmuUnoptimizedCrossPlatformRun(Cpu.CE02)(
"""
|byte output @$c000
|void stuff() {
| output = 42
|}
|void main() {
| word p
| p = stuff.addr
| asm {
| jsr (p)
| }
| return
| asm {
| bsr stuff
| see
| cle
| ldz #$a3
| ldz $abab
| ldz $bbbb,x
| stz $64
| stz $74,x
| stz $9c9c
| stz $9e9e,x
| asr
| asr $44
| asr $54,x
| asw $cbcb
| row $ebeb
| dew $c3
| inw $e3
| neg
| dec
| inc
| dez
| inz
| tab
| tba
| taz
| tza
| tsy
| tys
| phw #$f4f4
| phw $fcfc
| phx
| phy
| phz
| plz
| ply
| plx
| .here: bra .here
| bbr0 $0F, .here
| bbs0 $8F, .here
| jsr ($2323,x)
| jmp ($7c7c,x)
| rmb0 $07
| smb0 $87
| ora ($12),z
| and ($32),z
| eor ($52),z
| adc ($72),z
| sta ($92),z
| lda ($b2),z
| cmp ($d2),z
| sbc ($f2),z
| lda ($e2,s),y
| sta ($82,s),y
| map
| [for x,0,until,300 [0]]
| lbra .here
| lbeq .here
| lbcc .here
| lbcs .here
| lbvs .here
| lbvc .here
| lbmi .here
| lbpl .here
| }
|}
|""".stripMargin) { m =>
m.readByte(0xc000) should equal(42)
}
}
} }

View File

@ -10,6 +10,7 @@ object EmuUnoptimizedCrossPlatformRun {
def apply(platforms: Cpu.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = { def apply(platforms: Cpu.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = {
val (_, mm) = if (platforms.contains(Cpu.Mos) || platforms.contains(Cpu.StrictMos)) EmuUnoptimizedRun.apply2(source) else Timings(-1, -1) -> null val (_, mm) = if (platforms.contains(Cpu.Mos) || platforms.contains(Cpu.StrictMos)) EmuUnoptimizedRun.apply2(source) else Timings(-1, -1) -> null
val (_, mc) = if (platforms.contains(Cpu.Cmos)) EmuUnoptimizedCmosRun.apply2(source) else Timings(-1, -1) -> null val (_, mc) = if (platforms.contains(Cpu.Cmos)) EmuUnoptimizedCmosRun.apply2(source) else Timings(-1, -1) -> null
val (_, me) = if (platforms.contains(Cpu.CE02)) EmuUnoptimizedCE02Run.apply2(source) else Timings(-1, -1) -> null
val (_, ma) = if (platforms.contains(Cpu.Sixteen)) EmuUnoptimizedNative65816Run.apply2(source) else Timings(-1, -1) -> null val (_, ma) = if (platforms.contains(Cpu.Sixteen)) EmuUnoptimizedNative65816Run.apply2(source) else Timings(-1, -1) -> null
val (_, mn) = if (platforms.contains(Cpu.Ricoh)) EmuUnoptimizedRicohRun.apply2(source) else Timings(-1, -1) -> null val (_, mn) = if (platforms.contains(Cpu.Ricoh)) EmuUnoptimizedRicohRun.apply2(source) else Timings(-1, -1) -> null
val (_, mz) = if (platforms.contains(Cpu.Z80)) EmuUnoptimizedZ80Run.apply2(source) else Timings(-1, -1) -> null val (_, mz) = if (platforms.contains(Cpu.Z80)) EmuUnoptimizedZ80Run.apply2(source) else Timings(-1, -1) -> null
@ -33,6 +34,10 @@ object EmuUnoptimizedCrossPlatformRun {
println(f"Running 65C02") println(f"Running 65C02")
verifier(mc) verifier(mc)
} }
if (Settings.enableCE02Tests && platforms.contains(Cpu.CE02)) {
println(f"Running 65CE02")
verifier(me)
}
if (Settings.enableZ80Tests && platforms.contains(Cpu.Z80)) { if (Settings.enableZ80Tests && platforms.contains(Cpu.Z80)) {
println(f"Running Z80") println(f"Running Z80")
verifier(mz) verifier(mz)

View File

@ -12,6 +12,8 @@ object EmuUnoptimizedRicohRun extends EmuRun(Cpu.Ricoh, Nil, Nil)
object EmuUnoptimizedCmosRun extends EmuRun(Cpu.Cmos, Nil, Nil) object EmuUnoptimizedCmosRun extends EmuRun(Cpu.Cmos, Nil, Nil)
object EmuUnoptimizedCE02Run extends EmuRun(Cpu.CE02, Nil, Nil)
object EmuUnoptimizedHudsonRun extends EmuRun(Cpu.HuC6280, Nil, Nil) object EmuUnoptimizedHudsonRun extends EmuRun(Cpu.HuC6280, Nil, Nil)
object EmuUnoptimizedNative65816Run extends EmuRun(Cpu.Sixteen, Nil, Nil) { object EmuUnoptimizedNative65816Run extends EmuRun(Cpu.Sixteen, Nil, Nil) {

View File

@ -28,6 +28,12 @@ object Settings {
*/ */
val enableWdc85816Tests: Boolean = false val enableWdc85816Tests: Boolean = false
/**
* Should the 65CE02 tests be enabled?
* There is no emulator for 65CE02 right now.
*/
val enableCE02Tests: Boolean = false
/** /**
* Should the Ricoh tests be enabled? * Should the Ricoh tests be enabled?
* Ricoh tests: * Ricoh tests: