R800 support

This commit is contained in:
Karol Stasiak 2021-09-18 00:36:16 +02:00
parent 7530b382a8
commit 166acf2b18
20 changed files with 353 additions and 36 deletions

View File

@ -37,6 +37,8 @@ if a line ends with a backslash character, the value continues to the next line.
* `z80` (Zilog Z80)
* `strictz80` (Z80 without illegal instructions)
* `r800` (R800)
* `z80next` (Z80 core from ZX Spectrum Next)
Note: Millfork version 0.3.18 and earlier uses the name `zx80next` for this architecture.
@ -80,6 +82,8 @@ This list cannot contain module template instantiations.
* `emit_x80` whether the compiler should emit instructions present on Sharp LR35902 and Z80, but absent on Intel 8080, default is `true` on compatible processors and `false` elsewhere
* `emit_z80` whether the compiler should emit Zilog Z80 instructions not covered by `emit_x80`, default is `true` on compatible processors and `false` elsewhere
* `emit_r800` whether the compiler should emit R800 instructions, default is `true` on compatible processors and `false` elsewhere
* `prevent_jmp_indirect_bug` whether the compiler should try to avoid the indirect JMP bug,
default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere

View File

@ -11,6 +11,12 @@ inline asm byte __mul_u8u8u8() {
? LD A, E
? RET
}
#elseif CPUFEATURE_R800
inline asm byte __mul_u8u8u8() {
? MULUB A,D
? LD A,L
? RET
}
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
//A = A * D
noinline asm byte __mul_u8u8u8() {
@ -89,6 +95,16 @@ __divmod_u16u8u16u8_skip:
? RET
}
#if CPUFEATURE_R800
inline asm word __mul_u16u8u16() {
? LD L,A
? LD H,0
? MULUW HL,DE
? RET
}
#else
// HL=A*DE
noinline asm word __mul_u16u8u16() {
? LD HL,0
? LD B,8
@ -113,8 +129,17 @@ __mul_u16u8u16_skip:
#endif
? RET
}
#endif
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
#if CPUFEATURE_R800
inline asm word __mul_u16u16u16() {
? EX DE,HL
? MULUW HL,BC
? RET
}
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
// HL=BC*DE
noinline asm word __mul_u16u16u16() {
LD HL,0
LD A,16

View File

@ -45,7 +45,7 @@ case class CompilationOptions(platform: Platform,
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes, EmitR800Opcodes,
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
@ -172,6 +172,11 @@ case class CompilationOptions(platform: Platform,
log.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
}
}
if (flags(EmitR800Opcodes)) {
if (platform.cpu != R800) {
log.error("R800 opcodes enabled for architecture that doesn't support them")
}
}
if (flags(EmitIntel8080Opcodes)) {
if (!Intel8080Compatible(platform.cpu)) {
log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
@ -220,6 +225,7 @@ case class CompilationOptions(platform: Platform,
"OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)),
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
"CPUFEATURE_R800" -> toLong(flag(CompilationFlag.EmitR800Opcodes)),
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
@ -290,7 +296,7 @@ object CpuFamily extends Enumeration {
import Cpu._
cpu match {
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 | Z80Next => I80
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | R800 | EZ80 | Z80Next => I80
case Intel8086 | Intel80186 => I86
case Cpu.Motorola6809 => M6809
}
@ -368,6 +374,10 @@ object Cpu extends Enumeration {
* The Zilog Z80 processor, without illegal instructions
*/
val StrictZ80: Cpu.Value = Value
/**
* The R800 CPU (used in MSX Turbo-R)
*/
val R800: Cpu.Value = Value
/**
* The Zilog eZ80 processor
*/
@ -400,11 +410,11 @@ object Cpu extends Enumeration {
/**
* Processors that can run code for Zilog Z80
*/
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80, Z80Next)
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, R800, EZ80, Z80Next)
/**
* Processors that can run code for Intel 8080
*/
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80, Z80Next)
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, R800, EZ80, Z80Next)
/**
* Processors that can run code for Intel 8085
*/
@ -468,6 +478,8 @@ object Cpu extends Enumeration {
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
case StrictZ80 | Z80 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
case R800 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitR800Opcodes)
case Z80Next =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
case EZ80 =>
@ -514,6 +526,7 @@ object Cpu extends Enumeration {
case "strict2a07" => StrictRicoh
case "z80" => Z80
case "strictz80" => Z80
case "r800" => R800
case "zx80next" => Z80Next
case "z80next" => Z80Next
// disabled for now:
@ -564,7 +577,7 @@ object CompilationFlag extends Enumeration {
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
// compilation options for I80
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitR800Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
UseShadowRegistersForInterrupts,
UseIxForStack, UseIyForStack,
UseIxForScratch, UseIyForScratch,
@ -628,6 +641,7 @@ object CompilationFlag extends Enumeration {
"emit_65ce02" -> Emit65CE02Opcodes,
"emit_huc6280" -> EmitHudsonOpcodes,
"emit_z80" -> EmitZ80Opcodes,
"emit_r800" -> EmitR800Opcodes,
"emit_ez80" -> EmitEZ80Opcodes,
"emit_x80" -> EmitExtended80Opcodes,
"emit_8080" -> EmitIntel8080Opcodes,

View File

@ -46,10 +46,24 @@ case class Context(errorReporting: Logger,
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
addons += CompilationFlag.EmitZ80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
if (isFlagSet(CompilationFlag.EmitZ80NextOpcodes)) {
addons += CompilationFlag.EmitZ80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitR800Opcodes)) {
addons += CompilationFlag.EmitZ80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
addons += CompilationFlag.EmitExtended80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
addons += CompilationFlag.EmitIntel8080Opcodes -> true
}
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {

View File

@ -321,7 +321,9 @@ object Main {
val assemblyOptimizations = optLevel match {
case 0 => Nil
case _ =>
if (options.flag(CompilationFlag.EmitZ80Opcodes))
if (options.flag(CompilationFlag.EmitR800Opcodes))
Z80OptimizationPresets.GoodForR800
else if (options.flag(CompilationFlag.EmitZ80Opcodes))
Z80OptimizationPresets.GoodForZ80
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
Z80OptimizationPresets.GoodForIntel8080

View File

@ -1067,6 +1067,17 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case LHLX | RLDE => r == D || r == E
case RRHL => r == H || r == L
case MULUB => r == A || (registers match {
case TwoRegisters(p, q) => r == q || r == p
case _ => true
})
case MULUW => r == H || r == L || (registers match {
case TwoRegisters(_, BC) => r == B || r == C
case TwoRegisters(_, DE) => r == D || r == E
case TwoRegisters(_, SP) => r == SP
case _ => true
})
case _ => true // TODO
}
}
@ -1230,6 +1241,9 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case LHLX | RRHL | DSUB => r == H || r == L
case SHLX => false
case MULUB => r == H || r == L
case MULUW => r == H || r == L || r == D || r == E
case _ => true // TODO
}
}

View File

@ -29,6 +29,8 @@ object ZOpcode extends Enumeration {
LD_DESP, LD_DEHL, RRHL, RLDE, DSUB, RSTV, LHLX, SHLX,
//sharp:
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, SWAP, LDH_DA, LDH_AD, LDH_CA, LDH_AC, LD_HLSP, ADD_SP, STOP,
// R800:
MULUB, MULUW,
// next:
LDIX, LDWS, LDIRX, LDDX, LDDRX, LDPIRX, OUTINB, MUL, SWAPNIB, MIRROR, NEXTREG, PIXELDN, PIXELAD, SETAE, TEST,
DISCARD_A, DISCARD_F, DISCARD_HL, DISCARD_BC, DISCARD_DE, DISCARD_IX, DISCARD_IY, CHANGED_MEM,
@ -55,6 +57,7 @@ object ZOpcodeClasses {
val CbInstructions: Set[ZOpcode.Value] = Set(SLA, SRA, SRL, SLL, RLC, RRC, RL, RR) ++ BIT ++ RES ++ SET
val EdInstructions: Set[ZOpcode.Value] = Set(NEG, RETN, RETI, IM, RRD, RLD,
MULUB, MULUW,
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR) ++ BIT ++ RES ++ SET
@ -63,6 +66,7 @@ object ZOpcodeClasses {
val ChangesAFAlways: Set[ZOpcode.Value] = Set( // TODO: !
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, INC, DEC,
SCF, CCF, NEG, RIM,
MULUB, MULUW,
LDH_AC, LDH_AD, LD_AHLI, LD_AHLD,
ADD_16, ADC_16, SBC_16, INC_16, DEC_16,
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
@ -74,6 +78,7 @@ object ZOpcodeClasses {
LDIX, LDIRX, LDDX, LDDRX, LDPIRX,
EXX, CALL, JR, JP, LABEL, DJNZ)
val ChangesHLAlways: Set[ZOpcode.Value] = Set(
MULUB, MULUW,
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, LD_HLSP, DSUB,
@ -81,6 +86,7 @@ object ZOpcodeClasses {
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, PIXELAD, PIXELDN, OUTINB,
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
val ChangesDEAlways: Set[ZOpcode.Value] = Set(
MULUW,
LDI, LDIR, LDD, LDDR,
LD_DESP, LD_DEHL, RLDE,
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, MUL,
@ -88,6 +94,7 @@ object ZOpcodeClasses {
val ChangesOnlyRegister: Set[ZOpcode.Value] = Set(INC, DEC, INC_16, DEC_16, POP, EX_SP, IN_C, IN_IMM, RL, RR, RLC, RRC, SLA, SRA, SRL, SLL) ++ SET ++ RES
val ChangesFirstRegister: Set[ZOpcode.Value] = Set(LD, LD_16, ADD_16, SBC_16)
val ChangesAAlways: Set[ZOpcode.Value] = Set(
MULUB,
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, LD_AHLI, LD_AHLD, RIM,
MIRROR, SETAE,
)

View File

@ -0,0 +1,24 @@
package millfork.assembly.z80.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.ZLine
import millfork.assembly.z80.ZOpcode.MULUB
import millfork.assembly.z80.ZOpcode.MULUW
import millfork.node.ZRegister
/**
* Optimizations valid for R800
* @author Karol Stasiak
*/
object AlwaysGoodR800Optimizations {
val UnusedR800Instructions = new RuleBasedAssemblyOptimization("Simplifiable maths (R800)",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(MULUB) & DoesntMatterWhatItDoesWith(ZRegister.H, ZRegister.L) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
(Elidable & HasOpcode(MULUW) & DoesntMatterWhatItDoesWith(ZRegister.H, ZRegister.L, ZRegister.D, ZRegister.E) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
)
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
UnusedR800Instructions,
)
}

View File

@ -361,6 +361,38 @@ object CoarseFlowAnalyzer {
case ZLine0(RIM, _, _) =>
currentStatus = currentStatus.copy(a = AnyStatus)
case ZLine0(MULUB, TwoRegisters(ZRegister.A, r@(ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E)), _) =>
val hl = (currentStatus.a<*>currentStatus.getRegister(r, 0)){(a,b) => (a*b)&0xff}
currentStatus = currentStatus.copy(
h = hl.hi,
l = hl.lo,
hl = hl.map(NumericConstant(_, 2)),
cf = AnyStatus,
nf = AnyStatus,
hf = AnyStatus,
zf = AnyStatus,
sf = AnyStatus,
pf = AnyStatus
)
case ZLine0(MULUW, TwoRegisters(ZRegister.HL, ZRegister.BC), _) =>
val hl = (currentStatus.h<*>currentStatus.l)(currentStatus.mergeBytes)
val bc = (currentStatus.b<*>currentStatus.c)(currentStatus.mergeBytes)
val hi = (hl<*>bc){(a,b) => (a*b).>>(16).&(0xffff)}
val lo = (hl<*>bc){(a,b) => (a*b).&(0xffff)}
currentStatus = currentStatus.copy(
d = hi.hi,
e = hi.lo,
h = lo.hi,
l = lo.lo,
hl = lo.map(NumericConstant(_, 2)),
cf = AnyStatus,
nf = AnyStatus,
hf = AnyStatus,
zf = AnyStatus,
sf = AnyStatus,
pf = AnyStatus
)
case ZLine0(opcode, registers, _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)

View File

@ -22,6 +22,21 @@ object Z80OptimizationPresets {
).flatten
}
val GoodForR800: List[AssemblyOptimization[ZLine]] = {
List.fill(5)(
List.fill(5)(
AlwaysGoodI80Optimizations.All ++
AlwaysGoodZ80Optimizations.All ++
AlwaysGoodR800Optimizations.All ++
List(
EmptyParameterStoreRemoval,
EmptyMemoryStoreRemoval)
).flatten ++
List(ChangeRegisterPairPreferringDE, WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, ChangeRegisterPairPreferringBC, CompactStackFrame) ++
LaterIntel8080Optimizations.All ++ LaterI80Optimizations.All
).flatten
}
val GoodForIntel8080: List[AssemblyOptimization[ZLine]] = {
List.fill(5)(
List.fill(5)(

View File

@ -67,6 +67,14 @@ class Z80Assembler(program: Program,
def requireZ80Illegals(): Unit = if (!options.flag(EmitZ80Opcodes) || !options.flag(EmitIllegals)) log.error("Unsupported instruction: " + instr)
def requireR800(): Unit = if (!options.flag(EmitR800Opcodes)) log.error("Unsupported instruction: " + instr)
def requireNoR800(): Unit = if (options.flag(EmitR800Opcodes)) log.error("Unsupported instruction: " + instr)
def requireR800OrIllegals(): Unit = if (!options.flag(EmitR800Opcodes) && !options.flag(EmitIllegals)) log.error("Unsupported instruction: " + instr)
def requireR800Illegals(): Unit = if (!options.flag(EmitR800Opcodes) || !options.flag(EmitIllegals)) log.error("Unsupported instruction: " + instr)
def requireExtended80(): Unit = if (!options.flag(EmitExtended80Opcodes)) log.error("Unsupported instruction: " + instr)
def requireSharp(): Unit = if (!options.flag(EmitSharpOpcodes)) log.error("Unsupported instruction: " + instr)
@ -326,7 +334,7 @@ class Z80Assembler(program: Program,
writeByte(bank, index + 2, instr.parameter)
index + 3
case ZLine0(op, OneRegister(ix@(IXH | IYH | IXL | IYL)), _) if oneRegister.contains(op) =>
requireZ80Illegals()
requireR800OrIllegals()
val o = oneRegister(op)
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ix) * o.multiplier)
@ -337,6 +345,7 @@ class Z80Assembler(program: Program,
index + 1
case ZLine0(SLL, OneRegister(reg), _) =>
requireZ80Illegals()
requireNoR800()
writeByte(bank, index, 0xcb)
writeByte(bank, index + 1, 0x30 + internalRegisterIndex(reg))
index + 2
@ -347,6 +356,7 @@ class Z80Assembler(program: Program,
index + 2
case ZLine0(SLL, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) =>
requireZ80Illegals()
requireNoR800()
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0xcb)
writeByte(bank, index + 2, offset)
@ -443,22 +453,22 @@ class Z80Assembler(program: Program,
writeByte(bank, index + 2, offset)
index + 3
case TwoRegisters(target@(IXH | IYH | IXL | IYL), source@(A | B | C | D | E)) =>
requireZ80Illegals()
requireR800OrIllegals()
writeByte(bank, index, prefixByte(target))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
case TwoRegisters(target@(A | B | C | D | E), source@(IXH | IYH | IXL | IYL)) =>
requireZ80Illegals()
requireR800OrIllegals()
writeByte(bank, index, prefixByte(source))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
case TwoRegisters(target@(IXH | IXL), source@(IXH | IXL)) =>
requireZ80Illegals()
requireR800OrIllegals()
writeByte(bank, index, prefixByte(source))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
case TwoRegisters(target@(IYH | IYL), source@(IYH | IYL)) =>
requireZ80Illegals()
requireR800OrIllegals()
writeByte(bank, index, prefixByte(source))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
@ -771,6 +781,61 @@ class Z80Assembler(program: Program,
requireIntel8085Illegals()
writeByte(bank, index, 0x10)
index + 1
case ZLine0(MULUB, TwoRegisters(A, A), _) =>
requireR800Illegals()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xF9)
index + 2
case ZLine0(MULUB, TwoRegisters(A, B), _) =>
requireR800()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xC1)
index + 2
case ZLine0(MULUB, TwoRegisters(A, C), _) =>
requireR800()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xC9)
index + 2
case ZLine0(MULUB, TwoRegisters(A, D), _) =>
requireR800()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xD1)
index + 2
case ZLine0(MULUB, TwoRegisters(A, E), _) =>
requireR800()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xD9)
index + 2
case ZLine0(MULUB, TwoRegisters(A, H), _) =>
requireR800Illegals()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xE1)
index + 2
case ZLine0(MULUB, TwoRegisters(A, L), _) =>
requireR800Illegals()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xE9)
index + 2
case ZLine0(MULUW, TwoRegisters(HL, BC), _) =>
requireR800()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xC3)
index + 2
case ZLine0(MULUW, TwoRegisters(HL, DE), _) =>
requireR800Illegals()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xD3)
index + 2
case ZLine0(MULUW, TwoRegisters(HL, HL), _) =>
requireR800Illegals()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xE3)
index + 2
case ZLine0(MULUW, TwoRegisters(HL, SP), _) =>
requireR800()
writeByte(bank, index, 0xED)
writeByte(bank, index + 1, 0xF3)
index + 2
case _ =>
log.fatal("Cannot assemble " + instr)
index

View File

@ -109,7 +109,7 @@ case class Z80Parser(filename: String,
case (VariableExpression(r), false) if toRegister.contains(r)=> (toRegister(r), None)
case (VariableExpression(r), false)
if options.flag(CompilationFlag.EmitZ80Opcodes) &&
options.flag(CompilationFlag.EmitIllegals) &&
(options.flag(CompilationFlag.EmitIllegals) || options.flag(CompilationFlag.EmitR800Opcodes)) &&
toIndexHalf.contains(r)=> (toIndexHalf(r), None)
case (SumExpression(List(
(false, LiteralExpression(0xff00, _)),
@ -534,7 +534,7 @@ case class Z80Parser(filename: String,
case "PIXELAD" => imm(PIXELAD)
case "SETAE" => imm(SETAE)
case "MUL" => (("D"|"d") ~ HWS ~ "," ~/ HWS ~ ("E" | "e")).?.map { _ => (MUL, NoRegisters, None, zero)}
case "MIRROR" => ("A"|"a").?.map { _ => (MUL, NoRegisters, None, zero)}
case "MIRROR" => ("A"|"a").?.map { _ => (MIRROR, NoRegisters, None, zero)}
case "NEXTREG" =>(param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (ZRegister.IMM_8, Some(n), (ZRegister.A, None)) => (NEXTREG, TwoRegisters(ZRegister.IMM_8, ZRegister.A), None, n)
case (ZRegister.IMM_8, Some(n), (ZRegister.IMM_8, Some(v))) => (NEXTREG, TwoRegisters(ZRegister.IMM_8, ZRegister.IMM_8), None, SeparateBytesExpression(v, n))
@ -544,6 +544,19 @@ case class Z80Parser(filename: String,
}
case "TEST" => one8Register(TEST)
case "MULUB" => (param(allowAbsolute = false) ~ HWS ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (ZRegister.A, None, (r, None)) => (MULUB, TwoRegisters(ZRegister.A, r), None, zero)
case _ =>
log.error("Invalid parameters for MULUB", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "MULUW" => (param(allowAbsolute = false) ~ HWS ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (ZRegister.HL, None, (r, None)) => (MULUW, TwoRegisters(ZRegister.HL, r), None, zero)
case _ =>
log.error("Invalid parameters for MULUW", Some(pos))
(NOP, NoRegisters, None, zero)
}
case _ =>
log.error("Unsupported opcode " + opcode, Some(pos))
imm(NOP)

View File

@ -193,7 +193,7 @@ class ByteMathSuite extends FunSuite with Matchers with AppendedClues {
}
test("Byte multiplication 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Motorola6809)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.R800, Cpu.Motorola6809)(
"""
| import zp_reg
| byte output1 @$c001, output2 @$c002, output3 @$c003

View File

@ -249,6 +249,7 @@ class StructSuite extends FunSuite with Matchers {
| p->tmp[1] = 77
| outputAlias[0].tmp[id(3)] = 3
| outputAlias[id(0)].tmp[5] = 55
| output.tmp[6] = lo(output.tmp - output.addr)
|}
|""".stripMargin
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086, Cpu.Motorola6809)(code){ m =>
@ -257,6 +258,7 @@ class StructSuite extends FunSuite with Matchers {
m.readByte(0xc003) should equal(3)
m.readByte(0xc004) should equal(4)
m.readByte(0xc005) should equal(55)
m.readByte(0xc006) should equal(0)
}
}

View File

@ -1,7 +1,7 @@
package millfork.test
import millfork.Cpu
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedIntel8080Run, EmuUnoptimizedIntel8085Run, EmuUnoptimizedSharpRun, EmuUnoptimizedZ80NextRun, EmuUnoptimizedZ80Run}
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedIntel8080Run, EmuUnoptimizedIntel8085Run, EmuUnoptimizedR800Run, EmuUnoptimizedSharpRun, EmuUnoptimizedZ80NextRun, EmuUnoptimizedZ80Run}
import org.scalatest.{FunSuite, Matchers}
/**
@ -1340,4 +1340,42 @@ class Z80AssemblySuite extends FunSuite with Matchers {
""".stripMargin)
}
test("R800 stuff") {
EmuUnoptimizedR800Run(
"""
| #pragma zilog_syntax
| asm void main () {
| ret
| mulub a,b
| mulub a,c
| mulub a,d
| mulub a,e
| muluw hl,bc
| muluw hl,sp
| inc ixh
| inc ixl
| inc iyh
| inc iyl
| dec ixh
| dec ixl
| dec iyh
| dec iyl
| ld a,ixh
| ld a,ixl
| ld iyh,a
| ld iyl,a
| add a,iyl
| adc a,iyl
| sub iyl
| sbc a,iyl
| or iyl
| xor iyl
| and iyl
| cp iyl
| ld ixh,0
| ret
| }
""".stripMargin)
}
}

View File

@ -106,6 +106,31 @@ object EmuSharpBenchmarkRun {
}
}
object EmuR800BenchmarkRun {
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
val (Timings(t0, _), m0) = EmuUnoptimizedR800Run.apply2(source)
val (Timings(t1, _), m1) = EmuOptimizedR800Run.apply2(source)
val (Timings(t2, _), m2) = EmuOptimizedInlinedR800Run.apply2(source)
if (t0 > 0)println(f"Before optimization: $t0%7d")
if (t1 > 0)println(f"After optimization: $t1%7d")
if (t2 > 0)println(f"After inlining: $t2%7d")
if (t0 > 0 && t1 > 0) println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
if (t0 > 0 && t2 > 0) println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
if (t0 > 0) {
println(f"Running R800 unoptimized")
verifier(m0)
}
if (t1 > 0) {
println(f"Running R800 optimized")
verifier(m1)
}
if (t2 > 0) {
println(f"Running R800 optimized inlined")
verifier(m2)
}
}
}
object EmuIntel8086BenchmarkRun {
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
val (Timings(t0, _), m0) = EmuUnoptimizedIntel8086Run.apply2(source)
@ -170,6 +195,9 @@ object EmuCrossPlatformBenchmarkRun {
if (Settings.enableIntel8080Tests && platforms.contains(millfork.Cpu.Intel8080)) {
EmuIntel8080BenchmarkRun.apply(source)(verifier)
}
if (Settings.enableZ80Tests && platforms.contains(millfork.Cpu.R800)) {
EmuR800BenchmarkRun.apply(source)(verifier)
}
if (Settings.enableUnemulatedTests && platforms.contains(millfork.Cpu.Intel8085)) {
EmuUnoptimizedIntel8085Run.apply(source)
}

View File

@ -50,6 +50,10 @@ object EmuOptimizedInlinedSharpRun extends EmuZ80Run(Cpu.Sharp, OptimizationPres
override def inline: Boolean = true
}
object EmuOptimizedInlinedR800Run extends EmuZ80Run(Cpu.R800, OptimizationPresets.NodeOpt, Z80OptimizationPresets.GoodForR800) {
override def inline: Boolean = true
}
object EmuOptimizedInlinedM6809Run extends EmuM6809Run(Cpu.Motorola6809, OptimizationPresets.NodeOpt, M6809OptimizationPresets.Default) {
override def inline: Boolean = true
}

View File

@ -95,4 +95,6 @@ object EmuSizeOptimizedIntel8080Run extends EmuZ80Run(Cpu.Intel8080, Optimizatio
object EmuOptimizedSharpRun extends EmuZ80Run(Cpu.Sharp, OptimizationPresets.NodeOpt, Z80OptimizationPresets.GoodForSharp)
object EmuOptimizedR800Run extends EmuZ80Run(Cpu.R800, OptimizationPresets.NodeOpt, Z80OptimizationPresets.GoodForR800)
object EmuOptimizedM6809Run extends EmuM6809Run(Cpu.Motorola6809, OptimizationPresets.NodeOpt, M6809OptimizationPresets.Default)

View File

@ -28,6 +28,8 @@ object EmuUnoptimizedIntel8085Run extends EmuZ80Run(Cpu.Intel8085, Nil, Nil)
object EmuUnoptimizedZ80NextRun extends EmuZ80Run(Cpu.Z80Next, Nil, Nil)
object EmuUnoptimizedR800Run extends EmuZ80Run(Cpu.R800, Nil, Nil)
object EmuUnoptimizedIntel8086Run extends EmuI86Run(Nil, Nil)
object EmuUnoptimizedSharpRun extends EmuZ80Run(Cpu.Sharp, Nil, Nil)

View File

@ -30,6 +30,8 @@ import scala.collection.mutable
*/
object EmuZ80Run {
val secondBytesOfMulOnR800: Set[Int] = Set(0xf9, 0xc1, 0xc9, 0xd1, 0xf9, 0xe1, 0xe9, 0xc3, 0xd3, 0xe3, 0xf3)
private def preload(cpu: millfork.Cpu.Value, filename: String): Option[Program] = {
TestErrorReporting.log.info(s"Loading $filename for $cpu")
val source = Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.mkString("\n")
@ -87,6 +89,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
CompilationFlag.SubroutineExtraction -> optimizeForSize,
CompilationFlag.EmitIllegals -> (cpu == millfork.Cpu.Z80 || cpu == millfork.Cpu.Intel8085 || cpu == millfork.Cpu.Z80Next),
CompilationFlag.EmitZ80NextOpcodes -> (cpu == millfork.Cpu.Z80Next),
CompilationFlag.EmitR800Opcodes -> (cpu == millfork.Cpu.R800),
CompilationFlag.LenientTextEncoding -> true)
if (source.contains("intel_syntax")) {
extraFlags += CompilationFlag.UseIntelSyntaxForOutput -> true
@ -199,26 +202,35 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
method
}
val timings = platform.cpu match {
case millfork.Cpu.Z80 | millfork.Cpu.Intel8080 =>
val cpu = new Z80Core(Z80Memory(memoryBank), DummyIO)
cpu.reset()
cpu.setProgramCounter(0x1ed)
cpu.resetTStates()
while (!cpu.getHalt) {
cpu.executeOneInstruction()
if (resetN) {
resetNMethod.invoke(cpu)
case millfork.Cpu.Z80 | millfork.Cpu.Intel8080 | millfork.Cpu.R800 =>
val hasMultiplications = (platform.cpu == millfork.Cpu.R800) && ((0x200 to 0xfffe).exists { addr =>
val b0 = memoryBank.output(addr)
val b1 = memoryBank.output(addr + 1)
b0.&(0xff) == 0xED && EmuZ80Run.secondBytesOfMulOnR800(b1.&(0xff))
})
if (hasMultiplications) {
Timings(-1, -1) -> memoryBank
} else {
val cpu = new Z80Core(Z80Memory(memoryBank), DummyIO)
cpu.reset()
cpu.setProgramCounter(0x1ed)
cpu.resetTStates()
while (!cpu.getHalt) {
cpu.executeOneInstruction()
if (resetN) {
resetNMethod.invoke(cpu)
}
if (cpu.getSP.&(0xffff) < 0xd002) {
log.debug("stack dump:")
(0xD000 until 0xD0FF).map(memoryBank.output).grouped(16).map(_.map(i => f"$i%02x").mkString(" ")).foreach(log.debug(_))
throw new IllegalStateException("stack overflow")
}
// dump(cpu)
cpu.getTStates should be < TooManyCycles
}
if (cpu.getSP.&(0xffff) < 0xd002) {
log.debug("stack dump:")
(0xD000 until 0xD0FF).map(memoryBank.output).grouped(16).map(_.map(i => f"$i%02x").mkString(" ")).foreach(log.debug(_))
throw new IllegalStateException("stack overflow")
}
// dump(cpu)
cpu.getTStates should be < TooManyCycles
val tStates = cpu.getTStates
Timings(tStates, tStates) -> memoryBank
}
val tStates = cpu.getTStates
Timings(tStates, tStates) -> memoryBank
case millfork.Cpu.Sharp =>
var ticks = 0L
val cpu = GameboyStubs(memoryBank).cpu