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

ZX Spectrum Next CPU support

This commit is contained in:
Karol Stasiak 2019-10-01 00:46:15 +02:00
parent f7dd78e4c0
commit d1058b6223
11 changed files with 178 additions and 13 deletions

View File

@ -31,6 +31,8 @@ if a line ends with a backslash character, the value continues to the next line.
* `strictz80` (Z80 without illegal instructions)
* `z80next` (Z80 core from ZX Spectrum Next)
* `i8080` (Intel 8080)
* `i8085` (Intel 8085)

View File

@ -25,6 +25,8 @@ Only instructions available on the current CPU architecture are available.
Intel syntax does not support instructions that are unavailable on the 8080.
Undocumented Z80 instructions are not supported, except for `SLL`.
Not all ZX Spectrum Next are supported. `JP (C)`, `BSLA` and similar instructions are not supported.
Labels have to be followed by a colon and they can optionally be on a separate line.
Indentation is not important:

View File

@ -52,7 +52,7 @@ The following features are defined based on the chosen CPU and compilation optio
* `CPUFEATURE_65C02`, `CPUFEATURE_65CE02`, `CPUFEATURE_HUC6280`, `CPUFEATURE_65816_EMULATION`, `CPUFEATURE_65816_NATIVE`,
`CPUFEATURE_8080`, `CPUFEATURE_8085`, `CPUFEATURE_GAMEBOY`, `CPUFEATURE_Z80`,
`CPUFEATURE_6502_ILLEGALS`, `CPUFEATURE_8085_ILLEGALS`, `CPUFEATURE_Z80_ILLEGALS` 1 if given instruction subset is enabled, 0 otherwise
`CPUFEATURE_6502_ILLEGALS`, `CPUFEATURE_8085_ILLEGALS`, `CPUFEATURE_Z80_ILLEGALS`, `CPUFEATURE_Z80_NEXT` 1 if given instruction subset is enabled, 0 otherwise
* `ENCODING_SAME` - 1 if the encodings `default` and `src` are the same, 0 otherwise.

View File

@ -4,9 +4,15 @@
#endif
#pragma zilog_syntax
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
#if CPUFEATURE_Z80_NEXT
inline asm byte __mul_u8u8u8() {
? LD E,A
? MUL
? LD A, E
? RET
}
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
//A = A * D
noinline asm byte __mul_u8u8u8() {
? LD E,A
? LD A, 0

View File

@ -43,7 +43,7 @@ case class CompilationOptions(platform: Platform,
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes,
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
@ -111,7 +111,7 @@ case class CompilationOptions(platform: Platform,
}
case CpuFamily.I80 =>
if (flags(EmitIllegals)) {
if (platform.cpu != Z80 && platform.cpu != Intel8085) {
if (platform.cpu != Z80 && platform.cpu != Intel8085 && platform.cpu != Z80Next) {
log.error("Illegal opcodes enabled for architecture that doesn't support them")
}
}
@ -160,6 +160,11 @@ case class CompilationOptions(platform: Platform,
log.error("Sharp LR35902 opcodes enabled for architecture that doesn't support them")
}
}
if (flags(EmitZ80NextOpcodes)) {
if (platform.cpu != Z80Next) {
log.error("ZX Spectrum Next opcodes enabled for architecture that doesn't support them")
}
}
if (flags(EmitExtended80Opcodes)) {
if (platform.cpu != Sharp && !Z80Compatible(platform.cpu)) {
log.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
@ -221,6 +226,7 @@ case class CompilationOptions(platform: Platform,
"CPUFEATURE_65816_NATIVE" -> toLong(flag(CompilationFlag.EmitNative65816Opcodes)),
"CPUFEATURE_6502_ILLEGALS" -> toLong(platform.cpuFamily == CpuFamily.M6502 && flag(CompilationFlag.EmitIllegals)),
"CPUFEATURE_Z80_ILLEGALS" -> toLong(flag(CompilationFlag.EmitZ80Opcodes) && flag(CompilationFlag.EmitIllegals)),
"CPUFEATURE_Z80_NEXT" -> toLong(flag(CompilationFlag.EmitZ80NextOpcodes)),
"CPUFEATURE_8085_ILLEGALS" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes) && flag(CompilationFlag.EmitIllegals)),
"BIG_ENDIAN" -> toLong(Cpu.isBigEndian(platform.cpu)),
"LITTLE_ENDIAN" -> toLong(!Cpu.isBigEndian(platform.cpu)),
@ -276,7 +282,7 @@ object CpuFamily extends Enumeration {
import Cpu._
cpu match {
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | HuC6280 | CE02 | Sixteen => M6502
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 => I80
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 | Z80Next => I80
case Intel8086 | Intel80186 => I86
case Cpu.Motorola6809 => M6809
}
@ -346,6 +352,10 @@ object Cpu extends Enumeration {
* The Zilog eZ80 processor
*/
val EZ80: Cpu.Value = Value
/**
* The Z80 core from the ZX Spectrum Next
*/
val Z80Next: Cpu.Value = Value
/**
* The Sharp LR35902 processor
*/
@ -370,11 +380,11 @@ object Cpu extends Enumeration {
/**
* Processors that can run code for Zilog Z80
*/
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80)
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80, Z80Next)
/**
* Processors that can run code for Intel 8080
*/
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80)
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80, Z80Next)
/**
* Processors that can run code for Intel 8085
*/
@ -415,6 +425,8 @@ object Cpu extends Enumeration {
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
case StrictZ80 | Z80 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
case Z80Next =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
case EZ80 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitEZ80Opcodes)
case Sharp =>
@ -456,6 +468,7 @@ object Cpu extends Enumeration {
case "strict2a07" => StrictRicoh
case "z80" => Z80
case "strictz80" => Z80
case "zx80next" => Z80Next
// disabled for now:
// case "ez80" => EZ80
case "gameboy" => Sharp
@ -504,7 +517,7 @@ object CompilationFlag extends Enumeration {
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
// compilation options for I80
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes,
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
UseShadowRegistersForInterrupts,
UseIxForStack, UseIyForStack,
UseIxForScratch, UseIyForScratch,

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,
// 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,
LABEL, BYTE = Value
}
@ -69,19 +71,25 @@ object ZOpcodeClasses {
val ChangesBCAlways: Set[ZOpcode.Value] = Set(
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
LDIX, LDIRX, LDDX, LDDRX, LDPIRX,
EXX, CALL, JR, JP, LABEL, DJNZ)
val ChangesHLAlways: Set[ZOpcode.Value] = Set(
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,
RRHL, LHLX,
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, PIXELAD, PIXELDN, OUTINB,
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
val ChangesDEAlways: Set[ZOpcode.Value] = Set(
LDI, LDIR, LDD, LDDR,
LD_DESP, LD_DEHL, RLDE,
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, MUL,
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
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(DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, LD_AHLI, LD_AHLD, RIM)
val ChangesAAlways: Set[ZOpcode.Value] = Set(
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, LD_AHLI, LD_AHLD, RIM,
MIRROR, SETAE,
)
val NonLinear: Set[ZOpcode.Value] = Set(JP, JR, CALL, LABEL, BYTE, EXX, EX_DE_HL, EX_SP, EXX, RET, RETI, RETN, HALT, RST, RSTV)
}

View File

@ -69,6 +69,8 @@ class Z80Assembler(program: Program,
def requireIntel8085Illegals(): Unit = if (!options.flag(EmitIntel8085Opcodes) || !options.flag(EmitIllegals)) log.error("Unsupported instruction: " + instr)
def requireNext(): Unit = if (!options.flag(EmitZ80NextOpcodes)) log.error("Unsupported instruction: " + instr)
def useSharpOpcodes():Boolean = {
if (!options.flag(EmitSharpOpcodes) && !options.flag(EmitIntel8080Opcodes))
log.error("Cannot determine which variant to emit : " + instr)
@ -144,6 +146,47 @@ class Z80Assembler(program: Program,
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, edImplieds(op))
index + 2
case ZLine0(op, NoRegisters, _) if nextEdImplieds.contains(op) =>
requireNext()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, nextEdImplieds(op))
index + 2
case ZLine0(ADD_16, TwoRegisters(r@(ZRegister.HL | ZRegister.BC | ZRegister.DE), ZRegister.A), _) =>
requireNext()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x33 - internalRegisterIndex(r))
index + 2
case ZLine0(ADD_16, TwoRegisters(r@(ZRegister.HL | ZRegister.BC | ZRegister.DE), ZRegister.IMM_16), nn) =>
requireNext()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x36 - internalRegisterIndex(r))
writeWord(bank, index + 2, nn)
index + 4
case ZLine0(PUSH, OneRegister(ZRegister.IMM_16), nn) =>
requireNext()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x8A)
writeByte(bank, index + 2, nn.hiByte)
writeByte(bank, index + 3, nn.loByte)
index + 4
case ZLine0(TEST, OneRegister(ZRegister.IMM_8), nn) =>
requireNext()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x27)
writeByte(bank, index + 2, nn)
index + 3
case ZLine0(NEXTREG, TwoRegisters(ZRegister.IMM_8, ZRegister.IMM_8), nn) =>
requireNext()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x91)
writeWord(bank, index + 2, nn)
index + 4
case ZLine0(NEXTREG, TwoRegisters(ZRegister.IMM_8, ZRegister.A), nn) =>
requireNext()
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x92)
writeByte(bank, index + 2, nn)
index + 3
case ZLine0(ADD_16, TwoRegisters(ZRegister.HL, source), _) =>
writeByte(bank, index, 9 + 16 * internalRegisterIndex(source))
index + 1
@ -776,6 +819,7 @@ object Z80Assembler {
val edImplieds: mutable.Map[ZOpcode.Value, Int] = mutable.Map[ZOpcode.Value, Int]()
val oneRegister: mutable.Map[ZOpcode.Value, One] = mutable.Map[ZOpcode.Value, One]()
val cbOneRegister: mutable.Map[ZOpcode.Value, One] = mutable.Map[ZOpcode.Value, One]()
val nextEdImplieds: mutable.Map[ZOpcode.Value, Int] = mutable.Map[ZOpcode.Value, Int]()
do {
import ZOpcode._
@ -846,6 +890,20 @@ object Z80Assembler {
cbOneRegister(SLA) = One(0x20, 1)
cbOneRegister(SRA) = One(0x28, 1)
cbOneRegister(SRL) = One(0x38, 1)
nextEdImplieds(LDIX) = 0xa4
nextEdImplieds(LDWS) = 0xa5
nextEdImplieds(LDIRX) = 0xb4
nextEdImplieds(LDDX) = 0xb5
nextEdImplieds(LDDRX) = 0xac
nextEdImplieds(LDPIRX) = 0xbc
nextEdImplieds(OUTINB) = 0x90
nextEdImplieds(MUL) = 0xa4
nextEdImplieds(SWAPNIB) = 0x23
nextEdImplieds(MIRROR) = 0x24
nextEdImplieds(PIXELDN) = 0x93
nextEdImplieds(PIXELAD) = 0x94
nextEdImplieds(SETAE) = 0x95
} while (false)
}

View File

@ -452,6 +452,20 @@ case class Z80Parser(filename: String,
case (r1, e1, (r2, e2)) => merge(LD, LD_16, skipTargetA = false)((r1, e1, r2, e2))
}
case "ADD" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (ZRegister.HL, None, (ZRegister.A, None)) if options.flags(CompilationFlag.EmitZ80NextOpcodes) =>
(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.A), None, zero)
case (ZRegister.DE, None, (ZRegister.A, None)) if options.flags(CompilationFlag.EmitZ80NextOpcodes) =>
(ADD_16, TwoRegisters(ZRegister.DE, ZRegister.A), None, zero)
case (ZRegister.BC, None, (ZRegister.A, None)) if options.flags(CompilationFlag.EmitZ80NextOpcodes) =>
(ADD_16, TwoRegisters(ZRegister.BC, ZRegister.A), None, zero)
case (ZRegister.HL, None, (ZRegister.IMM_8, Some(expr))) =>
(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), None, expr)
case (ZRegister.DE, None, (ZRegister.IMM_8, Some(expr))) =>
(ADD_16, TwoRegisters(ZRegister.DE, ZRegister.IMM_16), None, expr)
case (ZRegister.BC, None, (ZRegister.IMM_8, Some(expr))) =>
(ADD_16, TwoRegisters(ZRegister.BC, ZRegister.IMM_16), None, expr)
case (ZRegister.SP, None, (ZRegister.IMM_8, Some(expr))) if options.flags(CompilationFlag.EmitSharpOpcodes) =>
(ADD_SP, OneRegister(ZRegister.IMM_8), None, expr)
case (r1, e1, (r2, e2)) => merge(ADD, ADD_16, skipTargetA = true)((r1, e1, r2, e2))
@ -466,6 +480,28 @@ case class Z80Parser(filename: String,
case "DSUB" => imm(DSUB)
case "RSTV" => imm(RSTV)
case "LDIX" => imm(LDIX)
case "LDWS" => imm(LDWS)
case "LDIRX" => imm(LDIRX)
case "LDDX" => imm(LDDX)
case "LDDRX" => imm(LDDRX)
case "LDPIRX" => imm(LDPIRX)
case "OUTINB" => imm(OUTINB)
case "SWAPNIB" => imm(SWAPNIB)
case "PIXELDN" => imm(PIXELDN)
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 "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))
case _ =>
log.error("Invalid parameters for NEXTREG", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "TEST" => one8Register(TEST)
case _ =>
log.error("Unsupported opcode " + opcode, Some(pos))
imm(NOP)

View File

@ -1,7 +1,7 @@
package millfork.test
import millfork.Cpu
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedIntel8080Run, EmuUnoptimizedIntel8085Run, EmuUnoptimizedSharpRun, EmuUnoptimizedZ80Run}
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedIntel8080Run, EmuUnoptimizedIntel8085Run, EmuUnoptimizedSharpRun, EmuUnoptimizedZ80NextRun, EmuUnoptimizedZ80Run}
import org.scalatest.{FunSuite, Matchers}
/**
@ -975,4 +975,41 @@ class Z80AssemblySuite extends FunSuite with Matchers {
| }
""".stripMargin)
}
test("Z80 Next instructions (Zilog syntax)") {
EmuUnoptimizedZ80NextRun(
"""
| #pragma zilog_syntax
| asm void main () {
| ret
| ldix
| ldws
| ldirx
| lddx
| lddrx
| ldpirx
| outinb
| mul
| mul d,e
| add hl,a
| add de,a
| add bc,a
| add hl,1
| add de,2
| add bc,3
| swapnib
| mirror
| mirror a
| push $5555
| nextreg 1,2
| nextreg 1,a
| pixeldn
| pixelad
| setae
| test 8
| ret
| }
""".stripMargin)
}
}

View File

@ -22,6 +22,8 @@ object EmuUnoptimizedIntel8080Run extends EmuZ80Run(Cpu.Intel8080, Nil, Nil)
object EmuUnoptimizedIntel8085Run extends EmuZ80Run(Cpu.Intel8085, Nil, Nil)
object EmuUnoptimizedZ80NextRun extends EmuZ80Run(Cpu.Z80Next, Nil, Nil)
object EmuUnoptimizedIntel8086Run extends EmuI86Run(Nil, Nil)
object EmuUnoptimizedSharpRun extends EmuZ80Run(Cpu.Sharp, Nil, Nil)

View File

@ -83,7 +83,8 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
CompilationFlag.OptimizeStdlib -> this.inline,
CompilationFlag.OptimizeForSize -> this.optimizeForSize,
CompilationFlag.SubroutineExtraction -> optimizeForSize,
CompilationFlag.EmitIllegals -> (cpu == millfork.Cpu.Z80 || cpu == millfork.Cpu.Intel8085),
CompilationFlag.EmitIllegals -> (cpu == millfork.Cpu.Z80 || cpu == millfork.Cpu.Intel8085 || cpu == millfork.Cpu.Z80Next),
CompilationFlag.EmitZ80NextOpcodes -> (cpu == millfork.Cpu.Z80Next),
CompilationFlag.LenientTextEncoding -> true)
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap ++ extraFlags, None, 0, Map(), JobContext(log, new LabelGenerator))
println(cpu)