1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-07-13 15:29:01 +00:00

Initial and very incomplete support for Z80 assembly

This commit is contained in:
Karol Stasiak 2018-07-05 00:49:51 +02:00
parent c153588600
commit fe85757e00
7 changed files with 147 additions and 21 deletions

View File

@ -425,7 +425,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case _ => ()
}
val result = function.params match {
case AssemblyParamSignature(List(AssemblyParam(typ1, ZRegisterVariable(ZRegister.A, typ2), AssemblyParameterPassingBehaviour.Copy)))
if typ1.size == 1 && typ2.size == 1 =>
compileToA(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress)
case AssemblyParamSignature(List(AssemblyParam(typ1, ZRegisterVariable(ZRegister.HL, typ2), AssemblyParameterPassingBehaviour.Copy)))
if typ1.size == 2 && typ2.size == 2 =>
compileToHL(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress)
case AssemblyParamSignature(paramConvs) =>
// TODO: stop being lazy and implement this
???
case NormalParamSignature(paramVars) =>
params.zip(paramVars).flatMap {

View File

@ -599,6 +599,8 @@ class Environment(val parent: Option[Environment], val prefix: String) {
AssemblyParam(typ, env.get[MemoryVariable](vn), AssemblyParameterPassingBehaviour.Copy)
case ByMosRegister(reg) =>
AssemblyParam(typ, RegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
case ByZRegister(reg) =>
AssemblyParam(typ, ZRegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
case ByConstant(vn) =>
AssemblyParam(typ, Placeholder(vn, typ), AssemblyParameterPassingBehaviour.ByConstant)
case ByReference(vn) =>
@ -733,6 +735,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case _ =>
}
case ByMosRegister(_) => ()
case ByZRegister(_) => ()
case ByConstant(name) =>
val v = ConstantThing(prefix + name, UnexpandedConstant(prefix + name, typ.size), typ)
addThing(v, stmt.position)

View File

@ -127,6 +127,10 @@ case class RegisterVariable(register: MosRegister.Value, typ: Type) extends Vari
def name: String = register.toString
}
case class ZRegisterVariable(register: ZRegister.Value, typ: Type) extends Variable {
def name: String = register.toString
}
case class Placeholder(name: String, typ: Type) extends Variable
sealed trait UninitializedMemory extends ThingInMemory {

View File

@ -63,6 +63,13 @@ class Z80Assembler(program: Program,
index
case ZLine(LABEL | BYTE | DISCARD_F | DISCARD_HL | DISCARD_BCDEIX | DISCARD_A, _, _, _) =>
???
case ZLine(RST, NoRegisters, param, _) =>
val opcode = param.quickSimplify match {
case NumericConstant(n, _) if n >=0 && n <= 0x38 && n % 8 == 0 => 0xc7 + n.toInt
case _ => ErrorReporting.error("Invalid param for RST"); 0xc7
}
writeByte(bank, index, opcode)
index + 1
case ZLine(op, NoRegisters, _, _) if implieds.contains(op) =>
writeByte(bank, index, implieds(op))
index + 1

View File

@ -191,6 +191,26 @@ abstract class MfParser[T](filename: String, input: String, currentDirectory: St
ParameterDeclaration(typ, ByVariable(name)).pos(p)
}
def asmExpression: P[Expression] = (position() ~ NoCut(
("<" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = false)) |
(">" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = true)) |
mfExpression(mathLevel)
)).map { case (p, e) => e.pos(p) }
def asmExpressionWithParens: P[(Expression, Boolean)] = (position() ~ NoCut(
("(" ~ HWS ~ asmExpression ~ HWS ~ ")").map(_ -> true) |
asmExpression.map(_ -> false)
)).map { case (p, e) => e._1.pos(p) -> e._2 }
def elidable: P[Boolean] = ("?".! ~/ HWS).?.map(_.isDefined)
val appcComplex: P[ParamPassingConvention] = P((("const" | "ref").! ~/ AWS).? ~ AWS ~ identifier) map {
case (None, name) => ByVariable(name)
case (Some("const"), name) => ByConstant(name)
case (Some("ref"), name) => ByReference(name)
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
def asmParamDefinition: P[ParameterDeclaration]
def arrayListElement: P[ArrayContents] = arrayStringContents | arrayLoopContents | arrayFileContents | mfExpression(nonStatementLevel).map(e => LiteralContents(List(e)))

View File

@ -19,12 +19,6 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
def asmOpcode: P[Opcode.Value] = (position() ~ letter.rep(exactly = 3).! ~ ("_W" | "_w").?.!).map { case (p, suffix, o) => Opcode.lookup(o + suffix, Some(p)) }
def asmExpression: P[Expression] = (position() ~ NoCut(
("<" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = false)) |
(">" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = true)) |
mfExpression(mathLevel)
)).map { case (p, e) => e.pos(p) }
private val commaX = HWS ~ "," ~ HWS ~ ("X" | "x") ~ HWS
private val commaY = HWS ~ "," ~ HWS ~ ("Y" | "y") ~ HWS
private val commaZ = HWS ~ "," ~ HWS ~ ("Z" | "z") ~ HWS
@ -52,8 +46,6 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
)).?.map(_.getOrElse(AddrMode.Implied -> LiteralExpression(0, 1)))
}
def elidable: P[Boolean] = ("?".! ~/ HWS).?.map(_.isDefined)
def asmInstruction: P[ExecutableStatement] = {
val lineParser: P[(Boolean, Opcode.Value, (AddrMode.Value, Expression))] = !"}" ~ elidable ~/ asmOpcode ~/ asmParameter
lineParser.map { case (elid, op, param) =>
@ -86,13 +78,6 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
val appcComplex: P[ParamPassingConvention] = P((("const" | "ref").! ~/ AWS).? ~ AWS ~ identifier) map {
case (None, name) => ByVariable(name)
case (Some("const"), name) => ByConstant(name)
case (Some("ref"), name) => ByReference(name)
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
val asmParamDefinition: P[ParameterDeclaration] = for {
p <- position()
typ <- identifier ~ SWS

View File

@ -1,17 +1,117 @@
package millfork.parser
import fastparse.all
import java.util.Locale
import fastparse.all._
import fastparse.core
import millfork.CompilationOptions
import millfork.assembly.z80.ZLine
import millfork.node.{ExecutableStatement, ParameterDeclaration, Position, Statement}
import millfork.assembly.z80.{ZOpcode, _}
import millfork.env.{ByZRegister, Constant, ParamPassingConvention}
import millfork.error.ErrorReporting
import millfork.node._
/**
* @author Karol Stasiak
*/
case class Z80Parser(filename: String, input: String, currentDirectory: String, options: CompilationOptions) extends MfParser[ZLine](filename, input, currentDirectory, options) {
override def asmParamDefinition: all.P[ParameterDeclaration] = ???
override def asmStatement: all.P[ExecutableStatement] = ???
private val zero = LiteralExpression(0, 1)
override def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]]): Unit = ???
val appcSimple: P[ParamPassingConvention] = P("a" | "b" | "c" | "d" | "e" | "hl" | "bc" | "de").!.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 "hl" => ByZRegister(ZRegister.HL)
case "bc" => ByZRegister(ZRegister.BC)
case "de" => ByZRegister(ZRegister.DE)
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
override def asmParamDefinition: P[ParameterDeclaration] = for {
p <- position()
typ <- identifier ~ SWS
appc <- appcSimple | appcComplex
} yield ParameterDeclaration(typ, appc).pos(p)
// TODO: label and instruction in one line
def asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, VariableExpression(l), elidable = true))
def asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall).map(ExpressionStatement)
private def normalOp8(op: ZOpcode.Value): P[(ZOpcode.Value, ZRegisters, Expression)] = asmExpressionWithParens.map {
case (VariableExpression("A" | "a"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("B" | "b"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("C" | "c"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("D" | "d"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("E" | "e"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("HL" | "hl"), true) => (op, OneRegister(ZRegister.MEM_HL), zero)
// TODO: IX/IY
case (e, false) => (op, OneRegister(ZRegister.IMM_8), e)
}
private def modifyOp8(op: ZOpcode.Value): P[(ZOpcode.Value, ZRegisters, Expression)] = asmExpressionWithParens.map {
case (VariableExpression("A" | "a"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("B" | "b"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("C" | "c"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("D" | "d"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("E" | "e"), false) => (op, OneRegister(ZRegister.A), zero)
case (VariableExpression("HL" | "hl"), true) => (op, OneRegister(ZRegister.MEM_HL), zero)
// TODO: IX/IY
case (e, _) => ErrorReporting.fatal("Invalid parameter for " + op, e.position)
}
private val jumpCondition: P[ZRegisters] = (HWS ~ identifier ~ HWS).map {
case "Z" | "z" => IfFlagSet(ZFlag.Z)
case "PE" | "pe" => 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 "NC" | "nc" => IfFlagClear(ZFlag.C)
case "P" | "p" => IfFlagClear(ZFlag.S)
case _ => ErrorReporting.fatal("Invalid condition flag")
}
private val jumpConditionWithComma: P[ZRegisters] = (jumpCondition ~ "," ~/ HWS).?.map (_.getOrElse(NoRegisters))
def asmInstruction: P[ExecutableStatement] = {
import ZOpcode._
for {
el <- elidable
opcode: String <- identifier ~/ HWS
(actualOpcode, registers, param) <- opcode.toUpperCase(Locale.ROOT) match {
case "RST" => asmExpression.map((RST, NoRegisters, _))
case "RET" => P("").map(imm(RET)) // TODO: conditionals
case "CALL" => (jumpCondition~asmExpression).map{case (reg, param) => (CALL, reg, param)}
case "JP" => (jumpCondition~asmExpression).map{case (reg, param) => (JP, reg, param)}
case "JR" => (jumpCondition~asmExpression).map{case (reg, param) => (JR, reg, param)}
case "DJNZ" => asmExpression.map((DJNZ, NoRegisters, _))
case "CP" => normalOp8(CP)
case "AND" => normalOp8(AND)
case "OR" => normalOp8(OR)
case "XOR" => normalOp8(XOR)
case "RL" => modifyOp8(RL)
case "RR" => modifyOp8(RR)
case "RLC" => modifyOp8(RLC)
case "RRC" => modifyOp8(RRC)
case "SLA" => modifyOp8(SLA)
case "SLL" => modifyOp8(SLL)
case "SRA" => modifyOp8(SRA)
case "SRL" => modifyOp8(SRL)
case _ => ErrorReporting.fatal("Unsupported opcode " + opcode)
}
} yield Z80AssemblyStatement(actualOpcode, registers, param, el)
}
private def imm(opcode: ZOpcode.Value): Any => (ZOpcode.Value, ZRegisters, Expression) = (_: Any) => {
(opcode, NoRegisters, zero)
}
override def 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 = {
// TODO
}
}