1
0
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:
Karol Stasiak 2018-07-27 19:07:12 +02:00
parent 27de426a38
commit 7ea2fe6a4e
11 changed files with 100 additions and 19 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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 _ => ???

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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 {