diff --git a/docs/lang/assemblyz80.md b/docs/lang/assemblyz80.md index 88c237b8..76820d0f 100644 --- a/docs/lang/assemblyz80.md +++ b/docs/lang/assemblyz80.md @@ -12,15 +12,13 @@ There are two ways to include raw assembly code in your Millfork programs: Millfork uses Zilog syntax for Intel 8080, Z80 and LR35902 assembly. -**Work in progress**: -Intel syntax is not supported yet. -LR35902 instructions for faster access to the $FFxx addresses are not available yet. - 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` +LR35902 instructions for faster access to the $FFxx addresses use the `LDH` mnemonic: `LDH A,(4)`, `LDH (C),A` etc. + Only instructions available on the current CPU architecture are available. Undocumented instructions are not supported, except for `SLL`. diff --git a/src/main/scala/millfork/assembly/z80/ZLine.scala b/src/main/scala/millfork/assembly/z80/ZLine.scala index f5fed503..cbc900a3 100644 --- a/src/main/scala/millfork/assembly/z80/ZLine.scala +++ b/src/main/scala/millfork/assembly/z80/ZLine.scala @@ -279,6 +279,10 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case LD_AHLD => " LD A,(HLD)" case LD_HLIA => " LD (HLI),A" case LD_HLDA => " LD (HLD),A" + case LDH_AC => " LDH A,(C)" + case LDH_CA => " LDH (C),A" + case LDH_DA => s" LDH ($parameter),A" + case LDH_AD => s" LDH A,($parameter)" case LD_HLSP => " LD HL,SP+" + parameter case ADD_SP => " ADD SP," + parameter case EX_SP => registers match { @@ -606,6 +610,12 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case DJNZ => r == B case DAA | NEG | CPL | RLA | RRA | RLCA | RRCA => r == A case LABEL | DI | EI | NOP | HALT => false + case LDH_AC => r == C + case LDH_CA => r == C || r == A + case LDH_DA => r == A + case LDH_AD => false + case LD_HLIA | LD_HLDA => r == H || r == L | r == A + case LD_AHLI | LD_AHLD => r == H || r == L case _ => true // TODO } } @@ -742,6 +752,10 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case DJNZ => r == B case LABEL | DI | EI | NOP | HALT => false case CALL => r != IXH && r != IXL && r != SP + case LDH_CA | LDH_DA => false + case LDH_AC | LDH_AD => r == A + case LD_HLIA | LD_HLDA => r == H || r == L + case LD_AHLI | LD_AHLD => r == H || r == L | r == A case _ => true // TODO } } @@ -770,6 +784,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta DISCARD_A | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_HL | DISCARD_F => false case EX_DE_HL | NEG => false case LABEL | DI | EI | NOP => false + case LDH_AC | LDH_AD | LD_AHLI | LD_AHLD => false + case LDH_CA | LDH_DA | LD_HLIA | LD_HLDA => true case _ => true // TODO } } @@ -792,6 +808,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta DISCARD_A | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_HL | DISCARD_F => false case EX_DE_HL | NEG => false case LABEL | DI | EI | NOP | HALT => false + case LDH_AC | LDH_AD | LD_AHLI | LD_AHLD => true + case LDH_CA | LDH_DA | LD_HLIA | LD_HLDA => false case _ => true // TODO } } diff --git a/src/main/scala/millfork/assembly/z80/ZOpcode.scala b/src/main/scala/millfork/assembly/z80/ZOpcode.scala index ba4fdb52..8cc08ba1 100644 --- a/src/main/scala/millfork/assembly/z80/ZOpcode.scala +++ b/src/main/scala/millfork/assembly/z80/ZOpcode.scala @@ -24,7 +24,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_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, DISCARD_A, DISCARD_F, DISCARD_HL, DISCARD_BC, DISCARD_DE, DISCARD_IX, DISCARD_IY, LABEL, BYTE = Value } @@ -57,6 +57,7 @@ object ZOpcodeClasses { val ChangesAFAlways = Set( // TODO: ! DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, INC, DEC, SCF, CCF, NEG, + LDH_AC, LDH_AD, LD_AHLI, LD_AHLD, ADD_16, ADC_16, SBC_16, INC_16, DEC_16, INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR, LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR, @@ -68,6 +69,7 @@ object ZOpcodeClasses { val ChangesHLAlways = 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, EXX, EX_DE_HL, CALL, JR, JP, LABEL) val ChangesDEAlways = Set( LDI, LDIR, LDD, LDDR, diff --git a/src/main/scala/millfork/output/Z80Assembler.scala b/src/main/scala/millfork/output/Z80Assembler.scala index 9119e08e..cf4c3490 100644 --- a/src/main/scala/millfork/output/Z80Assembler.scala +++ b/src/main/scala/millfork/output/Z80Assembler.scala @@ -596,6 +596,24 @@ class Z80Assembler(program: Program, requireSharp() writeByte(bank, index, 0x32) index + 1 + case ZLine(LDH_AD, _, param, _) => + requireSharp() + writeByte(bank, index, 0xf0) + writeByte(bank, index + 1, param.loByte) + index + 2 + case ZLine(LDH_DA, _, param, _) => + requireSharp() + writeByte(bank, index, 0xe0) + writeByte(bank, index + 1, param.loByte) + index + 2 + case ZLine(LDH_AC, _, _, _) => + requireSharp() + writeByte(bank, index, 0xf2) + index + 1 + case ZLine(LDH_CA, _, _, _) => + requireSharp() + writeByte(bank, index, 0xe2) + index + 1 case ZLine(STOP, _, _, _) => requireSharp() writeByte(bank, index, 0x10) diff --git a/src/main/scala/millfork/parser/Z80Parser.scala b/src/main/scala/millfork/parser/Z80Parser.scala index 313ba52a..08d96835 100644 --- a/src/main/scala/millfork/parser/Z80Parser.scala +++ b/src/main/scala/millfork/parser/Z80Parser.scala @@ -88,10 +88,15 @@ case class Z80Parser(filename: String, "SP" -> ZRegister.SP, "sp" -> ZRegister.SP, ) - private def param(allowAbsolute: Boolean, allowRI: Boolean = false): P[(ZRegister.Value, Option[Expression])] = asmExpressionWithParens.map { + private def param(allowAbsolute: Boolean, allowRI: Boolean = false, allowFfc: Boolean = false): P[(ZRegister.Value, Option[Expression])] = asmExpressionWithParens.map { case (VariableExpression("R" | "r"), false) if allowRI => (ZRegister.R, None) case (VariableExpression("I" | "i"), false) if allowRI => (ZRegister.I, None) case (VariableExpression(r), false) if toRegister.contains(r)=> (toRegister(r), None) + case (SumExpression(List( + (false, LiteralExpression(0xff00, _)), + (false, VariableExpression("C" | "c")) + ), false), true) if allowFfc => (ZRegister.MEM_BC, None) // MEM_BC is a placeholder here for ($FF00 + C) + case (VariableExpression("C" | "c"), true) if allowFfc => (ZRegister.MEM_BC, None) // MEM_BC is a placeholder here for ($FF00 + C) case (VariableExpression("HL" | "hl"), true) => (ZRegister.MEM_HL, None) case (VariableExpression("BC" | "bc"), true) => (ZRegister.MEM_BC, None) case (VariableExpression("DE" | "de"), true) => (ZRegister.MEM_DE, None) @@ -361,6 +366,15 @@ case class Z80Parser(filename: String, } } + case "LDH" => (param(allowAbsolute = true, allowFfc = true) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = true, allowFfc = true)).map { + case (ZRegister.MEM_ABS_8, Some(expr), (ZRegister.A, None)) => (LDH_DA, NoRegisters, None, expr) + case (ZRegister.A, None, (ZRegister.MEM_ABS_8, Some(expr))) => (LDH_AD, NoRegisters, None, expr) + case (ZRegister.A, None, (ZRegister.MEM_BC, None)) => (LDH_AC, NoRegisters, None, zero) + case (ZRegister.MEM_BC, None, (ZRegister.A, None)) => (LDH_CA, NoRegisters, None, zero) + case _ => + log.error("Invalid parameters for LDH", Some(pos)) + (NOP, NoRegisters, None, zero) + } 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) => diff --git a/src/test/scala/millfork/test/Z80AssemblySuite.scala b/src/test/scala/millfork/test/Z80AssemblySuite.scala index 08dc32dc..ef5c7737 100644 --- a/src/test/scala/millfork/test/Z80AssemblySuite.scala +++ b/src/test/scala/millfork/test/Z80AssemblySuite.scala @@ -879,6 +879,10 @@ class Z80AssemblySuite extends FunSuite with Matchers { | swap c | swap(hl) | + | ldh a,(9) + | ldh (9),a + | ldh a,(c) + | ldh (c),a | ret | } """.stripMargin)