1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00
millfork/src/main/scala/millfork/parser/Z80Parser.scala
2021-09-18 00:36:16 +02:00

931 lines
51 KiB
Scala

package millfork.parser
import java.util.Locale
import fastparse.all.{parserApi, _}
import fastparse.core
import millfork.assembly.{Elidability, z80}
import millfork.{CompilationFlag, CompilationOptions, node}
import millfork.assembly.z80.{ZOpcode, _}
import millfork.env.{ByZRegister, Constant, ParamPassingConvention}
import millfork.error.ConsoleLogger
import millfork.node.{ZRegister, _}
import millfork.output.{MemoryAlignment, NoAlignment, WithinPageAlignment}
/**
* @author Karol Stasiak
*/
case class Z80Parser(filename: String,
input: String,
currentDirectory: String,
options: CompilationOptions,
featureConstants: Map[String, Long],
useIntelSyntax: Boolean) extends MfParser[ZLine](filename, input, currentDirectory, options, featureConstants) {
import MfParser._
def allowIntelHexAtomsInAssembly: Boolean = useIntelSyntax
def fastAlignmentForArrays: MemoryAlignment = WithinPageAlignment
def fastAlignmentForFunctions: MemoryAlignment = NoAlignment
private val zero = LiteralExpression(0, 1)
override val appcRegister: P[ParamPassingConvention] = (P("hl" | "bc" | "de" | "a" | "b" | "c" | "d" | "e" | "h" | "l").! ~ !letterOrDigit).map {
case "a" => ByZRegister(ZRegister.A)
case "b" => ByZRegister(ZRegister.B)
case "c" => ByZRegister(ZRegister.C)
case "d" => ByZRegister(ZRegister.D)
case "e" => ByZRegister(ZRegister.E)
case "h" => ByZRegister(ZRegister.H)
case "l" => ByZRegister(ZRegister.L)
case "hl" => ByZRegister(ZRegister.HL)
case "bc" => ByZRegister(ZRegister.BC)
case "de" => ByZRegister(ZRegister.DE)
case x => log.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
override val asmParamDefinition: P[ParameterDeclaration] = for {
p <- position()
typ <- identifier ~ SWS
appc <- appcRegister | appcComplex
} yield ParameterDeclaration(typ, appc).pos(p)
val asmLabel: P[ExecutableStatement] = ((".".? ~ identifier).! ~ HWS ~ ":" ~/ AWS_asm).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, None, VariableExpression(l), elidability = Elidability.Elidable))
val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall(false)).map(ExpressionStatement)
private val toRegister: Map[String, ZRegister.Value] = Map(
"A" -> ZRegister.A, "a" -> ZRegister.A,
"B" -> ZRegister.B, "b" -> ZRegister.B,
"C" -> ZRegister.C, "c" -> ZRegister.C,
"D" -> ZRegister.D, "d" -> ZRegister.D,
"E" -> ZRegister.E, "e" -> ZRegister.E,
"H" -> ZRegister.H, "h" -> ZRegister.H,
"L" -> ZRegister.L, "l" -> ZRegister.L,
"HL" -> ZRegister.HL, "hl" -> ZRegister.HL,
"AF" -> ZRegister.AF, "af" -> ZRegister.AF,
"BC" -> ZRegister.BC, "bc" -> ZRegister.BC,
"DE" -> ZRegister.DE, "de" -> ZRegister.DE,
"IX" -> ZRegister.IX, "ix" -> ZRegister.IX,
"IY" -> ZRegister.IY, "iy" -> ZRegister.IY,
"SP" -> ZRegister.SP, "sp" -> ZRegister.SP,
)
private val toIndexHalf: Map[String, ZRegister.Value] = Map(
"IXH" -> ZRegister.IXH, "ixh" -> ZRegister.IXH,
"IXL" -> ZRegister.IXL, "ixl" -> ZRegister.IXL,
"IYH" -> ZRegister.IYH, "iyh" -> ZRegister.IYH,
"IYL" -> ZRegister.IYL, "iyl" -> ZRegister.IYL,
)
private val toIntelRegister8: Map[String, ZRegister.Value] = Map(
"A" -> ZRegister.A, "a" -> ZRegister.A,
"B" -> ZRegister.B, "b" -> ZRegister.B,
"C" -> ZRegister.C, "c" -> ZRegister.C,
"D" -> ZRegister.D, "d" -> ZRegister.D,
"E" -> ZRegister.E, "e" -> ZRegister.E,
"H" -> ZRegister.H, "h" -> ZRegister.H,
"L" -> ZRegister.L, "l" -> ZRegister.L,
"M" -> ZRegister.L, "m" -> ZRegister.MEM_HL,
)
private val toIntelRegister16: Map[String, ZRegister.Value] = Map(
"H" -> ZRegister.HL, "h" -> ZRegister.HL,
"HL" -> ZRegister.HL, "hl" -> ZRegister.HL,
"PSW" -> ZRegister.AF, "psw" -> ZRegister.AF,
"B" -> ZRegister.BC, "b" -> ZRegister.BC,
"BC" -> ZRegister.BC, "bc" -> ZRegister.BC,
"D" -> ZRegister.DE, "d" -> ZRegister.DE,
"DE" -> ZRegister.DE, "de" -> ZRegister.DE,
"SP" -> ZRegister.SP, "sp" -> ZRegister.SP,
"IX" -> ZRegister.IX, "ix" -> ZRegister.IX,
"IY" -> ZRegister.IY, "iy" -> ZRegister.IY,
)
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 (VariableExpression(r), false)
if options.flag(CompilationFlag.EmitZ80Opcodes) &&
(options.flag(CompilationFlag.EmitIllegals) || options.flag(CompilationFlag.EmitR800Opcodes)) &&
toIndexHalf.contains(r)=> (toIndexHalf(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)
case (FunctionCallExpression("IX" | "ix", List(o)), _) => (ZRegister.MEM_IX_D, Some(o))
case (FunctionCallExpression("IY" | "iy", List(o)), _) => (ZRegister.MEM_IY_D, Some(o))
case (e, true) if allowAbsolute => (ZRegister.MEM_ABS_8, Some(e))
case (e, _) => (ZRegister.IMM_8, Some(e))
}
private def intel8: P[ZRegister.Value] = asmExpression.map {
case VariableExpression(r) if toIntelRegister8.contains(r) => toIntelRegister8(r)
case x =>
options.log.error("Invalid operand", x.position)
ZRegister.A
}
private def intel16(allowPsw: Boolean): P[ZRegister.Value] = asmExpression.map {
case x@VariableExpression(r) if toIntelRegister16.contains(r) =>
val result = toIntelRegister16(r)
if (result == ZRegister.AF && !allowPsw) options.log.error("Invalid operand", x.position)
if (result == ZRegister.SP && allowPsw) options.log.error("Invalid operand", x.position)
result
case x =>
log.error("Invalid operand", x.position)
ZRegister.HL
}
def mapOne8Register(op: ZOpcode.Value)(param: (ZRegister.Value, Option[Expression])): (ZOpcode.Value, OneRegister, Option[Expression], Expression) = param match {
case (reg@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(e)) => (op, OneRegister(reg), Some(e), zero)
case (reg, addr) => (op, OneRegister(reg), None, addr.getOrElse(zero))
}
def one8Register(op: ZOpcode.Value): P[(ZOpcode.Value, OneRegister, Option[Expression], Expression)] = param(allowAbsolute = false).map{
case (reg@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(e)) => (op, OneRegister(reg), Some(e), zero)
case (reg, addr) => (op, OneRegister(reg), None, addr.getOrElse(zero))
}
def one8RegisterIntel(op: ZOpcode.Value): P[(ZOpcode.Value, OneRegister, Option[Expression], Expression)] = intel8.map(reg => (op, OneRegister(reg), None, zero))
def one8RegisterOr8085Illegal(op: ZOpcode.Value, illegalTarget: ZRegister.Value, illegalOp: ZOpcode.Value): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] = param(allowAbsolute = false).map{
case (reg@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(e)) => (op, OneRegister(reg), Some(e), zero)
case (reg, _) if reg == illegalTarget && options.flag(CompilationFlag.EmitIntel8085Opcodes) && options.flag(CompilationFlag.EmitIllegals) =>
(illegalOp, NoRegisters, None, zero)
case (reg, _) if reg == illegalTarget =>
log.error("This instruction requires support for undocumented 8085 instructions: " + ZLine.register(op, reg))
(op, OneRegister(ZRegister.A), None, zero)
case (reg, addr) => (op, OneRegister(reg), None, addr.getOrElse(zero))
}
def one8Or16Register(op8: ZOpcode.Value, op16: ZOpcode.Value): P[(ZOpcode.Value, OneRegister, Option[Expression], Expression)] = param(allowAbsolute = false).map{
case (reg@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(e)) => (op8, OneRegister(reg), Some(e), zero)
case (reg@(ZRegister.HL | ZRegister.DE | ZRegister.AF | ZRegister.SP | ZRegister.BC | ZRegister.IX | ZRegister.IY), addr) => (op16, OneRegister(reg), None, addr.getOrElse(zero))
case (reg, addr) => (op8, OneRegister(reg), None, addr.getOrElse(zero))
}
def one16Register(op: ZOpcode.Value): P[(ZOpcode.Value, OneRegister, Option[Expression], Expression)] = param(allowAbsolute = false).map{
case (reg@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(e)) => (op, OneRegister(reg), Some(e), zero)
case (ZRegister.MEM_ABS_8, addr) => (op, OneRegister(ZRegister.MEM_ABS_16), None, addr.getOrElse(zero))
case (ZRegister.IMM_8, addr) => (op, OneRegister(ZRegister.IMM_16), None, addr.getOrElse(zero))
case (reg, addr) => (op, OneRegister(reg), None, addr.getOrElse(zero))
}
private val jumpCondition: P[ZRegisters] = (HWS ~ (
if (options.flag(CompilationFlag.EmitIllegals) && options.flag(CompilationFlag.EmitIntel8085Opcodes))
"NZ" | "nz" | "nc" | "NC" | "NV" | "nv" |
"PO" | "po" | "PE" | "pe" |
"m" | "M" | "p" | "P" |
"c" | "C" | "Z" | "z" | "V" | "v" |
"k" | "K" | "nk" | "NK" |
"x5" | "X5" | "nx5" | "NX5"
else
"NZ" | "nz" | "nc" | "NC" | "NV" | "nv" |
"PO" | "po" | "PE" | "pe" |
"m" | "M" | "p" | "P" |
"c" | "C" | "Z" | "z" | "V" | "v"
).! ~ HWS).map {
case "Z" | "z" => IfFlagSet(ZFlag.Z)
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" | "NV" | "nv" => IfFlagClear(ZFlag.P)
case "NC" | "nc" => IfFlagClear(ZFlag.C)
case "P" | "p" => IfFlagClear(ZFlag.S)
case "K" | "k" | "X5" | "x5" => IfFlagSet(ZFlag.K)
case "NK" | "nk" |"NX5" | "nx5" => IfFlagClear(ZFlag.K)
case _ => NoRegisters // shouldn't happen
}
private def is16Bit(r: ZRegister.Value): Boolean = r match {
case ZRegister.HL => true
case ZRegister.BC => true
case ZRegister.DE => true
case ZRegister.IX => true
case ZRegister.IY => true
case ZRegister.SP => true
case ZRegister.AF => true
case ZRegister.MEM_ABS_16 => true
case ZRegister.IMM_16 => true
case _ => false
}
private def merge(op8: ZOpcode.Value, op16: ZOpcode.Value, skipTargetA: Boolean)
(params: (ZRegister.Value, Option[Expression], ZRegister.Value, Option[Expression]))
: (ZOpcode.Value, ZRegisters, Option[Expression], Expression) = params match {
case (ZRegister.A, _, s, e) if skipTargetA => mapOne8Register(op8)(s -> e)
case (tr@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(o), sr@(ZRegister.MEM_ABS_8 | ZRegister.IMM_8), Some(v)) =>
(op8, TwoRegisters(tr, sr), Some(o), v)
case (tr@(ZRegister.MEM_ABS_8 | ZRegister.IMM_8), Some(v), sr@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(o)) =>
(op8, TwoRegisters(tr, sr), Some(o), v)
case (tr@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(o), sr, _) =>
(op8, TwoRegisters(tr, sr), Some(o), zero)
case (tr, _, sr@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(o)) =>
(op8, TwoRegisters(tr, sr), Some(o), zero)
case (tr, _, ZRegister.MEM_ABS_8, Some(v)) if is16Bit(tr) =>
(op16, TwoRegisters(tr, ZRegister.MEM_ABS_16), None, v)
case (tr, _, ZRegister.IMM_8, Some(v)) if is16Bit(tr) =>
(op16, TwoRegisters(tr, ZRegister.IMM_16), None, v)
case (ZRegister.MEM_ABS_8, Some(v), sr, _) if is16Bit(sr) =>
(op16, TwoRegisters(ZRegister.MEM_ABS_16, sr), None, v)
case (ZRegister.IMM_8, Some(v), sr, _) if is16Bit(sr) =>
(op16, TwoRegisters(ZRegister.IMM_16, sr), None, v)
case (t, Some(v), s, None) =>
(op8, TwoRegisters(t, s), None, v)
case (t, None, s, Some(v)) =>
(op8, TwoRegisters(t, s), None, v)
case (t, None, s, None) =>
(if (is16Bit(t)) op16 else op8, TwoRegisters(t, s), None, zero)
case _ => ???
}
private val jumpConditionWithComma: P[ZRegisters] = (jumpCondition ~ "," ~/ HWS).?.map (_.getOrElse(NoRegisters))
private val jumpConditionWithoutComma: P[ZRegisters] = (jumpCondition ~/ HWS).?.map (_.getOrElse(NoRegisters))
private val indexHalves: Set[ZRegister.Value] = {
import ZRegister._
Set(IXH, IXL, IYH, IYL)
}
private val MEM_R: Set[ZRegister.Value] = {
import ZRegister._
Set(MEM_HL,MEM_IX_D,MEM_IY_D)
}
def invalidIllegalRegisters(r1: ZRegister.Value, r2: ZRegister.Value): Boolean = {
import ZRegister._
if (MEM_R(r1) && MEM_R(r2)) return true
if (!indexHalves(r1) && !indexHalves(r2)) return false
if (!options.flag(CompilationFlag.EmitIllegals) && !options.flag(CompilationFlag.EmitZ80Opcodes) && (indexHalves(r1) || indexHalves(r2))) return true
if ((r1 == H || r1 == L || r1 == MEM_IX_D || r1 == MEM_IY_D) && indexHalves(r2)) return true
if ((r2 == H || r2 == L || r2 == MEM_IX_D || r2 == MEM_IY_D) && indexHalves(r1)) return true
if ((r1 == IYH || r1 == IYL) && (r2 == IXH || r2 == IXL)) return true
if ((r1 == IXH || r1 == IXL) && (r2 == IYH || r2 == IYL)) return true
false // TODO
}
val zilogAsmInstruction: P[ExecutableStatement] = {
import ZOpcode._
for {
el <- elidable
pos <- position("assembly statement")
opcode: String <- identifier ~/ HWS
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 "HLT" => imm(HALT)
case "HALT" => imm(HALT)
case "STOP" => imm(STOP)
case "RIM" => imm(RIM)
case "SIM" => imm(SIM)
case "RETN" => imm(RETN)
case "RETI" => imm(RETI)
case "RET" => P(jumpConditionWithoutComma).map((RET, _, None, zero))
case "CALL" => (jumpConditionWithComma ~ asmExpression).map { case (reg, param) => (CALL, reg, None, param) }
case "JP" => (jumpConditionWithComma ~ param(allowAbsolute = true)).map {
case (NoRegisters, (ZRegister.MEM_ABS_8, Some(VariableExpression("ix" | "IX")))) =>
(JP, OneRegister(ZRegister.IX), None, zero)
case (NoRegisters, (ZRegister.MEM_ABS_8, Some(VariableExpression("iy" | "IY")))) =>
(JP, OneRegister(ZRegister.IY), None, zero)
case (NoRegisters, (ZRegister.MEM_HL, _)) =>
(JP, OneRegister(ZRegister.HL), None, zero)
case (cond, (ZRegister.MEM_ABS_8 | ZRegister.IMM_8, Some(param))) =>
(JP, cond, None, param)
case _ =>
log.error("Invalid parameters for JP", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "JR" => (jumpConditionWithComma ~ asmExpression).map{case (reg, param) => (JR, reg, None, param)}
case "DJNZ" => asmExpression.map((DJNZ, NoRegisters, None, _))
case "CP" => one8Register(CP)
case "AND" => one8Register(AND)
case "OR" => one8Register(OR)
case "XOR" => one8Register(XOR)
case "SUB" => one8Register(SUB)
case "DEC" => one8Or16Register(DEC, DEC_16)
case "INC" => one8Or16Register(INC, INC_16)
case "RLA" => imm(RLA)
case "RRA" => imm(RRA)
case "RLCA" => imm(RLCA)
case "RRCA" => imm(RRCA)
case "RL" => one8RegisterOr8085Illegal(RL, ZRegister.DE, RLDE)
case "RR" => one8Register(RR)
case "RLC" => one8Register(RLC)
case "RRC" => one8Register(RRC)
case "SLA" => one8Register(SLA)
case "SLL" | "SLS" => one8Register(SLL)
case "SRA" => one8RegisterOr8085Illegal(SRA, ZRegister.HL, RRHL)
case "SRL" => one8Register(SRL)
case "SWAP" => one8Register(SWAP)
case "BIT" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), (r2, e2))
if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE =>
(ZOpcodeClasses.BIT_seq(n.toInt), OneRegister(r2), e2, zero)
case _ =>
log.error("Invalid parameters for BIT", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "SET" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), (r2, e2))
if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE =>
(ZOpcodeClasses.SET_seq(n.toInt), OneRegister(r2), e2, zero)
case _ =>
log.error("Invalid parameters for SET", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "RES" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), (r2, e2))
if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE =>
(ZOpcodeClasses.RES_seq(n.toInt), OneRegister(r2), e2, zero)
case _ =>
log.error("Invalid parameters for RES", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "SCF" => imm(SCF)
case "CCF" => imm(CCF)
case "CPL" => imm(CPL)
case "DAA" => imm(DAA)
case "EXX" => imm(EXX)
case "NOP" => imm(NOP)
case "NEG" => imm(NEG)
case "LDI" => imm(LDI) // TODO: Gameboy has a different LDI
case "LDD" => imm(LDD) // TODO: Gameboy has a different LDD
case "LDIR" => imm(LDIR)
case "LDDR" => imm(LDDR)
case "CPI" => imm(CPI)
case "CPD" => imm(CPD)
case "CPIR" => imm(CPIR)
case "CPDR" => imm(CPDR)
case "INI" => imm(INI)
case "IND" => imm(IND)
case "INIR" => imm(INIR)
case "INDR" => imm(INDR)
case "OUTI" => imm(OUTI)
case "OUTD" => imm(OUTD)
case "OUTIR" => imm(OUTIR)
case "OUTDR" => imm(OUTDR)
case "OTIR" => imm(OUTIR)
case "OTDR" => imm(OUTDR)
case "RLD" => imm(RLD)
case "RRD" => imm(RRD)
case "PUSH" => one16Register(PUSH)
case "POP" => one16Register(POP)
case "IN" => (asmExpressionWithParens ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpressionWithParens).map { p: (Expression, Boolean, (Expression, Boolean)) =>
p match {
case (VariableExpression(r), false, (VariableExpression("C" | "c"), true))
if toRegister.contains(r) =>
(IN_C, OneRegister(toRegister(r)), None, zero)
case (VariableExpression(r), false, (port, true))
if toRegister.contains(r) =>
(IN_IMM, OneRegister(toRegister(r)), None, port)
case _ =>
log.error("Invalid parameters for IN", Some(pos))
(NOP, NoRegisters, None, zero)
}
}
case "OUT" => (asmExpressionWithParens ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpressionWithParens).map { p: (Expression, Boolean, (Expression, Boolean)) =>
p match {
case (VariableExpression("C" | "c"), true, (VariableExpression(r), false))
if toRegister.contains(r) =>
(OUT_C, OneRegister(toRegister(r)), None, zero)
case (port, true, (VariableExpression(r), false))
if toRegister.contains(r) =>
(OUT_IMM, OneRegister(toRegister(r)), None, port)
case _ =>
log.error("Invalid parameters for OUT", Some(pos))
(NOP, NoRegisters, None, zero)
}
}
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"), _)) =>
(EX_AF_AF, NoRegisters, None, zero)
case (VariableExpression("DE" | "de"), false, (VariableExpression("HL" | "hl"), false)) =>
(EX_DE_HL, NoRegisters, None, zero)
case (VariableExpression("HL" | "hl"), false, (VariableExpression("DE" | "de"), false)) =>
(EX_DE_HL, NoRegisters, None, zero)
case (VariableExpression("SP" | "sp"), true, (VariableExpression(r), false))
if toRegister.contains(r) =>
(EX_SP, OneRegister(toRegister(r)), None, zero)
case (VariableExpression(r), false, (VariableExpression("SP" | "sp"), true))
if toRegister.contains(r) =>
(EX_SP, OneRegister(toRegister(r)), None, zero)
case _ =>
log.error("Invalid parameters for EX", Some(pos))
(NOP, NoRegisters, None, zero)
}
}
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.DE, None, (ZRegister.IMM_8 | ZRegister.IMM_16, Some(SumExpression((false, VariableExpression("sp" | "SP")) :: offset, false))))
if options.flags(CompilationFlag.EmitIntel8085Opcodes) && options.flags(CompilationFlag.EmitIllegals) =>
(LD_DESP, 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.DE, None, (ZRegister.IMM_8 | ZRegister.IMM_16, Some(SumExpression((false, VariableExpression("hl" | "HL")) :: offset, false))))
if options.flags(CompilationFlag.EmitIntel8085Opcodes) && options.flags(CompilationFlag.EmitIllegals) =>
(LD_DEHL, 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.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.MEM_DE, None, (ZRegister.HL, None))
if options.flags(CompilationFlag.EmitIntel8085Opcodes) && options.flags(CompilationFlag.EmitIllegals) =>
(SHLX, NoRegisters, None, zero)
case (ZRegister.HL, None, (ZRegister.MEM_DE, None))
if options.flags(CompilationFlag.EmitIntel8085Opcodes) && options.flags(CompilationFlag.EmitIllegals) =>
(SHLX, NoRegisters, None, zero)
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)) =>
if (invalidIllegalRegisters(r1,r2)) {
log.error("Invalid parameters for LD", Some(pos))
}
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))
}
case "ADC" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (r1, e1, (r2, e2)) => merge(ADC, ADC_16, skipTargetA = true)((r1, e1, r2, e2))
}
case "SBC" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
case (r1, e1, (r2, e2)) => merge(SBC, SBC_16, skipTargetA = true)((r1, e1, r2, e2))
}
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 { _ => (MIRROR, 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 "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 _ =>
log.error("Unsupported opcode " + opcode, Some(pos))
imm(NOP)
}
} yield {
val (actualOpcode, registers, offset, param) = tuple4
Z80AssemblyStatement(actualOpcode, registers, offset, param, el).pos(pos)
}
}
val intelAsmInstruction: P[ExecutableStatement] = {
import ZOpcode._
import ZRegister._
for {
el <- elidable
pos <- position()
opcode: String <- identifier ~/ HWS
tuple4: (ZOpcode.Value, ZRegisters, Option[Expression], Expression) <- opcode.toUpperCase(Locale.ROOT) match {
case "NOP" => imm(NOP)
case "RLC" => imm(RLCA)
case "RRC" => imm(RRCA)
case "RAL" => imm(RLA)
case "RAR" => imm(RRA)
case "DAA" => imm(DAA)
case "CMA" => imm(CPL)
case "STC" => imm(SCF)
case "CMC" => imm(CCF)
case "RIM" => imm(RIM)
case "SIM" => imm(SIM)
case "EXAF" => imm(EX_AF_AF)
case "EXX" => imm(EXX)
case "NEG" => imm(NEG)
case "IM0" => P("").map(_ => (IM, NoRegisters, None, LiteralExpression(0, 1)))
case "IM1" => P("").map(_ => (IM, NoRegisters, None, LiteralExpression(1, 1)))
case "IM2" => P("").map(_ => (IM, NoRegisters, None, LiteralExpression(2, 1)))
case "RETI" => imm(RETI)
case "RETN" => imm(RETN)
case "RLD" => imm(RLD)
case "RRD" => imm(RRD)
case "INI" => imm(INI)
case "INIR" => imm(INIR)
case "IND" => imm(IND)
case "INDR" => imm(INDR)
case "OUTI" => imm(OUTI)
case "OUTIR" => imm(OUTIR)
case "OUTD" => imm(OUTD)
case "OUTDR" => imm(OUTDR)
case "LDI" => imm(LDI)
case "LDIR" => imm(LDIR)
case "LDD" => imm(LDD)
case "LDDR" => imm(LDDR)
case "CCI" => imm(CPI)
case "CCIR" => imm(CPIR)
case "CCD" => imm(CPD)
case "CCDR" => imm(CPDR)
case "RALR" => one8RegisterIntel(RL)
case "RARR" => one8RegisterIntel(RR)
case "RLCR" => one8RegisterIntel(RLC)
case "RRCR" => one8RegisterIntel(RRC)
case "SLAR" => one8RegisterIntel(SLA)
case "SLLR" => one8RegisterIntel(SLL)
case "SRAR" => one8RegisterIntel(SRA)
case "SRLR" => one8RegisterIntel(SRL)
case "RALX" => asmExpression.map(d => (RL, OneRegister(MEM_IX_D), Some(d), zero))
case "RARX" => asmExpression.map(d => (RR, OneRegister(MEM_IX_D), Some(d), zero))
case "RLCX" => asmExpression.map(d => (RLC, OneRegister(MEM_IX_D), Some(d), zero))
case "RRCX" => asmExpression.map(d => (RRC, OneRegister(MEM_IX_D), Some(d), zero))
case "SLAX" => asmExpression.map(d => (SLA, OneRegister(MEM_IX_D), Some(d), zero))
case "SLLX" => asmExpression.map(d => (SLL, OneRegister(MEM_IX_D), Some(d), zero))
case "SRAX" => asmExpression.map(d => (SRA, OneRegister(MEM_IX_D), Some(d), zero))
case "SRLX" => asmExpression.map(d => (SRL, OneRegister(MEM_IX_D), Some(d), zero))
case "RALY" => asmExpression.map(d => (RL, OneRegister(MEM_IY_D), Some(d), zero))
case "RARY" => asmExpression.map(d => (RR, OneRegister(MEM_IY_D), Some(d), zero))
case "RLCY" => asmExpression.map(d => (RLC, OneRegister(MEM_IY_D), Some(d), zero))
case "RRCY" => asmExpression.map(d => (RRC, OneRegister(MEM_IY_D), Some(d), zero))
case "SLAY" => asmExpression.map(d => (SLA, OneRegister(MEM_IY_D), Some(d), zero))
case "SLLY" => asmExpression.map(d => (SLL, OneRegister(MEM_IY_D), Some(d), zero))
case "SRAY" => asmExpression.map(d => (SRA, OneRegister(MEM_IY_D), Some(d), zero))
case "SRLY" => asmExpression.map(d => (SRL, OneRegister(MEM_IY_D), Some(d), zero))
case "HLT" => imm(HALT)
case "EI" => imm(EI)
case "DI" => imm(DI)
case "XCHG" => imm(EX_DE_HL)
case "XTHL" => P("").map(_ => (EX_SP, OneRegister(HL), None, zero))
case "XTIX" => P("").map(_ => (EX_SP, OneRegister(IX), None, zero))
case "XTIY" => P("").map(_ => (EX_SP, OneRegister(IY), None, zero))
case "SPHL" => P("").map(_ => (LD_16, TwoRegisters(SP, HL), None, zero))
case "SPIX" => P("").map(_ => (LD_16, TwoRegisters(SP, IX), None, zero))
case "SPIY" => P("").map(_ => (LD_16, TwoRegisters(SP, IY), None, zero))
case "PCHL" => P("").map(_ => (JP, z80.OneRegister(HL), None, zero))
case "PCIX" => P("").map(_ => (JP, z80.OneRegister(IX), None, zero))
case "PCIY" => P("").map(_ => (JP, z80.OneRegister(IY), None, zero))
case "MOV" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ intel8).map {
case (r1, r2) => (LD, TwoRegisters(r1, r2), None, zero)
}
case "LXI" => (intel16(false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, e) => (LD_16, TwoRegisters(r, IMM_16), None, e)
}
case "LXIX" => asmExpression.map {
case (e) => (LD_16, TwoRegisters(IX, IMM_16), None, e)
}
case "LXIY" => asmExpression.map {
case (e) => (LD_16, TwoRegisters(IY, IMM_16), None, e)
}
case "MVI" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, e) => (LD, TwoRegisters(r, IMM_8), None, e)
}
case "MVIX" => (asmExpression ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (e, d) => (LD, TwoRegisters(MEM_IX_D, IMM_8), Some(d), e)
}
case "MVIY" => (asmExpression ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (e, d) => (LD, TwoRegisters(MEM_IY_D, IMM_8), Some(d), e)
}
case "LDX" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(r, MEM_IX_D), Some(d), zero)
}
case "LDY" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(r, MEM_IY_D), Some(d), zero)
}
case "STX" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(MEM_IX_D, r), Some(d), zero)
}
case "STY" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(MEM_IY_D, r), Some(d), zero)
}
case "DAD" => intel16(false).map { r => (ADD_16, TwoRegisters(HL, r), None, zero)}
case "DADC" => intel16(false).map { r => (ADC_16, TwoRegisters(HL, r), None, zero)}
case "DSBC" => intel16(false).map { r => (SBC_16, TwoRegisters(HL, r), None, zero)}
case "DADX" => intel16(false).map { r => (ADD_16, TwoRegisters(IX, r), None, zero)}
case "DADY" => intel16(false).map { r => (ADD_16, TwoRegisters(IY, r), None, zero)}
case "STAX" => intel16(false).map {
case r@(ZRegister.BC | ZRegister.DE) => (LD, TwoRegisters(r, A), None, zero)
case _ =>
log.error("Invalid parameters for STAX", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "LDAX" => intel16(false).map {
case r@(ZRegister.BC | ZRegister.DE) => (LD, TwoRegisters(A, r), None, zero)
case _ =>
log.error("Invalid parameter for STAX", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "INX" => intel16(false).map { r => (INC_16, OneRegister(r), None, zero)}
case "INXIX" => P("").map(_ => (INC_16, OneRegister(IX), None, zero))
case "INXIY" => P("").map(_ => (INC_16, OneRegister(IY), None, zero))
case "DCX" => intel16(false).map { r => (DEC_16, OneRegister(r), None, zero)}
case "DCXIX" => P("").map(_ => (DEC_16, OneRegister(IX), None, zero))
case "DCXIY" => P("").map(_ => (DEC_16, OneRegister(IY), None, zero))
case "POP" => intel16(true).map { r => (POP, OneRegister(r), None, zero)}
case "POPIX" => P("").map(_ => (POP, OneRegister(IX), None, zero))
case "POPIY" => P("").map(_ => (POP, OneRegister(IY), None, zero))
case "PUSH" => intel16(true).map { r => (PUSH, OneRegister(r), None, zero)}
case "PUSHIX" => P("").map(_ => (PUSH, OneRegister(IX), None, zero))
case "PUSHIY" => P("").map(_ => (PUSH, OneRegister(IY), None, zero))
case "INR" => intel8.map { r => (INC, OneRegister(r), None, zero)}
case "INRX" => asmExpression.map { d => (INC, OneRegister(MEM_IX_D), Some(d), zero)}
case "INRY" => asmExpression.map { d => (INC, OneRegister(MEM_IY_D), Some(d), zero)}
case "DCR" => intel8.map { r => (DEC, OneRegister(r), None, zero)}
case "DCRX" => asmExpression.map { d => (DEC, OneRegister(MEM_IX_D), Some(d), zero)}
case "DCRY" => asmExpression.map { d => (DEC, OneRegister(MEM_IY_D), Some(d), zero)}
case "ADD" => intel8.map { r => (ADD, OneRegister(r), None, zero)}
case "ADDX" => asmExpression.map { d => (ADD, OneRegister(MEM_IX_D), Some(d), zero)}
case "ADDY" => asmExpression.map { d => (ADD, OneRegister(MEM_IY_D), Some(d), zero)}
case "SUB" => intel8.map { r => (SUB, OneRegister(r), None, zero)}
case "SUBX" => asmExpression.map { d => (SUB, OneRegister(MEM_IX_D), Some(d), zero)}
case "SUBY" => asmExpression.map { d => (SUB, OneRegister(MEM_IY_D), Some(d), zero)}
case "ADC" => intel8.map { r => (ADC, OneRegister(r), None, zero)}
case "ADCX" => asmExpression.map { d => (ADC, OneRegister(MEM_IX_D), Some(d), zero)}
case "ADCY" => asmExpression.map { d => (ADC, OneRegister(MEM_IY_D), Some(d), zero)}
case "SBB" => intel8.map { r => (SBC, OneRegister(r), None, zero)}
case "SBCX" => asmExpression.map { d => (SBC, OneRegister(MEM_IX_D), Some(d), zero)}
case "SBCY" => asmExpression.map { d => (SBC, OneRegister(MEM_IY_D), Some(d), zero)}
case "ORA" => intel8.map { r => (OR, OneRegister(r), None, zero)}
case "ORX" => asmExpression.map { d => (OR, OneRegister(MEM_IX_D), Some(d), zero)}
case "ORY" => asmExpression.map { d => (OR, OneRegister(MEM_IY_D), Some(d), zero)}
case "ANA" => intel8.map { r => (AND, OneRegister(r), None, zero)}
case "ANDX" => asmExpression.map { d => (AND, OneRegister(MEM_IX_D), Some(d), zero)}
case "ANDY" => asmExpression.map { d => (AND, OneRegister(MEM_IY_D), Some(d), zero)}
case "XRA" => intel8.map { r => (XOR, OneRegister(r), None, zero)}
case "XORX" => asmExpression.map { d => (XOR, OneRegister(MEM_IX_D), Some(d), zero)}
case "XORY" => asmExpression.map { d => (XOR, OneRegister(MEM_IY_D), Some(d), zero)}
case "CMP" => intel8.map { r => (CP, OneRegister(r), None, zero)}
case "CMPX" => asmExpression.map { d => (CP, OneRegister(MEM_IX_D), Some(d), zero)}
case "CMPY" => asmExpression.map { d => (CP, OneRegister(MEM_IY_D), Some(d), zero)}
case "ADI" => asmExpression.map { e => (ADD, OneRegister(IMM_8), None, e)}
case "ACI" => asmExpression.map { e => (ADC, OneRegister(IMM_8), None, e)}
case "SUI" => asmExpression.map { e => (SUB, OneRegister(IMM_8), None, e)}
case "SBI" => asmExpression.map { e => (SBC, OneRegister(IMM_8), None, e)}
case "ANI" => asmExpression.map { e => (AND, OneRegister(IMM_8), None, e)}
case "ORI" => asmExpression.map { e => (OR, OneRegister(IMM_8), None, e)}
case "XRI" => asmExpression.map { e => (XOR, OneRegister(IMM_8), None, e)}
case "CPI" => asmExpression.map { e => (CP, OneRegister(IMM_8), None, e)}
case "SHLD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, HL), None, e)}
case "LHLD" => asmExpression.map { e => (LD_16, TwoRegisters(HL, MEM_ABS_16), None, e)}
case "LBCD" => asmExpression.map { e => (LD_16, TwoRegisters(BC, MEM_ABS_16), None, e)}
case "SBCD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, BC), None, e)}
case "LDED" => asmExpression.map { e => (LD_16, TwoRegisters(DE, MEM_ABS_16), None, e)}
case "SDED" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, DE), None, e)}
case "LSPD" => asmExpression.map { e => (LD_16, TwoRegisters(SP, MEM_ABS_16), None, e)}
case "SSPD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, SP), None, e)}
case "LIXD" => asmExpression.map { e => (LD_16, TwoRegisters(IX, MEM_ABS_16), None, e)}
case "SIXD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, IX), None, e)}
case "LIYD" => asmExpression.map { e => (LD_16, TwoRegisters(IY, MEM_ABS_16), None, e)}
case "SIYD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, IY), None, e)}
case "STA" => asmExpression.map { e => (LD, TwoRegisters(MEM_ABS_8, A), None, e)}
case "LDA" => asmExpression.map { e => (LD, TwoRegisters(A, MEM_ABS_8), None, e)}
case "LDAI" => P("").map(_ => (LD, TwoRegisters(A, I), None, zero))
case "LDAR" => P("").map(_ => (LD, TwoRegisters(A, R), None, zero))
case "STAI" => P("").map(_ => (LD, TwoRegisters(I, A), None, zero))
case "STAR" => P("").map(_ => (LD, TwoRegisters(R, A), None, zero))
case "RST" => asmExpression.map {
case LiteralExpression(value, _) if value >=0 && value <= 7=> (RST, NoRegisters, None, LiteralExpression(value * 8, 1))
case _ =>
log.error("Invalid parameter for RST", Some(pos))
(NOP, NoRegisters, None, zero)
}
case "INP" => intel8.map { r => (IN_C, OneRegister(r), None, zero) }
case "OUTP" => intel8.map { r => (OUT_C, OneRegister(r), None, zero) }
case "IN" => asmExpression.map { e => (IN_IMM, OneRegister(A), None, e) }
case "OUT" => asmExpression.map { e => (OUT_IMM, OneRegister(A), None, e) }
case "DJNZ" => asmExpression.map { e => (DJNZ, NoRegisters, None, e)}
case "JMP" => asmExpression.map { e => (JP, NoRegisters, None, e)}
case "JMPR" | "JR" => asmExpression.map { e => (JR, NoRegisters, None, e)}
case "JC" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.C), None, e)}
case "JZ" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.Z), None, e)}
case "JM" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.S), None, e)}
case "JPE" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.P), None, e)}
case "JNC" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.C), None, e)}
case "JNZ" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.Z), None, e)}
case "JP" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.S), None, e)}
case "JPO" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.P), None, e)}
case "JRC" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.C), None, e)}
case "JRZ" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.Z), None, e)}
case "JRM" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.S), None, e)}
case "JRPE" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.P), None, e)}
case "JRNC" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.C), None, e)}
case "JRNZ" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.Z), None, e)}
case "JRP" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.S), None, e)}
case "JRPO" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.P), None, e)}
case "RET" => imm(RET)
case "RC" => P("").map { _ => (RET, IfFlagSet(ZFlag.C), None, zero)}
case "RZ" => P("").map { _ => (RET, IfFlagSet(ZFlag.Z), None, zero)}
case "RM" => P("").map { _ => (RET, IfFlagSet(ZFlag.S), None, zero)}
case "RPE" => P("").map { _ => (RET, IfFlagSet(ZFlag.P), None, zero)}
case "RNC" => P("").map { _ => (RET, IfFlagClear(ZFlag.C), None, zero)}
case "RNZ" => P("").map { _ => (RET, IfFlagClear(ZFlag.Z), None, zero)}
case "RP" => P("").map { _ => (RET, IfFlagClear(ZFlag.S), None, zero)}
case "RPO" => P("").map { _ => (RET, IfFlagClear(ZFlag.P), None, zero)}
case "CALL" => asmExpression.map { e => (CALL, NoRegisters, None, e)}
case "CC" => asmExpression.map { e => (CALL, IfFlagSet(ZFlag.C), None, e)}
case "CZ" => asmExpression.map { e => (CALL, IfFlagSet(ZFlag.Z), None, e)}
case "CM" => asmExpression.map { e => (CALL, IfFlagSet(ZFlag.S), None, e)}
case "CPE" => asmExpression.map { e => (CALL, IfFlagSet(ZFlag.P), None, e)}
case "CNC" => asmExpression.map { e => (CALL, IfFlagClear(ZFlag.C), None, e)}
case "CNZ" => asmExpression.map { e => (CALL, IfFlagClear(ZFlag.Z), None, e)}
case "CP" => asmExpression.map { e => (CALL, IfFlagClear(ZFlag.S), None, e)}
case "CPO" => asmExpression.map { e => (CALL, IfFlagClear(ZFlag.P), None, e)}
case "DSUB" => imm(DSUB)
case "JNK" | "JNX5" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.K), None, e)}
case "JK" | "JX5" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.K), None, e)}
case "RRHL" | "ARHL" => imm(RRHL)
case "RLDE" | "RDEL" => imm(RLDE)
case "RSTV" | "OVRST8" => imm(RSTV)
case "LDSI" => asmExpression.map { e => (LD_DESP, NoRegisters, None, e)}
case "LDHI" => asmExpression.map { e => (LD_DEHL, NoRegisters, None, e)}
case "SHLDE" | "SHLX" => imm(SHLX)
case "LHLDE" | "LHLX" =>imm(LHLX)
case "RES" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.RES_seq, "RES", pos)
case "SETB" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.SET_seq, "SETB", pos)
case "SET" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.SET_seq, "SET", pos)
case "BIT" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.BIT_seq, "BIT", pos)
case "RESX" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.RES_seq, MEM_IX_D, "RESX", pos)
case "SETX" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.SET_seq, MEM_IX_D, "SETX", pos)
case "BITX" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.BIT_seq, MEM_IX_D, "BITX", pos)
case "RESY" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.RES_seq, MEM_IY_D, "RESY", pos)
case "SETY" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.SET_seq, MEM_IY_D, "SETY", pos)
case "BITY" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.BIT_seq, MEM_IY_D, "BITY", pos)
case _ =>
log.error("Unsupported opcode " + opcode, Some(pos))
imm(NOP)
}
} yield {
val (actualOpcode, registers, offset, param) = tuple4
Z80AssemblyStatement(actualOpcode, registers, offset, param, el)
}
}
private def parseSingleBitOpWithIntelSyntax(OP_seq: IndexedSeq[ZOpcode.Value], opcodeString: String, pos: Position): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] = {
import ZOpcode.NOP
(param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ intel8).map {
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), r2)
if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE =>
(OP_seq(n.toInt), OneRegister(r2), None, zero)
case _ =>
log.error("Invalid parameters for " + opcodeString, Some(pos))
(NOP, NoRegisters, None, zero)
}
}
private def parseSingleBitIndexedOpWithIntelSyntax(OP_seq: IndexedSeq[ZOpcode.Value], memIndex: ZRegister.Value, opcodeString: String, pos: Position): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] = {
import ZOpcode.NOP
(param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), e2)
if n >= 0 && n <= 7 =>
(OP_seq(n.toInt), OneRegister(memIndex), Some(e2), zero)
case _ =>
log.error("Invalid parameters for " + opcodeString, Some(pos))
(NOP, NoRegisters, None, zero)
}
}
val asmInstruction: P[ExecutableStatement] = if (useIntelSyntax) intelAsmInstruction else zilogAsmInstruction
private def imm(opcode: ZOpcode.Value): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] =
P("").map(_=>(opcode, NoRegisters, None, zero))
private def regA(opcode: ZOpcode.Value): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] =
P("").map(_=>(opcode, OneRegister(ZRegister.A), None, zero))
override val asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros
override def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]]): Unit = {
if (!options.flag(CompilationFlag.BuggyCodeWarning)) return
statements match {
case Some(Nil) => log.warn("Assembly function `$name` is empty, did you mean RET, RETI, RETN or JP", Some(p))
case Some(xs) =>
if (flags("interrupt")) {
if (xs.exists {
case Z80AssemblyStatement(ZOpcode.RET, _, _, _, _) => true
case _ => false
}) log.warn("Assembly interrupt function `$name` contains RET, did you mean RETI/RETN?", Some(p))
} else {
if (xs.exists {
case Z80AssemblyStatement(ZOpcode.RETI, _, _, _, _) => true
case Z80AssemblyStatement(ZOpcode.RETN, _, _, _, _) => true
case _ => false
}) log.warn("Assembly non-interrupt function `$name` contains RETI or RETN, did you mean RET?", Some(p))
}
if (!name.startsWith("__") && !flags("macro")) {
xs.last match {
case Z80AssemblyStatement(ZOpcode.RET, NoRegisters, _, _, _) => () // OK
case Z80AssemblyStatement(ZOpcode.RETN, NoRegisters, _, _, _) => () // OK
case Z80AssemblyStatement(ZOpcode.RETI, NoRegisters, _, _, _) => () // OK
case Z80AssemblyStatement(ZOpcode.JP, NoRegisters, _, _, _) => () // OK
case Z80AssemblyStatement(ZOpcode.JR, NoRegisters, _, _, _) => () // OK
case _ =>
val validReturn = if (flags("interrupt")) "RETI/RETN" else "RET"
log.warn(s"Non-macro assembly function `$name` should end in " + validReturn, Some(p))
}
}
case None => ()
}
}
}