mirror of
https://github.com/KarolS/millfork.git
synced 2025-03-21 21:30:23 +00:00
Initial and very incomplete support for Z80 assembly
This commit is contained in:
parent
c153588600
commit
fe85757e00
src/main/scala/millfork
@ -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 {
|
||||
|
@ -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)
|
||||
|
4
src/main/scala/millfork/env/Thing.scala
vendored
4
src/main/scala/millfork/env/Thing.scala
vendored
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user