diff --git a/src/main/scala/millfork/assembly/mos/AssemblyLine.scala b/src/main/scala/millfork/assembly/mos/AssemblyLine.scala index c3933e2b..897f8005 100644 --- a/src/main/scala/millfork/assembly/mos/AssemblyLine.scala +++ b/src/main/scala/millfork/assembly/mos/AssemblyLine.scala @@ -693,6 +693,8 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para parameter match { 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) { parameter match { case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b" diff --git a/src/main/scala/millfork/assembly/mos/Opcode.scala b/src/main/scala/millfork/assembly/mos/Opcode.scala index f438c8c0..fb7bf41b 100644 --- a/src/main/scala/millfork/assembly/mos/Opcode.scala +++ b/src/main/scala/millfork/assembly/mos/Opcode.scala @@ -180,20 +180,31 @@ object Opcode extends Enumeration { case "AXA" => AHX case "AXS" => SBX // could mean SAX case "BCC" => BCC + case "LBCC" => BCC case "BCS" => BCS + case "LBCS" => BCS case "BEQ" => BEQ + case "LBEQ" => BEQ case "BIT" => BIT case "BMI" => BMI + case "LBMI" => BMI case "BNE" => BNE + case "LBNE" => BNE case "BPL" => BPL + case "LBPL" => BPL case "BRA" => BRA + case "LBRA" => BRA case "BRK" => BRK case "BRL" => BRL case "BSR" => BSR + case "LBSR" => BSR case "BVC" => BVC + case "LBVC" => BVC case "BVS" => BVS + case "LBVS" => BVS case "CLC" => CLC case "CLD" => CLD + case "CLE" => CLE case "CLI" => CLI case "CLV" => CLV case "CLX" => CLX @@ -232,6 +243,7 @@ object Opcode extends Enumeration { case "LSE" => SRE case "LSR" => LSR case "LXA" => LXA + case "MAP" => MAP case "NEG" => NEG case "NOP" => NOP case "OAL" => LXA @@ -247,17 +259,19 @@ object Opcode extends Enumeration { case "PHW" => PHW case "PHX" => PHX case "PHY" => PHY + case "PHZ" => PHZ case "PLA" => PLA case "PLB" => PLB case "PLD" => PLD case "PLP" => PLP case "PLX" => PLX case "PLY" => PLY + case "PLZ" => PLZ case "REP" => REP case "RLA" => RLA case "ROL" => ROL case "ROR" => ROR - case "ROW" => ROR_W // TODO: is this correct? + case "ROW" => ROL_W case "RRA" => RRA case "RTI" => RTI case "RTL" => RTL @@ -268,6 +282,7 @@ object Opcode extends Enumeration { case "SBX" => SBX case "SEC" => SEC case "SED" => SED + case "SEE" => SEE case "SEI" => SEI case "SEP" => SEP case "SET" => SET diff --git a/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala b/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala index ea00970e..ec55ba67 100644 --- a/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala +++ b/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala @@ -164,14 +164,14 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] { val c: Constant = compileParameterForAssemblyStatement(env, o, x) val actualAddrMode = a match { 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 case Absolute if OpcodeClasses.SupportsZeropage(o) && c.fitsProvablyIntoByte => ZeroPage case ImmediateWithAbsolute if (c match { case StructureConstant(_, List(a, b)) => b.fitsProvablyIntoByte }) => ImmediateWithZeroPage - case IndexedX if o == JMP => AbsoluteIndexedX - case Indirect if o != JMP => IndexedZ + case IndexedX if o == JMP || o == JSR => AbsoluteIndexedX + case Indirect if o != JMP && o != JSR => IndexedZ case _ => a } List(AssemblyLine(o, actualAddrMode, c, e)) -> Nil diff --git a/src/main/scala/millfork/output/MosAssembler.scala b/src/main/scala/millfork/output/MosAssembler.scala index 3f7fc972..b682bb13 100644 --- a/src/main/scala/millfork/output/MosAssembler.scala +++ b/src/main/scala/millfork/output/MosAssembler.scala @@ -64,6 +64,11 @@ class MosAssembler(program: Program, writeByte(bank, index, MosAssembler.opcodeFor(op, am, options)) writeWord(bank, index + 1, param) 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) => writeByte(bank, index, MosAssembler.opcodeFor(op, am, options)) writeWord(bank, index + 1, param) @@ -720,8 +725,7 @@ object MosAssembler { ce(DEC_W, ZeroPage, 0xC3) ce(INC_W, ZeroPage, 0xE3) ce(ASL_W, Absolute, 0xCB) - // TODO: or is it ROL_W? - ce(ROR_W, Absolute, 0xEB) + ce(ROL_W, Absolute, 0xEB) ce(ASR, Implied, 0x43) ce(ASR, ZeroPage, 0x44) ce(ASR, ZeroPageX, 0x54) @@ -738,10 +742,26 @@ object MosAssembler { ce(PHW, Absolute, 0xFC) ce(PHZ, Implied, 0xDB) ce(PLZ, Implied, 0xFB) -// ce(CLE, Implied, ) -// ce(SEE, Implied, ) -// ce(BSR, , ) + ce(JSR, Indirect, 0x22) + ce(JSR, AbsoluteIndexedX, 0x23) + 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(CLX, Implied, 0x82) hu(CLA, Implied, 0x62) @@ -816,6 +836,8 @@ object MosAssembler { em(XBA, Implied, 0xEB) em(TXY, Implied, 0x9B) em(TYX, Implied, 0xBB) + em(JSR, LongAbsolute, 0x22) + em(JSR, AbsoluteIndexedX, 0xFC) na(RTL, Implied, 0x6B) diff --git a/src/main/scala/millfork/parser/MosParser.scala b/src/main/scala/millfork/parser/MosParser.scala index 4a1f1f18..09b88957 100644 --- a/src/main/scala/millfork/parser/MosParser.scala +++ b/src/main/scala/millfork/parser/MosParser.scala @@ -24,7 +24,11 @@ case class MosParser(filename: String, input: String, currentDirectory: String, // 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 commaY = HWS ~ "," ~ HWS ~ ("Y" | "y") ~ HWS @@ -77,8 +81,8 @@ case class MosParser(filename: String, input: String, currentDirectory: String, import Opcode._ for { elid <- !"}" ~ elidable - position <- position("assembly statement") - op <- asmOpcode ~/ Pass + pos <- position("assembly statement") + (longrelative, op) <- asmOpcode ~/ Pass param <- op match { case op if OpcodeClasses.SingleBitBranch(op) => (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.AbsoluteX) => MosAssemblyStatement(Opcode.ASR, AddrMode.ZeroPageX, 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.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.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 _ => MosAssemblyStatement(op, param._1, param._2, elid) - }).pos(position) + }).pos(pos) } } diff --git a/src/test/scala/millfork/test/AssemblySuite.scala b/src/test/scala/millfork/test/AssemblySuite.scala index 0c225b9d..b15da1f4 100644 --- a/src/test/scala/millfork/test/AssemblySuite.scala +++ b/src/test/scala/millfork/test/AssemblySuite.scala @@ -572,4 +572,91 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues { |""".stripMargin) 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) + } + } + + } diff --git a/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala b/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala index fdda69ca..adceeebb 100644 --- a/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala +++ b/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala @@ -10,6 +10,7 @@ object EmuUnoptimizedCrossPlatformRun { 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 (_, 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 (_, 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 @@ -33,6 +34,10 @@ object EmuUnoptimizedCrossPlatformRun { println(f"Running 65C02") verifier(mc) } + if (Settings.enableCE02Tests && platforms.contains(Cpu.CE02)) { + println(f"Running 65CE02") + verifier(me) + } if (Settings.enableZ80Tests && platforms.contains(Cpu.Z80)) { println(f"Running Z80") verifier(mz) diff --git a/src/test/scala/millfork/test/emu/EmuUnoptimizedRun.scala b/src/test/scala/millfork/test/emu/EmuUnoptimizedRun.scala index d14ee171..c88f0fb0 100644 --- a/src/test/scala/millfork/test/emu/EmuUnoptimizedRun.scala +++ b/src/test/scala/millfork/test/emu/EmuUnoptimizedRun.scala @@ -12,6 +12,8 @@ object EmuUnoptimizedRicohRun extends EmuRun(Cpu.Ricoh, 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 EmuUnoptimizedNative65816Run extends EmuRun(Cpu.Sixteen, Nil, Nil) { diff --git a/src/test/scala/millfork/test/emu/Settings.scala b/src/test/scala/millfork/test/emu/Settings.scala index 1dd59051..3b56c1b3 100644 --- a/src/test/scala/millfork/test/emu/Settings.scala +++ b/src/test/scala/millfork/test/emu/Settings.scala @@ -28,6 +28,12 @@ object Settings { */ 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? * Ricoh tests: