mirror of
https://github.com/KarolS/millfork.git
synced 2024-06-11 15:29:34 +00:00
R800 support
This commit is contained in:
parent
7530b382a8
commit
166acf2b18
|
@ -37,6 +37,8 @@ if a line ends with a backslash character, the value continues to the next line.
|
||||||
* `z80` (Zilog Z80)
|
* `z80` (Zilog Z80)
|
||||||
|
|
||||||
* `strictz80` (Z80 without illegal instructions)
|
* `strictz80` (Z80 without illegal instructions)
|
||||||
|
|
||||||
|
* `r800` (R800)
|
||||||
|
|
||||||
* `z80next` (Z80 core from ZX Spectrum Next)
|
* `z80next` (Z80 core from ZX Spectrum Next)
|
||||||
Note: Millfork version 0.3.18 and earlier uses the name `zx80next` for this architecture.
|
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_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_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,
|
* `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
|
default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere
|
||||||
|
|
|
@ -11,6 +11,12 @@ inline asm byte __mul_u8u8u8() {
|
||||||
? LD A, E
|
? LD A, E
|
||||||
? RET
|
? RET
|
||||||
}
|
}
|
||||||
|
#elseif CPUFEATURE_R800
|
||||||
|
inline asm byte __mul_u8u8u8() {
|
||||||
|
? MULUB A,D
|
||||||
|
? LD A,L
|
||||||
|
? RET
|
||||||
|
}
|
||||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||||
//A = A * D
|
//A = A * D
|
||||||
noinline asm byte __mul_u8u8u8() {
|
noinline asm byte __mul_u8u8u8() {
|
||||||
|
@ -89,6 +95,16 @@ __divmod_u16u8u16u8_skip:
|
||||||
? RET
|
? 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() {
|
noinline asm word __mul_u16u8u16() {
|
||||||
? LD HL,0
|
? LD HL,0
|
||||||
? LD B,8
|
? LD B,8
|
||||||
|
@ -113,8 +129,17 @@ __mul_u16u8u16_skip:
|
||||||
#endif
|
#endif
|
||||||
? RET
|
? 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() {
|
noinline asm word __mul_u16u16u16() {
|
||||||
LD HL,0
|
LD HL,0
|
||||||
LD A,16
|
LD A,16
|
||||||
|
|
|
@ -45,7 +45,7 @@ case class CompilationOptions(platform: Platform,
|
||||||
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||||
|
|
||||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
||||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
|
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes, EmitR800Opcodes,
|
||||||
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
|
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
|
||||||
|
|
||||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
|
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")
|
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 (flags(EmitIntel8080Opcodes)) {
|
||||||
if (!Intel8080Compatible(platform.cpu)) {
|
if (!Intel8080Compatible(platform.cpu)) {
|
||||||
log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
|
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)),
|
"OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)),
|
||||||
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
|
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
|
||||||
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
|
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
|
||||||
|
"CPUFEATURE_R800" -> toLong(flag(CompilationFlag.EmitR800Opcodes)),
|
||||||
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
|
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
|
||||||
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
|
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
|
||||||
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
|
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
|
||||||
|
@ -290,7 +296,7 @@ object CpuFamily extends Enumeration {
|
||||||
import Cpu._
|
import Cpu._
|
||||||
cpu match {
|
cpu match {
|
||||||
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
|
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 Intel8086 | Intel80186 => I86
|
||||||
case Cpu.Motorola6809 => M6809
|
case Cpu.Motorola6809 => M6809
|
||||||
}
|
}
|
||||||
|
@ -368,6 +374,10 @@ object Cpu extends Enumeration {
|
||||||
* The Zilog Z80 processor, without illegal instructions
|
* The Zilog Z80 processor, without illegal instructions
|
||||||
*/
|
*/
|
||||||
val StrictZ80: Cpu.Value = Value
|
val StrictZ80: Cpu.Value = Value
|
||||||
|
/**
|
||||||
|
* The R800 CPU (used in MSX Turbo-R)
|
||||||
|
*/
|
||||||
|
val R800: Cpu.Value = Value
|
||||||
/**
|
/**
|
||||||
* The Zilog eZ80 processor
|
* The Zilog eZ80 processor
|
||||||
*/
|
*/
|
||||||
|
@ -400,11 +410,11 @@ object Cpu extends Enumeration {
|
||||||
/**
|
/**
|
||||||
* Processors that can run code for Zilog Z80
|
* 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
|
* 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
|
* Processors that can run code for Intel 8085
|
||||||
*/
|
*/
|
||||||
|
@ -468,6 +478,8 @@ object Cpu extends Enumeration {
|
||||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||||
case StrictZ80 | Z80 =>
|
case StrictZ80 | Z80 =>
|
||||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
|
||||||
|
case R800 =>
|
||||||
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitR800Opcodes)
|
||||||
case Z80Next =>
|
case Z80Next =>
|
||||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
|
||||||
case EZ80 =>
|
case EZ80 =>
|
||||||
|
@ -514,6 +526,7 @@ object Cpu extends Enumeration {
|
||||||
case "strict2a07" => StrictRicoh
|
case "strict2a07" => StrictRicoh
|
||||||
case "z80" => Z80
|
case "z80" => Z80
|
||||||
case "strictz80" => Z80
|
case "strictz80" => Z80
|
||||||
|
case "r800" => R800
|
||||||
case "zx80next" => Z80Next
|
case "zx80next" => Z80Next
|
||||||
case "z80next" => Z80Next
|
case "z80next" => Z80Next
|
||||||
// disabled for now:
|
// disabled for now:
|
||||||
|
@ -564,7 +577,7 @@ object CompilationFlag extends Enumeration {
|
||||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
|
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
|
||||||
// compilation options for I80
|
// compilation options for I80
|
||||||
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitR800Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
||||||
UseShadowRegistersForInterrupts,
|
UseShadowRegistersForInterrupts,
|
||||||
UseIxForStack, UseIyForStack,
|
UseIxForStack, UseIyForStack,
|
||||||
UseIxForScratch, UseIyForScratch,
|
UseIxForScratch, UseIyForScratch,
|
||||||
|
@ -628,6 +641,7 @@ object CompilationFlag extends Enumeration {
|
||||||
"emit_65ce02" -> Emit65CE02Opcodes,
|
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||||
"emit_huc6280" -> EmitHudsonOpcodes,
|
"emit_huc6280" -> EmitHudsonOpcodes,
|
||||||
"emit_z80" -> EmitZ80Opcodes,
|
"emit_z80" -> EmitZ80Opcodes,
|
||||||
|
"emit_r800" -> EmitR800Opcodes,
|
||||||
"emit_ez80" -> EmitEZ80Opcodes,
|
"emit_ez80" -> EmitEZ80Opcodes,
|
||||||
"emit_x80" -> EmitExtended80Opcodes,
|
"emit_x80" -> EmitExtended80Opcodes,
|
||||||
"emit_8080" -> EmitIntel8080Opcodes,
|
"emit_8080" -> EmitIntel8080Opcodes,
|
||||||
|
|
|
@ -46,10 +46,24 @@ case class Context(errorReporting: Logger,
|
||||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
|
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
|
||||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
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
|
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
|
addons += CompilationFlag.EmitIntel8080Opcodes -> true
|
||||||
}
|
}
|
||||||
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {
|
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {
|
||||||
|
|
|
@ -321,7 +321,9 @@ object Main {
|
||||||
val assemblyOptimizations = optLevel match {
|
val assemblyOptimizations = optLevel match {
|
||||||
case 0 => Nil
|
case 0 => Nil
|
||||||
case _ =>
|
case _ =>
|
||||||
if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
if (options.flag(CompilationFlag.EmitR800Opcodes))
|
||||||
|
Z80OptimizationPresets.GoodForR800
|
||||||
|
else if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||||
Z80OptimizationPresets.GoodForZ80
|
Z80OptimizationPresets.GoodForZ80
|
||||||
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
||||||
Z80OptimizationPresets.GoodForIntel8080
|
Z80OptimizationPresets.GoodForIntel8080
|
||||||
|
|
|
@ -1067,6 +1067,17 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||||
case LHLX | RLDE => r == D || r == E
|
case LHLX | RLDE => r == D || r == E
|
||||||
case RRHL => r == H || r == L
|
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
|
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 LHLX | RRHL | DSUB => r == H || r == L
|
||||||
case SHLX => false
|
case SHLX => false
|
||||||
|
|
||||||
|
case MULUB => r == H || r == L
|
||||||
|
case MULUW => r == H || r == L || r == D || r == E
|
||||||
|
|
||||||
case _ => true // TODO
|
case _ => true // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ object ZOpcode extends Enumeration {
|
||||||
LD_DESP, LD_DEHL, RRHL, RLDE, DSUB, RSTV, LHLX, SHLX,
|
LD_DESP, LD_DEHL, RRHL, RLDE, DSUB, RSTV, LHLX, SHLX,
|
||||||
//sharp:
|
//sharp:
|
||||||
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, SWAP, LDH_DA, LDH_AD, LDH_CA, LDH_AC, LD_HLSP, ADD_SP, STOP,
|
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:
|
// next:
|
||||||
LDIX, LDWS, LDIRX, LDDX, LDDRX, LDPIRX, OUTINB, MUL, SWAPNIB, MIRROR, NEXTREG, PIXELDN, PIXELAD, SETAE, TEST,
|
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,
|
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 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,
|
val EdInstructions: Set[ZOpcode.Value] = Set(NEG, RETN, RETI, IM, RRD, RLD,
|
||||||
|
MULUB, MULUW,
|
||||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||||
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR) ++ BIT ++ RES ++ SET
|
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR) ++ BIT ++ RES ++ SET
|
||||||
|
|
||||||
|
@ -63,6 +66,7 @@ object ZOpcodeClasses {
|
||||||
val ChangesAFAlways: Set[ZOpcode.Value] = Set( // TODO: !
|
val ChangesAFAlways: Set[ZOpcode.Value] = Set( // TODO: !
|
||||||
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, INC, DEC,
|
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, INC, DEC,
|
||||||
SCF, CCF, NEG, RIM,
|
SCF, CCF, NEG, RIM,
|
||||||
|
MULUB, MULUW,
|
||||||
LDH_AC, LDH_AD, LD_AHLI, LD_AHLD,
|
LDH_AC, LDH_AD, LD_AHLI, LD_AHLD,
|
||||||
ADD_16, ADC_16, SBC_16, INC_16, DEC_16,
|
ADD_16, ADC_16, SBC_16, INC_16, DEC_16,
|
||||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||||
|
@ -74,6 +78,7 @@ object ZOpcodeClasses {
|
||||||
LDIX, LDIRX, LDDX, LDDRX, LDPIRX,
|
LDIX, LDIRX, LDDX, LDDRX, LDPIRX,
|
||||||
EXX, CALL, JR, JP, LABEL, DJNZ)
|
EXX, CALL, JR, JP, LABEL, DJNZ)
|
||||||
val ChangesHLAlways: Set[ZOpcode.Value] = Set(
|
val ChangesHLAlways: Set[ZOpcode.Value] = Set(
|
||||||
|
MULUB, MULUW,
|
||||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||||
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
||||||
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, LD_HLSP, DSUB,
|
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,
|
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, PIXELAD, PIXELDN, OUTINB,
|
||||||
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
|
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
|
||||||
val ChangesDEAlways: Set[ZOpcode.Value] = Set(
|
val ChangesDEAlways: Set[ZOpcode.Value] = Set(
|
||||||
|
MULUW,
|
||||||
LDI, LDIR, LDD, LDDR,
|
LDI, LDIR, LDD, LDDR,
|
||||||
LD_DESP, LD_DEHL, RLDE,
|
LD_DESP, LD_DEHL, RLDE,
|
||||||
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, MUL,
|
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 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 ChangesFirstRegister: Set[ZOpcode.Value] = Set(LD, LD_16, ADD_16, SBC_16)
|
||||||
val ChangesAAlways: Set[ZOpcode.Value] = Set(
|
val ChangesAAlways: Set[ZOpcode.Value] = Set(
|
||||||
|
MULUB,
|
||||||
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, LD_AHLI, LD_AHLD, RIM,
|
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, LD_AHLI, LD_AHLD, RIM,
|
||||||
MIRROR, SETAE,
|
MIRROR, SETAE,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
|
}
|
|
@ -361,6 +361,38 @@ object CoarseFlowAnalyzer {
|
||||||
case ZLine0(RIM, _, _) =>
|
case ZLine0(RIM, _, _) =>
|
||||||
currentStatus = currentStatus.copy(a = AnyStatus)
|
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, _) =>
|
case ZLine0(opcode, registers, _) =>
|
||||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||||
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
|
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
|
||||||
|
|
|
@ -22,6 +22,21 @@ object Z80OptimizationPresets {
|
||||||
).flatten
|
).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]] = {
|
val GoodForIntel8080: List[AssemblyOptimization[ZLine]] = {
|
||||||
List.fill(5)(
|
List.fill(5)(
|
||||||
List.fill(5)(
|
List.fill(5)(
|
||||||
|
|
|
@ -67,6 +67,14 @@ class Z80Assembler(program: Program,
|
||||||
|
|
||||||
def requireZ80Illegals(): Unit = if (!options.flag(EmitZ80Opcodes) || !options.flag(EmitIllegals)) log.error("Unsupported instruction: " + instr)
|
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 requireExtended80(): Unit = if (!options.flag(EmitExtended80Opcodes)) log.error("Unsupported instruction: " + instr)
|
||||||
|
|
||||||
def requireSharp(): Unit = if (!options.flag(EmitSharpOpcodes)) 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)
|
writeByte(bank, index + 2, instr.parameter)
|
||||||
index + 3
|
index + 3
|
||||||
case ZLine0(op, OneRegister(ix@(IXH | IYH | IXL | IYL)), _) if oneRegister.contains(op) =>
|
case ZLine0(op, OneRegister(ix@(IXH | IYH | IXL | IYL)), _) if oneRegister.contains(op) =>
|
||||||
requireZ80Illegals()
|
requireR800OrIllegals()
|
||||||
val o = oneRegister(op)
|
val o = oneRegister(op)
|
||||||
writeByte(bank, index, prefixByte(ix))
|
writeByte(bank, index, prefixByte(ix))
|
||||||
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ix) * o.multiplier)
|
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ix) * o.multiplier)
|
||||||
|
@ -337,6 +345,7 @@ class Z80Assembler(program: Program,
|
||||||
index + 1
|
index + 1
|
||||||
case ZLine0(SLL, OneRegister(reg), _) =>
|
case ZLine0(SLL, OneRegister(reg), _) =>
|
||||||
requireZ80Illegals()
|
requireZ80Illegals()
|
||||||
|
requireNoR800()
|
||||||
writeByte(bank, index, 0xcb)
|
writeByte(bank, index, 0xcb)
|
||||||
writeByte(bank, index + 1, 0x30 + internalRegisterIndex(reg))
|
writeByte(bank, index + 1, 0x30 + internalRegisterIndex(reg))
|
||||||
index + 2
|
index + 2
|
||||||
|
@ -347,6 +356,7 @@ class Z80Assembler(program: Program,
|
||||||
index + 2
|
index + 2
|
||||||
case ZLine0(SLL, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) =>
|
case ZLine0(SLL, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) =>
|
||||||
requireZ80Illegals()
|
requireZ80Illegals()
|
||||||
|
requireNoR800()
|
||||||
writeByte(bank, index, prefixByte(ix))
|
writeByte(bank, index, prefixByte(ix))
|
||||||
writeByte(bank, index + 1, 0xcb)
|
writeByte(bank, index + 1, 0xcb)
|
||||||
writeByte(bank, index + 2, offset)
|
writeByte(bank, index + 2, offset)
|
||||||
|
@ -443,22 +453,22 @@ class Z80Assembler(program: Program,
|
||||||
writeByte(bank, index + 2, offset)
|
writeByte(bank, index + 2, offset)
|
||||||
index + 3
|
index + 3
|
||||||
case TwoRegisters(target@(IXH | IYH | IXL | IYL), source@(A | B | C | D | E)) =>
|
case TwoRegisters(target@(IXH | IYH | IXL | IYL), source@(A | B | C | D | E)) =>
|
||||||
requireZ80Illegals()
|
requireR800OrIllegals()
|
||||||
writeByte(bank, index, prefixByte(target))
|
writeByte(bank, index, prefixByte(target))
|
||||||
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
||||||
index + 2
|
index + 2
|
||||||
case TwoRegisters(target@(A | B | C | D | E), source@(IXH | IYH | IXL | IYL)) =>
|
case TwoRegisters(target@(A | B | C | D | E), source@(IXH | IYH | IXL | IYL)) =>
|
||||||
requireZ80Illegals()
|
requireR800OrIllegals()
|
||||||
writeByte(bank, index, prefixByte(source))
|
writeByte(bank, index, prefixByte(source))
|
||||||
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
||||||
index + 2
|
index + 2
|
||||||
case TwoRegisters(target@(IXH | IXL), source@(IXH | IXL)) =>
|
case TwoRegisters(target@(IXH | IXL), source@(IXH | IXL)) =>
|
||||||
requireZ80Illegals()
|
requireR800OrIllegals()
|
||||||
writeByte(bank, index, prefixByte(source))
|
writeByte(bank, index, prefixByte(source))
|
||||||
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
||||||
index + 2
|
index + 2
|
||||||
case TwoRegisters(target@(IYH | IYL), source@(IYH | IYL)) =>
|
case TwoRegisters(target@(IYH | IYL), source@(IYH | IYL)) =>
|
||||||
requireZ80Illegals()
|
requireR800OrIllegals()
|
||||||
writeByte(bank, index, prefixByte(source))
|
writeByte(bank, index, prefixByte(source))
|
||||||
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
||||||
index + 2
|
index + 2
|
||||||
|
@ -771,6 +781,61 @@ class Z80Assembler(program: Program,
|
||||||
requireIntel8085Illegals()
|
requireIntel8085Illegals()
|
||||||
writeByte(bank, index, 0x10)
|
writeByte(bank, index, 0x10)
|
||||||
index + 1
|
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 _ =>
|
case _ =>
|
||||||
log.fatal("Cannot assemble " + instr)
|
log.fatal("Cannot assemble " + instr)
|
||||||
index
|
index
|
||||||
|
|
|
@ -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 toRegister.contains(r)=> (toRegister(r), None)
|
||||||
case (VariableExpression(r), false)
|
case (VariableExpression(r), false)
|
||||||
if options.flag(CompilationFlag.EmitZ80Opcodes) &&
|
if options.flag(CompilationFlag.EmitZ80Opcodes) &&
|
||||||
options.flag(CompilationFlag.EmitIllegals) &&
|
(options.flag(CompilationFlag.EmitIllegals) || options.flag(CompilationFlag.EmitR800Opcodes)) &&
|
||||||
toIndexHalf.contains(r)=> (toIndexHalf(r), None)
|
toIndexHalf.contains(r)=> (toIndexHalf(r), None)
|
||||||
case (SumExpression(List(
|
case (SumExpression(List(
|
||||||
(false, LiteralExpression(0xff00, _)),
|
(false, LiteralExpression(0xff00, _)),
|
||||||
|
@ -534,7 +534,7 @@ case class Z80Parser(filename: String,
|
||||||
case "PIXELAD" => imm(PIXELAD)
|
case "PIXELAD" => imm(PIXELAD)
|
||||||
case "SETAE" => imm(SETAE)
|
case "SETAE" => imm(SETAE)
|
||||||
case "MUL" => (("D"|"d") ~ HWS ~ "," ~/ HWS ~ ("E" | "e")).?.map { _ => (MUL, NoRegisters, None, zero)}
|
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 "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.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))
|
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 "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 _ =>
|
case _ =>
|
||||||
log.error("Unsupported opcode " + opcode, Some(pos))
|
log.error("Unsupported opcode " + opcode, Some(pos))
|
||||||
imm(NOP)
|
imm(NOP)
|
||||||
|
|
|
@ -193,7 +193,7 @@ class ByteMathSuite extends FunSuite with Matchers with AppendedClues {
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Byte multiplication 2") {
|
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
|
| import zp_reg
|
||||||
| byte output1 @$c001, output2 @$c002, output3 @$c003
|
| byte output1 @$c001, output2 @$c002, output3 @$c003
|
||||||
|
|
|
@ -249,6 +249,7 @@ class StructSuite extends FunSuite with Matchers {
|
||||||
| p->tmp[1] = 77
|
| p->tmp[1] = 77
|
||||||
| outputAlias[0].tmp[id(3)] = 3
|
| outputAlias[0].tmp[id(3)] = 3
|
||||||
| outputAlias[id(0)].tmp[5] = 55
|
| outputAlias[id(0)].tmp[5] = 55
|
||||||
|
| output.tmp[6] = lo(output.tmp - output.addr)
|
||||||
|}
|
|}
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086, Cpu.Motorola6809)(code){ m =>
|
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(0xc003) should equal(3)
|
||||||
m.readByte(0xc004) should equal(4)
|
m.readByte(0xc004) should equal(4)
|
||||||
m.readByte(0xc005) should equal(55)
|
m.readByte(0xc005) should equal(55)
|
||||||
|
m.readByte(0xc006) should equal(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package millfork.test
|
package millfork.test
|
||||||
|
|
||||||
import millfork.Cpu
|
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}
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1340,4 +1340,42 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||||
""".stripMargin)
|
""".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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
object EmuIntel8086BenchmarkRun {
|
||||||
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
|
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
|
||||||
val (Timings(t0, _), m0) = EmuUnoptimizedIntel8086Run.apply2(source)
|
val (Timings(t0, _), m0) = EmuUnoptimizedIntel8086Run.apply2(source)
|
||||||
|
@ -170,6 +195,9 @@ object EmuCrossPlatformBenchmarkRun {
|
||||||
if (Settings.enableIntel8080Tests && platforms.contains(millfork.Cpu.Intel8080)) {
|
if (Settings.enableIntel8080Tests && platforms.contains(millfork.Cpu.Intel8080)) {
|
||||||
EmuIntel8080BenchmarkRun.apply(source)(verifier)
|
EmuIntel8080BenchmarkRun.apply(source)(verifier)
|
||||||
}
|
}
|
||||||
|
if (Settings.enableZ80Tests && platforms.contains(millfork.Cpu.R800)) {
|
||||||
|
EmuR800BenchmarkRun.apply(source)(verifier)
|
||||||
|
}
|
||||||
if (Settings.enableUnemulatedTests && platforms.contains(millfork.Cpu.Intel8085)) {
|
if (Settings.enableUnemulatedTests && platforms.contains(millfork.Cpu.Intel8085)) {
|
||||||
EmuUnoptimizedIntel8085Run.apply(source)
|
EmuUnoptimizedIntel8085Run.apply(source)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,10 @@ object EmuOptimizedInlinedSharpRun extends EmuZ80Run(Cpu.Sharp, OptimizationPres
|
||||||
override def inline: Boolean = true
|
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) {
|
object EmuOptimizedInlinedM6809Run extends EmuM6809Run(Cpu.Motorola6809, OptimizationPresets.NodeOpt, M6809OptimizationPresets.Default) {
|
||||||
override def inline: Boolean = true
|
override def inline: Boolean = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,4 +95,6 @@ object EmuSizeOptimizedIntel8080Run extends EmuZ80Run(Cpu.Intel8080, Optimizatio
|
||||||
|
|
||||||
object EmuOptimizedSharpRun extends EmuZ80Run(Cpu.Sharp, OptimizationPresets.NodeOpt, Z80OptimizationPresets.GoodForSharp)
|
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)
|
object EmuOptimizedM6809Run extends EmuM6809Run(Cpu.Motorola6809, OptimizationPresets.NodeOpt, M6809OptimizationPresets.Default)
|
||||||
|
|
|
@ -28,6 +28,8 @@ object EmuUnoptimizedIntel8085Run extends EmuZ80Run(Cpu.Intel8085, Nil, Nil)
|
||||||
|
|
||||||
object EmuUnoptimizedZ80NextRun extends EmuZ80Run(Cpu.Z80Next, 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 EmuUnoptimizedIntel8086Run extends EmuI86Run(Nil, Nil)
|
||||||
|
|
||||||
object EmuUnoptimizedSharpRun extends EmuZ80Run(Cpu.Sharp, Nil, Nil)
|
object EmuUnoptimizedSharpRun extends EmuZ80Run(Cpu.Sharp, Nil, Nil)
|
||||||
|
|
|
@ -30,6 +30,8 @@ import scala.collection.mutable
|
||||||
*/
|
*/
|
||||||
object EmuZ80Run {
|
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] = {
|
private def preload(cpu: millfork.Cpu.Value, filename: String): Option[Program] = {
|
||||||
TestErrorReporting.log.info(s"Loading $filename for $cpu")
|
TestErrorReporting.log.info(s"Loading $filename for $cpu")
|
||||||
val source = Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.mkString("\n")
|
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.SubroutineExtraction -> optimizeForSize,
|
||||||
CompilationFlag.EmitIllegals -> (cpu == millfork.Cpu.Z80 || cpu == millfork.Cpu.Intel8085 || cpu == millfork.Cpu.Z80Next),
|
CompilationFlag.EmitIllegals -> (cpu == millfork.Cpu.Z80 || cpu == millfork.Cpu.Intel8085 || cpu == millfork.Cpu.Z80Next),
|
||||||
CompilationFlag.EmitZ80NextOpcodes -> (cpu == millfork.Cpu.Z80Next),
|
CompilationFlag.EmitZ80NextOpcodes -> (cpu == millfork.Cpu.Z80Next),
|
||||||
|
CompilationFlag.EmitR800Opcodes -> (cpu == millfork.Cpu.R800),
|
||||||
CompilationFlag.LenientTextEncoding -> true)
|
CompilationFlag.LenientTextEncoding -> true)
|
||||||
if (source.contains("intel_syntax")) {
|
if (source.contains("intel_syntax")) {
|
||||||
extraFlags += CompilationFlag.UseIntelSyntaxForOutput -> true
|
extraFlags += CompilationFlag.UseIntelSyntaxForOutput -> true
|
||||||
|
@ -199,26 +202,35 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||||
method
|
method
|
||||||
}
|
}
|
||||||
val timings = platform.cpu match {
|
val timings = platform.cpu match {
|
||||||
case millfork.Cpu.Z80 | millfork.Cpu.Intel8080 =>
|
case millfork.Cpu.Z80 | millfork.Cpu.Intel8080 | millfork.Cpu.R800 =>
|
||||||
val cpu = new Z80Core(Z80Memory(memoryBank), DummyIO)
|
val hasMultiplications = (platform.cpu == millfork.Cpu.R800) && ((0x200 to 0xfffe).exists { addr =>
|
||||||
cpu.reset()
|
val b0 = memoryBank.output(addr)
|
||||||
cpu.setProgramCounter(0x1ed)
|
val b1 = memoryBank.output(addr + 1)
|
||||||
cpu.resetTStates()
|
b0.&(0xff) == 0xED && EmuZ80Run.secondBytesOfMulOnR800(b1.&(0xff))
|
||||||
while (!cpu.getHalt) {
|
})
|
||||||
cpu.executeOneInstruction()
|
if (hasMultiplications) {
|
||||||
if (resetN) {
|
Timings(-1, -1) -> memoryBank
|
||||||
resetNMethod.invoke(cpu)
|
} 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) {
|
val tStates = cpu.getTStates
|
||||||
log.debug("stack dump:")
|
Timings(tStates, tStates) -> memoryBank
|
||||||
(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
|
|
||||||
case millfork.Cpu.Sharp =>
|
case millfork.Cpu.Sharp =>
|
||||||
var ticks = 0L
|
var ticks = 0L
|
||||||
val cpu = GameboyStubs(memoryBank).cpu
|
val cpu = GameboyStubs(memoryBank).cpu
|
||||||
|
|
Loading…
Reference in New Issue
Block a user