mirror of
https://github.com/KarolS/millfork.git
synced 2024-05-28 13:41:31 +00:00
Almost full LR35902 opcode space coverage
This commit is contained in:
parent
27de426a38
commit
7ea2fe6a4e
|
@ -27,7 +27,7 @@
|
|||
|
||||
* [Inline 6502 assembly syntax](lang/assembly.md)
|
||||
|
||||
* [Inline Z80 assembly syntax](lang/assemblyz80.md)
|
||||
* [Inline 8080/LR35902/Z80 assembly syntax](lang/assemblyz80.md)
|
||||
|
||||
* [Important guidelines regarding reentrancy](lang/reentrancy.md)
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ even up to hardware damage.
|
|||
|
||||
* on ROM-based platforms: using global variables with an initial value (they will not be initialized!)
|
||||
|
||||
* violating the [safe assembly rules](../lang/assembly.md)
|
||||
* violating the safe assembly rules ([6502](../lang/assembly.md), [8080/LR35902/Z80](../lang/assemblyz80.md))
|
||||
|
||||
* violating the [safe reentrancy rules](../lang/reentrancy.md)
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
# Undocumented opcodes
|
||||
|
||||
## 6502
|
||||
|
||||
Original 6502 processors accidentally supported a bunch of extra undocumented instructions.
|
||||
Millfork can emit them if so desired.
|
||||
|
||||
|
@ -61,3 +63,9 @@ and either it must appear in an assembly block or it may be a result of optimiza
|
|||
|
||||
Optimization will never emit any of the following opcodes due to their instability and/or uselessness:
|
||||
AHX, LAS, LXA, SHX, SHY, TAS, XAA.
|
||||
|
||||
## Z80
|
||||
|
||||
Original Z80 processors accidentally supported a bunch of extra undocumented instructions.
|
||||
Millfork will not emit them.
|
||||
The only exception is SLL, which will be emitted if it occurs in a handwritten assembly block.
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
* [Inline 6502 assembly syntax](lang/assembly.md)
|
||||
|
||||
* [Inline Z80 assembly syntax](lang/assemblyz80.md)
|
||||
* [Inline 8080/LR35902/Z80 assembly syntax](lang/assemblyz80.md)
|
||||
|
||||
* [Important guidelines regarding reentrancy](lang/reentrancy.md)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[< back to index](../index.md)
|
||||
|
||||
# Using Intel8080/Z80 assembly within Millfork programs
|
||||
# Using 8080/LR35902/Z80 assembly within Millfork programs
|
||||
|
||||
There are two ways to include raw assembly code in your Millfork programs:
|
||||
|
||||
|
@ -10,13 +10,18 @@ There are two ways to include raw assembly code in your Millfork programs:
|
|||
|
||||
## Assembly syntax
|
||||
|
||||
Millfork uses Zilog syntax for Intel 8080 and Z80 assembly. Intel syntax is not supported.
|
||||
Millfork uses Zilog syntax for Intel 8080, Z80 and LR35902 assembly. Intel syntax is not supported.
|
||||
|
||||
Indexing via the IX/IY register uses the following syntax: `IX(1)`
|
||||
|
||||
LR35902 instructions that load/store the accumulator indirectly via HL and then increment/decrement HL are written
|
||||
`LD A,(HLI)`, `LD, A,(HLD)`, `LD (HLI),A` and `LD (HLD),A`
|
||||
|
||||
Only instructions available on the current CPU architecture are available.
|
||||
Undocumented instructions are not supported, except for `SLL`.
|
||||
|
||||
LR35902 instructions for faster access to the $FFxx addresses are not available yet.
|
||||
|
||||
Labels have to be followed by a colon and they can optionally be on a separate line.
|
||||
Indentation is not important:
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ For more details about how to pass parameters to `asm` functions, see:
|
|||
|
||||
* for 6502: [Using 6502 assembly within Millfork programs#Assembly functions](./assembly.md#assembly-functions).
|
||||
|
||||
* for Z80: [Using Z80 assembly within Millfork programs#Assembly functions](./assembly.md#assembly-functions).
|
||||
* for Z80: [Using Z80 assembly within Millfork programs#Assembly functions](./assemblyz80.md#assembly-functions).
|
||||
|
||||
## Calling external functions at a dynamic address
|
||||
|
||||
|
|
|
@ -241,6 +241,12 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||
case RST => s" RST $parameter"
|
||||
case IM => s" IM $parameter"
|
||||
case EX_AF_AF => " EX AF,AF'"
|
||||
case LD_AHLI => " LD A,(HLI)"
|
||||
case LD_AHLD => " LD A,(HLD)"
|
||||
case LD_HLIA => " LD (HLI),A"
|
||||
case LD_HLDA => " LD (HLD),A"
|
||||
case LD_HLSP => " LD HL,SP+" + parameter
|
||||
case ADD_SP => " ADD SP," + parameter
|
||||
case EX_SP => registers match {
|
||||
case OneRegister(r) => s" EX (SP),${asAssemblyString(r)}"
|
||||
case _ => ???
|
||||
|
|
|
@ -23,7 +23,7 @@ object ZOpcode extends Enumeration {
|
|||
RST, IM, EI, DI,
|
||||
DJNZ, JP, JR, CALL, RET, RETN, RETI, HALT,
|
||||
//sharp:
|
||||
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, SWAP, LD_H,
|
||||
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, SWAP, LD_H, LD_HLSP, ADD_SP, STOP,
|
||||
DISCARD_A, DISCARD_F, DISCARD_HL, DISCARD_BCDEIX,
|
||||
LABEL, BYTE = Value
|
||||
}
|
||||
|
|
|
@ -138,17 +138,17 @@ object ZRegister extends Enumeration {
|
|||
|
||||
val A, B, C, D, E, H, L, AF, BC, HL, DE, SP, IXH, IXL, IYH, IYL, IX, IY, R, I, MEM_HL, MEM_BC, MEM_DE, MEM_IX_D, MEM_IY_D, MEM_ABS_8, MEM_ABS_16, IMM_8, IMM_16 = Value
|
||||
|
||||
def size(reg: Value): Int = reg match {
|
||||
def registerSize(reg: Value): Int = reg match {
|
||||
case AF | BC | DE | HL | IX | IY | IMM_16 => 2
|
||||
case A | B | C | D | E | H | L | IXH | IXL | IYH | IYL | R | I | IMM_8 => 1
|
||||
}
|
||||
|
||||
def matchingImm(target: ZRegister.Value): ZRegister.Value = size(target) match {
|
||||
def matchingImm(target: ZRegister.Value): ZRegister.Value = registerSize(target) match {
|
||||
case 1 => IMM_8
|
||||
case 2 => IMM_16
|
||||
}
|
||||
|
||||
def matchingMemAbs(target: ZRegister.Value): ZRegister.Value = size(target) match {
|
||||
def matchingMemAbs(target: ZRegister.Value): ZRegister.Value = registerSize(target) match {
|
||||
case 1 => MEM_ABS_8
|
||||
case 2 => MEM_ABS_16
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ class Z80Assembler(program: Program,
|
|||
options.flag(EmitSharpOpcodes)
|
||||
}
|
||||
|
||||
instr match {
|
||||
try { instr match {
|
||||
case ZLine(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName)), _) =>
|
||||
labelMap(labelName) = index
|
||||
index
|
||||
|
@ -142,10 +142,12 @@ class Z80Assembler(program: Program,
|
|||
writeByte(bank, index + 1, 9 + 16 * internalRegisterIndex(source))
|
||||
index + 2
|
||||
case ZLine(ADC_16, TwoRegisters(ZRegister.HL, reg), _, _) =>
|
||||
requireZ80()
|
||||
writeByte(bank, index, 0xed)
|
||||
writeByte(bank, index + 1, 0x4a + 0x10 * internalRegisterIndex(reg))
|
||||
index + 2
|
||||
case ZLine(SBC_16, TwoRegisters(ZRegister.HL, reg), _, _) =>
|
||||
requireZ80()
|
||||
writeByte(bank, index, 0xed)
|
||||
writeByte(bank, index + 1, 0x42 + 0x10 * internalRegisterIndex(reg))
|
||||
index + 2
|
||||
|
@ -342,11 +344,13 @@ class Z80Assembler(program: Program,
|
|||
writeByte(bank, index, 0x1a)
|
||||
index + 1
|
||||
case TwoRegistersOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), source, offset) =>
|
||||
requireZ80()
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(ZRegister.MEM_HL) * 8)
|
||||
writeByte(bank, index + 2, offset)
|
||||
index + 3
|
||||
case TwoRegistersOffset(target, ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset) =>
|
||||
requireZ80()
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
|
||||
writeByte(bank, index + 2, offset)
|
||||
|
@ -568,9 +572,44 @@ class Z80Assembler(program: Program,
|
|||
requireIntel8080()
|
||||
writeByte(bank, index, 0xeb)
|
||||
index + 1
|
||||
|
||||
case ZLine(ADD_SP, _, param, _) =>
|
||||
requireSharp()
|
||||
writeByte(bank, index, 0xe8)
|
||||
writeByte(bank, index + 1, param)
|
||||
index + 2
|
||||
case ZLine(LD_HLSP, _, param, _) =>
|
||||
requireSharp()
|
||||
writeByte(bank, index, 0xf8)
|
||||
writeByte(bank, index + 1, param)
|
||||
index + 2
|
||||
case ZLine(LD_AHLI, _, _, _) =>
|
||||
requireSharp()
|
||||
writeByte(bank, index, 0x2a)
|
||||
index + 1
|
||||
case ZLine(LD_AHLD, _, _, _) =>
|
||||
requireSharp()
|
||||
writeByte(bank, index, 0x3a)
|
||||
index + 1
|
||||
case ZLine(LD_HLIA, _, _, _) =>
|
||||
requireSharp()
|
||||
writeByte(bank, index, 0x22)
|
||||
index + 1
|
||||
case ZLine(LD_HLDA, _, _, _) =>
|
||||
requireSharp()
|
||||
writeByte(bank, index, 0x32)
|
||||
index + 1
|
||||
case ZLine(STOP, _, _, _) =>
|
||||
requireSharp()
|
||||
writeByte(bank, index, 0x10)
|
||||
index + 1
|
||||
case _ =>
|
||||
ErrorReporting.fatal("Cannot assemble " + instr)
|
||||
index
|
||||
} } catch {
|
||||
case _ : MatchError =>
|
||||
ErrorReporting.fatal("Cannot assemble " + instr)
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,7 +652,6 @@ object Z80Assembler {
|
|||
immediates(CP) = 0xfe
|
||||
|
||||
edImplieds(NEG) = 0x44
|
||||
edImplieds(RETI) = 0x4d
|
||||
edImplieds(RETN) = 0x45
|
||||
edImplieds(RRD) = 0x67
|
||||
edImplieds(RLD) = 0x6f
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.util.Locale
|
|||
|
||||
import fastparse.all.{parserApi, _}
|
||||
import fastparse.core
|
||||
import millfork.CompilationOptions
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.z80.{ZOpcode, _}
|
||||
import millfork.env.{ByZRegister, Constant, ParamPassingConvention}
|
||||
import millfork.error.ErrorReporting
|
||||
|
@ -92,16 +92,16 @@ case class Z80Parser(filename: String, input: String, currentDirectory: String,
|
|||
}
|
||||
|
||||
private val jumpCondition: P[ZRegisters] = (HWS ~ (
|
||||
"NZ" | "nz" | "nc" | "NC" |
|
||||
"NZ" | "nz" | "nc" | "NC" | "NV" | "nv" |
|
||||
"PO" | "po" | "PE" | "pe" |
|
||||
"m" | "M" | "p" | "P" |
|
||||
"c" | "C" | "Z" | "z").! ~ HWS).map {
|
||||
"c" | "C" | "Z" | "z" | "V" | "v").! ~ HWS).map {
|
||||
case "Z" | "z" => IfFlagSet(ZFlag.Z)
|
||||
case "PE" | "pe" => IfFlagSet(ZFlag.P)
|
||||
case "PE" | "pe" | "v" | "V" => IfFlagSet(ZFlag.P)
|
||||
case "C" | "c" => IfFlagSet(ZFlag.C)
|
||||
case "M" | "m" => IfFlagSet(ZFlag.S)
|
||||
case "NZ" | "nz" => IfFlagClear(ZFlag.Z)
|
||||
case "PO" | "po" => IfFlagClear(ZFlag.P)
|
||||
case "PO" | "po" | "NV" | "nv" => IfFlagClear(ZFlag.P)
|
||||
case "NC" | "nc" => IfFlagClear(ZFlag.C)
|
||||
case "P" | "p" => IfFlagClear(ZFlag.S)
|
||||
case _ => NoRegisters // shouldn't happen
|
||||
|
@ -162,12 +162,13 @@ case class Z80Parser(filename: String, input: String, currentDirectory: String,
|
|||
el <- elidable
|
||||
pos <- position()
|
||||
opcode: String <- identifier ~/ HWS
|
||||
tuple4/*: (ZOpcode.Value, ZRegisters, Option[Expression], Expression)*/ <- opcode.toUpperCase(Locale.ROOT) match {
|
||||
tuple4: (ZOpcode.Value, ZRegisters, Option[Expression], Expression) <- opcode.toUpperCase(Locale.ROOT) match {
|
||||
case "RST" => asmExpression.map((RST, NoRegisters, None, _))
|
||||
case "IM" => asmExpression.map((IM, NoRegisters, None, _))
|
||||
case "EI" => imm(EI)
|
||||
case "DI" => imm(DI)
|
||||
case "HALT" => imm(HALT)
|
||||
case "STOP" => imm(STOP)
|
||||
|
||||
case "RETN" => imm(RETN)
|
||||
case "RETI" => imm(RETI)
|
||||
|
@ -209,6 +210,7 @@ case class Z80Parser(filename: String, input: String, currentDirectory: String,
|
|||
case "SLL" => one8Register(SLL)
|
||||
case "SRA" => one8Register(SRA)
|
||||
case "SRL" => one8Register(SRL)
|
||||
case "SWAP" => one8Register(SWAP)
|
||||
|
||||
case "BIT" => (param(false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(false)).map {
|
||||
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), (r2, e2))
|
||||
|
@ -295,7 +297,7 @@ case class Z80Parser(filename: String, input: String, currentDirectory: String,
|
|||
}
|
||||
case "EX" => (asmExpressionWithParens ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpressionWithParensOrApostrophe).map { p: (Expression, Boolean, (Expression, Boolean)) =>
|
||||
p match {
|
||||
case (VariableExpression("AF" | "af"), false, (VariableExpression("AF" | "af"), true)) =>
|
||||
case (VariableExpression("AF" | "af"), false, (VariableExpression("AF" | "af"), _)) =>
|
||||
(EX_AF_AF, NoRegisters, None, zero)
|
||||
case (VariableExpression("DE" | "de"), false, (VariableExpression("HL" | "hl"), false)) =>
|
||||
(EX_DE_HL, NoRegisters, None, zero)
|
||||
|
@ -314,9 +316,31 @@ case class Z80Parser(filename: String, input: String, currentDirectory: String,
|
|||
}
|
||||
|
||||
case "LD" => (param(allowAbsolute = true, allowRI = true) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = true, allowRI = true)).map {
|
||||
case (ZRegister.HL, None, (ZRegister.IMM_8 | ZRegister.IMM_16, Some(SumExpression((false, VariableExpression("sp" | "SP")) :: offset, false))))
|
||||
if options.flags(CompilationFlag.EmitSharpOpcodes) =>
|
||||
(LD_HLSP, OneRegister(ZRegister.IMM_8), None, offset match {
|
||||
case List((false, expr)) => expr
|
||||
case (true, _) :: _ => SumExpression((false -> LiteralExpression(0, 1)) :: offset, decimal = false)
|
||||
case _ => SumExpression(offset, decimal = false)
|
||||
})
|
||||
case (ZRegister.A, None, (ZRegister.MEM_ABS_8, Some(VariableExpression("HLI" | "hli"))))
|
||||
if options.flags(CompilationFlag.EmitSharpOpcodes) =>
|
||||
(LD_AHLI, NoRegisters, None, zero)
|
||||
case (ZRegister.A, None, (ZRegister.MEM_ABS_8, Some(VariableExpression("HLD" | "hld"))))
|
||||
if options.flags(CompilationFlag.EmitSharpOpcodes) =>
|
||||
(LD_AHLD, NoRegisters, None, zero)
|
||||
case (ZRegister.MEM_ABS_8, Some(VariableExpression("HLI" | "hli")), (ZRegister.A, None))
|
||||
if options.flags(CompilationFlag.EmitSharpOpcodes) =>
|
||||
(LD_HLIA, NoRegisters, None, zero)
|
||||
case (ZRegister.MEM_ABS_8, Some(VariableExpression("HLD" | "hld")), (ZRegister.A, None))
|
||||
if options.flags(CompilationFlag.EmitSharpOpcodes) =>
|
||||
(LD_HLDA, NoRegisters, None, zero)
|
||||
|
||||
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.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))
|
||||
}
|
||||
case "ADC" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
|
||||
|
|
Loading…
Reference in New Issue
Block a user