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

Z80: Intel syntax support

This commit is contained in:
Karol Stasiak 2018-08-03 13:23:37 +02:00
parent e393a3de9c
commit fab1cafec3
15 changed files with 592 additions and 70 deletions

View File

@ -26,10 +26,6 @@ no extension for BBC micro program file,
* `-s` Generate also the assembly output. It is not compatible with any assembler, but it serves purely informational purpose. The file has the same nam as the output file and the extension is `.asm`. * `-s` Generate also the assembly output. It is not compatible with any assembler, but it serves purely informational purpose. The file has the same nam as the output file and the extension is `.asm`.
* `-foutput_intel_syntax`, `-foutput_zilog_syntax`
Choose syntax for assembly output on 8080-like targets.
`.ini` equivalent: `output_intel_syntax`. Default: Intel (true) on Intel 8080, Zilog (false) otherwise.
* `-g` Generate also the label file. The label file contains labels with their addresses, with duplicates removed. It can be loaded into the monitor of the Vice emulator for debugging purposes. The file has the same name as the output file and the extension is `.lbl`. * `-g` Generate also the label file. The label file contains labels with their addresses, with duplicates removed. It can be loaded into the monitor of the Vice emulator for debugging purposes. The file has the same name as the output file and the extension is `.lbl`.
* `-I <dir>;<dir>` The include directories. The current working directory is also an include directory. Those directories are searched for modules and platform definitions. * `-I <dir>;<dir>` The include directories. The current working directory is also an include directory. Those directories are searched for modules and platform definitions.
@ -40,6 +36,17 @@ Choose syntax for assembly output on 8080-like targets.
* `-D <feature>=<value>` Defines a feature value for the preprocessor. * `-D <feature>=<value>` Defines a feature value for the preprocessor.
* `-finput_intel_syntax`, `-finput_zilog_syntax`
Choose syntax for assembly sources on 8080-like targets.
Can be overridden by the source file itself using `#pragma`.
`.ini` equivalent: `input_intel_syntax`. Default: Intel (true) on Intel 8080, Zilog (false) otherwise.
* `-foutput_intel_syntax`, `-foutput_zilog_syntax`
Choose syntax for assembly output on 8080-like targets.
`.ini` equivalent: `output_intel_syntax`. Default: Intel (true) on Intel 8080, Zilog (false) otherwise.
* `--syntax=intel`, `--syntax=zilog` sets both previous options at once
## Verbosity options ## Verbosity options
* `-q` Suppress all messages except for errors. * `-q` Suppress all messages except for errors.

View File

@ -48,7 +48,7 @@ You may be also interested in the following:
* `-fipo` enable interprocedural optimization * `-fipo` enable interprocedural optimization
* `-s` additionally generate assembly output * `-s` additionally generate assembly output
(if targeting Intel 8080, use `-foutput_intel_syntax` or `-foutput_zilog_syntax` to choose the preferred output syntax) (if targeting Intel 8080, use `--syntax=intel` or `--syntax=zilog` to choose the preferred assembly syntax)
* `-g` additionally generate a label file, in format compatible with VICE emulator * `-g` additionally generate a label file, in format compatible with VICE emulator

View File

@ -14,6 +14,9 @@ Octal: `0o172`
Hexadecimal: `$D323`, `0x2a2` Hexadecimal: `$D323`, `0x2a2`
When using Intel syntax for inline assembly, another hexadecimal syntax is available: `0D323H`, `2a2h`.
It is not allowed in any other places.
## String literals ## String literals
String literals can be used as either array initializers or expressions of type `pointer`. String literals can be used as either array initializers or expressions of type `pointer`.

View File

@ -103,4 +103,12 @@ To use such value in other files, consider this:
#use WIDESCREEN #use WIDESCREEN
const byte is_widescreen = WIDESCREEN const byte is_widescreen = WIDESCREEN
### `#pragma`
Changes the behaviour of the parser for the current file.
* `#pragma intel_syntax` interpret assembly using Intel syntax
* `#pragma zilog_syntax` interpret assembly using Zilog syntax

View File

@ -1,54 +1,56 @@
#pragma intel_syntax
inline asm void exit() { inline asm void exit() {
? ld c, 0 ? mvi c, 0
call 5 call 5
? ret ? ret
} }
inline asm void putchar (byte e) { inline asm void putchar (byte e) {
? ld c, 2 ? mvi c, 2
call 5 call 5
? ret ? ret
} }
inline asm byte getchar() { inline asm byte getchar() {
? ld c, 1 ? mvi c, 1
call 5 call 5
? ret ? ret
} }
#if 0 #if 0
inline asm void putstr_cpm(pointer de) { inline asm void putstr_cpm(pointer de) {
? ld c, 9 ? mvi c, 9
call 5 call 5
? ret ? ret
} }
#endif #endif
inline asm byte readychar() { inline asm byte readychar() {
? ld c, 11 ? mvi c, 11
call 5 call 5
? ret ? ret
} }
inline asm word cpm_version() { inline asm word cpm_version() {
? ld c, 12 ? mvi c, 12
call 5 call 5
? ret ? ret
} }
inline asm void reset_disk_system() { inline asm void reset_disk_system() {
? ld c, 13 ? mvi c, 13
call 5 call 5
? ret ? ret
} }
inline asm byte current_disk() { inline asm byte current_disk() {
? ld c,25 ? mvi c,25
call 5 call 5
? ret ? ret
} }
#if 0 #if 0
inline asm word console_mode(word de) { inline asm word console_mode(word de) {
? ld c, 109 ? mvi c, 109
call 5 call 5
? ret ? ret
} }

View File

@ -3,6 +3,8 @@
#warn stdio_zxspectrum module should be only used on ZX Spectrum-compatible targets #warn stdio_zxspectrum module should be only used on ZX Spectrum-compatible targets
#endif #endif
#pragma zilog_syntax
import stdio import stdio
void putstr(pointer str, byte len) { void putstr(pointer str, byte len) {

View File

@ -4,6 +4,8 @@
#warn stdlib_i80 module should be only used on Intel 8080-like targets #warn stdlib_i80 module should be only used on Intel 8080-like targets
#endif #endif
#pragma zilog_syntax
macro asm void poke(word const addr, byte a) { macro asm void poke(word const addr, byte a) {
LD (addr), A LD (addr), A
} }

View File

@ -3,6 +3,8 @@
#warn zxspectrum module should be only used on ZX Spectrum-compatible targets #warn zxspectrum module should be only used on ZX Spectrum-compatible targets
#endif #endif
#pragma zilog_syntax
inline asm void putchar(byte a) { inline asm void putchar(byte a) {
rst $10 rst $10
? ret ? ret

View File

@ -210,7 +210,7 @@ object Cpu extends Enumeration {
case Sixteen => case Sixteen =>
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator) mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator)
case Intel8080 => case Intel8080 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, UseIntelSyntaxForOutput) i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
case Z80 => case Z80 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts) i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
case EZ80 => case EZ80 =>
@ -316,6 +316,7 @@ object CompilationFlag extends Enumeration {
"iy_scratch" -> UseIyForScratch, "iy_scratch" -> UseIyForScratch,
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts, "use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
"output_intel_syntax" -> UseIntelSyntaxForOutput, "output_intel_syntax" -> UseIntelSyntaxForOutput,
"input_intel_syntax" -> UseIntelSyntaxForInput,
"ipo" -> InterproceduralOptimization, "ipo" -> InterproceduralOptimization,
"inline" -> InlineFunctions, "inline" -> InlineFunctions,
"dangerous_optimizations" -> DangerousOptimizations, "dangerous_optimizations" -> DangerousOptimizations,

View File

@ -285,6 +285,18 @@ object Main {
} }
}.description("Define a feature value for the preprocessor.") }.description("Define a feature value for the preprocessor.")
boolean("-finput_intel_syntax", "-finput_zilog_syntax").action((c,v) =>
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v)
).description("Select syntax for assembly source input.")
boolean("-foutput_intel_syntax", "-foutput_zilog_syntax").action((c,v) =>
c.changeFlag(CompilationFlag.UseIntelSyntaxForOutput, v)
).description("Select syntax for assembly output.")
boolean("--syntax=intel", "--syntax=zilog").action((c,v) =>
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v).changeFlag(CompilationFlag.UseIntelSyntaxForOutput, v)
).description("Select syntax for assembly input and output.")
endOfFlags("--").description("Marks the end of options.") endOfFlags("--").description("Marks the end of options.")
fluff("", "Verbosity options:", "") fluff("", "Verbosity options:", "")
@ -451,9 +463,6 @@ object Main {
flag("--single-threaded").action(c => flag("--single-threaded").action(c =>
c.changeFlag(CompilationFlag.SingleThreaded, true) c.changeFlag(CompilationFlag.SingleThreaded, true)
).description("Run the compiler in a single thread.") ).description("Run the compiler in a single thread.")
boolean("-foutput_intel_syntax", "-foutput_zilog_syntax").action((c,v) =>
c.changeFlag(CompilationFlag.UseIntelSyntaxForOutput, v)
).description("Select syntax for assembly output.")
flag("--help").action(c => { flag("--help").action(c => {
println("millfork version " + BuildInfo.version) println("millfork version " + BuildInfo.version)

View File

@ -21,6 +21,8 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
var lastLabel = "" var lastLabel = ""
protected val log: Logger = options.log protected val log: Logger = options.log
def allowIntelHexAtomsInAssembly: Boolean
def toAst: Parsed[Program] = program.parse(input + "\n\n\n") def toAst: Parsed[Program] = program.parse(input + "\n\n\n")
private val lineStarts: Array[Int] = (0 +: input.zipWithIndex.filter(_._1 == '\n').map(_._2)).toArray private val lineStarts: Array[Int] = (0 +: input.zipWithIndex.filter(_._1 == '\n').map(_._2)).toArray
@ -88,8 +90,12 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
val literalAtom: P[LiteralExpression] = charAtom | binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom val literalAtom: P[LiteralExpression] = charAtom | binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom
val literalAtomWithIntel: P[LiteralExpression] = charAtom | binaryAtom | hexAtom | octalAtom | quaternaryAtom | intelHexAtom | decimalAtom
val atom: P[Expression] = P(position() ~ (literalAtom | variableAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)} val atom: P[Expression] = P(position() ~ (literalAtom | variableAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
val atomWithIntel: P[Expression] = P(position() ~ (literalAtomWithIntel | variableAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
val globalVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(true) val globalVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(true)
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false) val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
@ -99,8 +105,8 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
flags <- variableFlags ~ HWS flags <- variableFlags ~ HWS
typ <- identifier ~ SWS typ <- identifier ~ SWS
name <- identifier ~/ HWS ~/ Pass name <- identifier ~/ HWS ~/ Pass
addr <- ("@" ~/ HWS ~/ mfExpression(1)).?.opaque("<address>") ~ HWS addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("<address>") ~ HWS
initialValue <- ("=" ~/ HWS ~/ mfExpression(1)).? ~ HWS initialValue <- ("=" ~/ HWS ~/ mfExpression(1, false)).? ~ HWS
_ <- &(EOL) ~/ "" _ <- &(EOL) ~/ ""
} yield { } yield {
Seq(VariableDeclarationStatement(name, typ, Seq(VariableDeclarationStatement(name, typ,
@ -122,9 +128,9 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
} }
def asmExpression: P[Expression] = (position() ~ NoCut( def asmExpression: P[Expression] = (position() ~ NoCut(
("<" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = false)) | ("<" ~/ HWS ~ mfExpression(mathLevel, allowIntelHexAtomsInAssembly)).map(e => HalfWordExpression(e, hiByte = false)) |
(">" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = true)) | (">" ~/ HWS ~ mfExpression(mathLevel, allowIntelHexAtomsInAssembly)).map(e => HalfWordExpression(e, hiByte = true)) |
mfExpression(mathLevel) mfExpression(mathLevel, allowIntelHexAtomsInAssembly)
)).map { case (p, e) => e.pos(p) } )).map { case (p, e) => e.pos(p) }
def asmExpressionWithParens: P[(Expression, Boolean)] = (position() ~ NoCut( def asmExpressionWithParens: P[(Expression, Boolean)] = (position() ~ NoCut(
@ -147,7 +153,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
def asmParamDefinition: P[ParameterDeclaration] def asmParamDefinition: P[ParameterDeclaration]
def arrayListElement: P[ArrayContents] = arrayStringContents | arrayProcessedContents | arrayLoopContents | arrayFileContents | mfExpression(nonStatementLevel).map(e => LiteralContents(List(e))) def arrayListElement: P[ArrayContents] = arrayStringContents | arrayProcessedContents | arrayLoopContents | arrayFileContents | mfExpression(nonStatementLevel, false).map(e => LiteralContents(List(e)))
def arrayProcessedContents: P[ArrayContents] = for { def arrayProcessedContents: P[ArrayContents] = for {
_ <- "@" ~/ HWS _ <- "@" ~/ HWS
@ -176,10 +182,10 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
def arrayLoopContents: P[ArrayContents] = for { def arrayLoopContents: P[ArrayContents] = for {
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
start <- mfExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass start <- mfExpression(nonStatementLevel, false) ~ HWS ~ "," ~/ HWS ~/ Pass
pos <- position("loop direction") pos <- position("loop direction")
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
end <- mfExpression(nonStatementLevel) end <- mfExpression(nonStatementLevel, false)
body <- AWS ~ arrayContents body <- AWS ~ arrayContents
} yield { } yield {
val fixedDirection = direction match { val fixedDirection = direction match {
@ -208,21 +214,27 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
p <- position() p <- position()
bank <- bankDeclaration bank <- bankDeclaration
name <- "array" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS name <- "array" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS
length <- ("[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~ "]").? ~ HWS length <- ("[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~ "]").? ~ HWS
addr <- ("@" ~/ HWS ~/ mfExpression(1)).? ~/ HWS addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
} yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents).pos(p)) } yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents).pos(p))
def tightMfExpression: P[Expression] = P(mfParenExpr | functionCall | mfIndexedExpression | atom) // TODO def tightMfExpression(allowIntelHex: Boolean): P[Expression] = {
val a = if (allowIntelHex) atomWithIntel else atom
P(mfParenExpr(allowIntelHex) | functionCall(allowIntelHex) | mfIndexedExpression | a) // TODO
}
def tightMfExpressionButNotCall: P[Expression] = P(mfParenExpr | mfIndexedExpression | atom) // TODO def tightMfExpressionButNotCall(allowIntelHex: Boolean): P[Expression] = {
val a = if (allowIntelHex) atomWithIntel else atom
P(mfParenExpr(allowIntelHex) | mfIndexedExpression | a) // TODO
}
def mfExpression(level: Int): P[Expression] = { def mfExpression(level: Int, allowIntelHex: Boolean): P[Expression] = {
val allowedOperators = mfOperatorsDropFlatten(level) val allowedOperators = mfOperatorsDropFlatten(level)
def inner: P[SeparatedList[Expression, String]] = { def inner: P[SeparatedList[Expression, String]] = {
for { for {
head <- tightMfExpression ~/ HWS head <- tightMfExpression(allowIntelHex) ~/ HWS
maybeOperator <- StringIn(allowedOperators: _*).!.? maybeOperator <- StringIn(allowedOperators: _*).!.?
maybeTail <- maybeOperator.fold[P[Option[List[(String, Expression)]]]](Pass.map(_ => None))(o => (HWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail))) maybeTail <- maybeOperator.fold[P[Option[List[(String, Expression)]]]](Pass.map(_ => None))(o => (HWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail)))
} yield { } yield {
@ -237,7 +249,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
xs.separators.distinct match { xs.separators.distinct match {
case Nil => case Nil =>
if (xs.tail.nonEmpty) if (xs.tail.nonEmpty)
log.error("Too many different operators") log.error("Too many different operators", xs.head.head.position)
p(xs.head, level + 1) p(xs.head, level + 1)
case List("+") | List("-") | List("+", "-") | List("-", "+") => case List("+") | List("-") | List("+", "-") | List("-", "+") =>
SumExpression(xs.toPairList("+").map { case (op, value) => (op == "-", p(value, level + 1)) }, decimal = false) SumExpression(xs.toPairList("+").map { case (op, value) => (op == "-", p(value, level + 1)) }, decimal = false)
@ -253,7 +265,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
case List(op) => case List(op) =>
FunctionCallExpression(op, xs.items.map(value => p(value, level + 1))) FunctionCallExpression(op, xs.items.map(value => p(value, level + 1)))
case _ => case _ =>
log.error("Too many different operators") log.error("Too many different operators", xs.head.head.position)
LiteralExpression(0, 1) LiteralExpression(0, 1)
} }
} }
@ -269,24 +281,24 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
} yield rightOpt.fold(left)(right => SeparateBytesExpression(left, right).pos(p)) } yield rightOpt.fold(left)(right => SeparateBytesExpression(left, right).pos(p))
def mfParenExpr: P[Expression] = P("(" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~/ ")") def mfParenExpr(allowIntelHex: Boolean): P[Expression] = P("(" ~/ AWS ~/ mfExpression(nonStatementLevel, allowIntelHex) ~ AWS ~/ ")")
def mfIndexedExpression: P[IndexedExpression] = for { def mfIndexedExpression: P[IndexedExpression] = for {
p <- position() p <- position()
array <- identifier array <- identifier
index <- HWS ~ "[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~/ "]" index <- HWS ~ "[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~/ "]"
} yield IndexedExpression(array, index).pos(p) } yield IndexedExpression(array, index).pos(p)
def functionCall: P[FunctionCallExpression] = for { def functionCall(allowIntelHex: Boolean): P[FunctionCallExpression] = for {
p <- position() p <- position()
name <- identifier name <- identifier
params <- HWS ~ "(" ~/ AWS ~/ mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "" params <- HWS ~ "(" ~/ AWS ~/ mfExpression(nonStatementLevel, allowIntelHex).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ ""
} yield FunctionCallExpression(name, params.toList).pos(p) } yield FunctionCallExpression(name, params.toList).pos(p)
val expressionStatement: P[Seq[ExecutableStatement]] = mfExpression(0).map(x => Seq(ExpressionStatement(x))) val expressionStatement: P[Seq[ExecutableStatement]] = mfExpression(0, false).map(x => Seq(ExpressionStatement(x)))
val assignmentStatement: P[Seq[ExecutableStatement]] = val assignmentStatement: P[Seq[ExecutableStatement]] =
(position() ~ mfLhsExpression ~ HWS ~ "=" ~/ HWS ~ mfExpression(1)).map { (position() ~ mfLhsExpression ~ HWS ~ "=" ~/ HWS ~ mfExpression(1, false)).map {
case (p, l, r) => Seq(Assignment(l, r).pos(p)) case (p, l, r) => Seq(Assignment(l, r).pos(p))
} }
@ -314,7 +326,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
def executableStatements: P[Seq[ExecutableStatement]] = ("{" ~/ AWS ~/ executableStatement.rep(sep = NoCut(EOL) ~ !"}" ~/ Pass) ~/ AWS ~ "}").map(_.flatten) def executableStatements: P[Seq[ExecutableStatement]] = ("{" ~/ AWS ~/ executableStatement.rep(sep = NoCut(EOL) ~ !"}" ~/ Pass) ~/ AWS ~ "}").map(_.flatten)
val dispatchLabel: P[ReturnDispatchLabel] = val dispatchLabel: P[ReturnDispatchLabel] =
("default" ~ !letterOrDigit ~/ AWS ~/ ("(" ~/ position("default branch range") ~ AWS ~/ mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?).map{ ("default" ~ !letterOrDigit ~/ AWS ~/ ("(" ~/ position("default branch range") ~ AWS ~/ mfExpression(nonStatementLevel, false).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?).map{
case None => DefaultReturnDispatchLabel(None, None) case None => DefaultReturnDispatchLabel(None, None)
case Some((_, Seq())) => DefaultReturnDispatchLabel(None, None) case Some((_, Seq())) => DefaultReturnDispatchLabel(None, None)
case Some((_, Seq(e))) => DefaultReturnDispatchLabel(None, Some(e)) case Some((_, Seq(e))) => DefaultReturnDispatchLabel(None, Some(e))
@ -322,17 +334,17 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
case Some((pos, _)) => case Some((pos, _)) =>
log.error("Invalid default branch declaration", Some(pos)) log.error("Invalid default branch declaration", Some(pos))
DefaultReturnDispatchLabel(None, None) DefaultReturnDispatchLabel(None, None)
} | mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS).map(exprs => StandardReturnDispatchLabel(exprs.toList)) } | mfExpression(nonStatementLevel, false).rep(min = 0, sep = AWS ~ "," ~/ AWS).map(exprs => StandardReturnDispatchLabel(exprs.toList))
val dispatchBranch: P[ReturnDispatchBranch] = for { val dispatchBranch: P[ReturnDispatchBranch] = for {
pos <- position() pos <- position()
l <- dispatchLabel ~/ HWS ~/ "@" ~/ HWS l <- dispatchLabel ~/ HWS ~/ "@" ~/ HWS
f <- tightMfExpressionButNotCall ~/ HWS f <- tightMfExpressionButNotCall(false) ~/ HWS
parameters <- ("(" ~/ position("dispatch actual parameters") ~ AWS ~/ mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").? parameters <- ("(" ~/ position("dispatch actual parameters") ~ AWS ~/ mfExpression(nonStatementLevel, false).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
} yield ReturnDispatchBranch(l, f, parameters.map(_._2.toList).getOrElse(Nil)).pos(pos) } yield ReturnDispatchBranch(l, f, parameters.map(_._2.toList).getOrElse(Nil)).pos(pos)
val dispatchStatementBody: P[Seq[ExecutableStatement]] = for { val dispatchStatementBody: P[Seq[ExecutableStatement]] = for {
indexer <- "[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~/ AWS ~/ "]" ~/ AWS indexer <- "[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~/ AWS ~/ "]" ~/ AWS
_ <- position("dispatch statement body") _ <- position("dispatch statement body")
parameters <- ("(" ~/ position("dispatch parameters") ~ AWS ~/ mfLhsExpression.rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").? parameters <- ("(" ~/ position("dispatch parameters") ~ AWS ~/ mfLhsExpression.rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
_ <- AWS ~/ position("dispatch statement body") ~/ "{" ~/ AWS _ <- AWS ~/ position("dispatch statement body") ~/ "{" ~/ AWS
@ -340,24 +352,24 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
_ <- AWS ~/ "}" _ <- AWS ~/ "}"
} yield Seq(ReturnDispatchStatement(indexer, parameters.map(_._2.toList).getOrElse(Nil), branches.toList)) } yield Seq(ReturnDispatchStatement(indexer, parameters.map(_._2.toList).getOrElse(Nil), branches.toList))
val returnOrDispatchStatement: P[Seq[ExecutableStatement]] = "return" ~ !letterOrDigit ~/ HWS ~ (dispatchStatementBody | mfExpression(nonStatementLevel).?.map(ReturnStatement).map(Seq(_))) val returnOrDispatchStatement: P[Seq[ExecutableStatement]] = "return" ~ !letterOrDigit ~/ HWS ~ (dispatchStatementBody | mfExpression(nonStatementLevel, false).?.map(ReturnStatement).map(Seq(_)))
def ifStatement: P[Seq[ExecutableStatement]] = for { def ifStatement: P[Seq[ExecutableStatement]] = for {
condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel) condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel, false)
thenBranch <- AWS ~/ executableStatements thenBranch <- AWS ~/ executableStatements
elseBranch <- (AWS ~ "else" ~/ AWS ~/ (ifStatement | executableStatements)).? elseBranch <- (AWS ~ "else" ~/ AWS ~/ (ifStatement | executableStatements)).?
} yield Seq(IfStatement(condition, thenBranch.toList, elseBranch.getOrElse(Nil).toList)) } yield Seq(IfStatement(condition, thenBranch.toList, elseBranch.getOrElse(Nil).toList))
def whileStatement: P[Seq[ExecutableStatement]] = for { def whileStatement: P[Seq[ExecutableStatement]] = for {
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel) condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel, false)
body <- AWS ~ executableStatements body <- AWS ~ executableStatements
} yield Seq(WhileStatement(condition, body.toList, Nil)) } yield Seq(WhileStatement(condition, body.toList, Nil))
def forStatement: P[Seq[ExecutableStatement]] = for { def forStatement: P[Seq[ExecutableStatement]] = for {
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
start <- mfExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass start <- mfExpression(nonStatementLevel, false) ~ HWS ~ "," ~/ HWS ~/ Pass
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
end <- mfExpression(nonStatementLevel) end <- mfExpression(nonStatementLevel, false)
body <- AWS ~ executableStatements body <- AWS ~ executableStatements
} yield Seq(ForStatement(identifier, start, end, direction, body.toList)) } yield Seq(ForStatement(identifier, start, end, direction, body.toList))
@ -366,7 +378,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
//noinspection MutatorLikeMethodIsParameterless //noinspection MutatorLikeMethodIsParameterless
def doWhileStatement: P[Seq[ExecutableStatement]] = for { def doWhileStatement: P[Seq[ExecutableStatement]] = for {
body <- "do" ~ !letterOrDigit ~/ AWS ~ executableStatements ~/ AWS body <- "do" ~ !letterOrDigit ~/ AWS ~ executableStatements ~/ AWS
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel) condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel, false)
} yield Seq(DoWhileStatement(body.toList, Nil, condition)) } yield Seq(DoWhileStatement(body.toList, Nil, condition))
val functionDefinition: P[Seq[DeclarationStatement]] = for { val functionDefinition: P[Seq[DeclarationStatement]] = for {
@ -377,7 +389,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
if !InvalidReturnTypes(returnType) if !InvalidReturnTypes(returnType)
name <- identifier ~ HWS name <- identifier ~ HWS
params <- "(" ~/ AWS ~/ (if (flags("asm")) asmParamDefinition else paramDefinition).rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ ")" ~/ AWS params <- "(" ~/ AWS ~/ (if (flags("asm")) asmParamDefinition else paramDefinition).rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ ")" ~/ AWS
addr <- ("@" ~/ HWS ~/ mfExpression(1)).?.opaque("<address>") ~/ AWS addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("<address>") ~/ AWS
statements <- (externFunctionBody | (if (flags("asm")) asmStatements else statements).map(l => Some(l))) ~/ Pass statements <- (externFunctionBody | (if (flags("asm")) asmStatements else statements).map(l => Some(l))) ~/ Pass
} yield { } yield {
if (flags("interrupt") && flags("macro")) log.error(s"Interrupt function `$name` cannot be macros", Some(p)) if (flags("interrupt") && flags("macro")) log.error(s"Interrupt function `$name` cannot be macros", Some(p))
@ -408,7 +420,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
val enumVariant: P[(String, Option[Expression])] = for { val enumVariant: P[(String, Option[Expression])] = for {
name <- identifier ~/ HWS name <- identifier ~/ HWS
value <- ("=" ~/ HWS ~/ mfExpression(1)).? ~ HWS value <- ("=" ~/ HWS ~/ mfExpression(1, false)).? ~ HWS
} yield name -> value } yield name -> value
val enumVariants: P[List[(String, Option[Expression])]] = val enumVariants: P[List[(String, Option[Expression])]] =
@ -503,6 +515,20 @@ object MfParser {
LiteralExpression(value, size(value, s.length > 2, s.length > 4, s.length > 6)) LiteralExpression(value, size(value, s.length > 2, s.length > 4, s.length > 6))
} }
val intelHexAtom: P[LiteralExpression] =
for {
minus <- "-".!.?
head <- CharIn("0123456789").!
tail <- CharsWhileIn("1234567890abcdefABCDEF", min = 1).!.opaque("<hex digits>")
_ <- P("h" | "H")
} yield {
// check for marking zero:
val s = if (head == "0" && tail.nonEmpty && tail.head >'9') tail else head + tail
val abs = parseLong(s, 16)
val value = sign(abs, minus.isDefined)
LiteralExpression(value, size(value, s.length > 2, s.length > 4, s.length > 6))
}
val octalAtom: P[LiteralExpression] = val octalAtom: P[LiteralExpression] =
for { for {
minus <- "-".!.? minus <- "-".!.?

View File

@ -14,6 +14,8 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
import MfParser._ import MfParser._
def allowIntelHexAtomsInAssembly: Boolean = false
// TODO: label and instruction in one line // TODO: label and instruction in one line
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => MosAssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), elidable = true)) val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => MosAssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), elidable = true))
@ -62,7 +64,7 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
} }
} }
val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall).map(ExpressionStatement) val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall(false)).map(ExpressionStatement)
val asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros val asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros

View File

@ -4,7 +4,8 @@ import java.util.Locale
import fastparse.all.{parserApi, _} import fastparse.all.{parserApi, _}
import fastparse.core import fastparse.core
import millfork.{CompilationFlag, CompilationOptions} import millfork.assembly.z80
import millfork.{CompilationFlag, CompilationOptions, node}
import millfork.assembly.z80.{ZOpcode, _} import millfork.assembly.z80.{ZOpcode, _}
import millfork.env.{ByZRegister, Constant, ParamPassingConvention} import millfork.env.{ByZRegister, Constant, ParamPassingConvention}
import millfork.error.ConsoleLogger import millfork.error.ConsoleLogger
@ -20,12 +21,10 @@ case class Z80Parser(filename: String,
featureConstants: Map[String, Long], featureConstants: Map[String, Long],
useIntelSyntax: Boolean) extends MfParser[ZLine](filename, input, currentDirectory, options, featureConstants) { useIntelSyntax: Boolean) extends MfParser[ZLine](filename, input, currentDirectory, options, featureConstants) {
if (useIntelSyntax) {
options.log.error("Parsing assembly with Intel syntax not supported yet")
}
import MfParser._ import MfParser._
def allowIntelHexAtomsInAssembly: Boolean = useIntelSyntax
private val zero = LiteralExpression(0, 1) private val zero = LiteralExpression(0, 1)
val appcSimple: P[ParamPassingConvention] = (P("hl" | "bc" | "de" | "a" | "b" | "c" | "d" | "e" | "h" | "l").! ~ !letterOrDigit).map { val appcSimple: P[ParamPassingConvention] = (P("hl" | "bc" | "de" | "a" | "b" | "c" | "d" | "e" | "h" | "l").! ~ !letterOrDigit).map {
@ -51,7 +50,7 @@ case class Z80Parser(filename: String,
// TODO: label and instruction in one line // TODO: label and instruction in one line
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, None, VariableExpression(l), elidable = true)) val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, None, VariableExpression(l), elidable = true))
val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall).map(ExpressionStatement) val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall(false)).map(ExpressionStatement)
private val toRegister: Map[String, ZRegister.Value] = Map( private val toRegister: Map[String, ZRegister.Value] = Map(
"A" -> ZRegister.A, "a" -> ZRegister.A, "A" -> ZRegister.A, "a" -> ZRegister.A,
@ -70,6 +69,25 @@ case class Z80Parser(filename: String,
"SP" -> ZRegister.SP, "sp" -> ZRegister.SP, "SP" -> ZRegister.SP, "sp" -> ZRegister.SP,
) )
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,
"PSW" -> ZRegister.AF, "psw" -> ZRegister.AF,
"B" -> ZRegister.BC, "b" -> ZRegister.BC,
"D" -> ZRegister.DE, "d" -> ZRegister.DE,
"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): P[(ZRegister.Value, Option[Expression])] = asmExpressionWithParens.map {
case (VariableExpression("R" | "r"), false) if allowRI => (ZRegister.R, None) case (VariableExpression("R" | "r"), false) if allowRI => (ZRegister.R, None)
case (VariableExpression("I" | "i"), false) if allowRI => (ZRegister.I, None) case (VariableExpression("I" | "i"), false) if allowRI => (ZRegister.I, None)
@ -83,6 +101,24 @@ case class Z80Parser(filename: String,
case (e, _) => (ZRegister.IMM_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 { 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@(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)) case (reg, addr) => (op, OneRegister(reg), None, addr.getOrElse(zero))
@ -370,7 +406,132 @@ case class Z80Parser(filename: String,
} }
} }
val intelAsmInstruction: P[ExecutableStatement] = null // TODO 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 "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 "SPHL" => P("").map(_ => (LD_16, TwoRegisters(SP, HL), None, zero))
case "PCHL" => P("").map(_ => (JP, z80.OneRegister(HL), 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 "MVI" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, e) => (LD, TwoRegisters(r, IMM_8), None, e)
}
case "DAD" => intel16(false).map { r => (ADD_16, TwoRegisters(HL, 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 "DCX" => intel16(false).map { r => (DEC_16, OneRegister(r), None, zero)}
case "POP" => intel16(true).map { r => (POP, OneRegister(r), None, zero)}
case "PUSH" => intel16(true).map { r => (PUSH, OneRegister(r), None, zero)}
case "INR" => intel8.map { r => (INC, OneRegister(r), None, zero)}
case "DCR" => intel8.map { r => (DEC, OneRegister(r), None, zero)}
case "ADD" => intel8.map { r => (ADD, OneRegister(r), None, zero)}
case "SUB" => intel8.map { r => (SUB, OneRegister(r), None, zero)}
case "ADC" => intel8.map { r => (ADC, OneRegister(r), None, zero)}
case "SBB" => intel8.map { r => (SBC, OneRegister(r), None, zero)}
case "ORA" => intel8.map { r => (OR, OneRegister(r), None, zero)}
case "ANA" => intel8.map { r => (AND, OneRegister(r), None, zero)}
case "XRA" => intel8.map { r => (XOR, OneRegister(r), None, zero)}
case "CMP" => intel8.map { r => (CP, OneRegister(r), None, 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 "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 "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 "IN" => asmExpression.map { e => (IN_IMM, OneRegister(A), None, e) }
case "OUT" => asmExpression.map { e => (OUT_IMM, OneRegister(A), None, e) }
case "JMP" => asmExpression.map { e => (JP, 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 "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 _ =>
log.error("Unsupported opcode " + opcode, Some(pos))
imm(NOP)
}
} yield {
val (actualOpcode, registers, offset, param) = tuple4
Z80AssemblyStatement(actualOpcode, registers, offset, param, el)
}
}
val asmInstruction: P[ExecutableStatement] = if (useIntelSyntax) intelAsmInstruction else zilogAsmInstruction val asmInstruction: P[ExecutableStatement] = if (useIntelSyntax) intelAsmInstruction else zilogAsmInstruction

View File

@ -8,9 +8,10 @@ import org.scalatest.{FunSuite, Matchers}
*/ */
class Z80AssemblySuite extends FunSuite with Matchers { class Z80AssemblySuite extends FunSuite with Matchers {
test("Common I80 instructions") { test("Common I80 instructions (Zilog syntax)") {
EmuUnoptimizedIntel8080Run( EmuUnoptimizedIntel8080Run(
""" """
| #pragma zilog_syntax
| asm void main () { | asm void main () {
| ret | ret
| |
@ -58,19 +59,19 @@ class Z80AssemblySuite extends FunSuite with Matchers {
| ld l,$2e | ld l,$2e
| cpl | cpl
| |
| ld hl,$2121 | ld sp,$3131
| ld ($fffe),a | ld ($fffe),a
| inc sp | inc sp
| inc (hl) | inc (hl)
| dec (hl) | dec (hl)
| ld h,$26 | ld (hl),$36
| scf | scf
| add hl,sp | add hl,sp
| ld a,($fffe) | ld a,($fffe)
| dec sp | dec sp
| inc a | inc a
| dec a | dec a
| ld l,$2e | ld a,$3e
| ccf | ccf
| |
| ld b,b | ld b,b
@ -273,10 +274,277 @@ class Z80AssemblySuite extends FunSuite with Matchers {
| } | }
""".stripMargin) """.stripMargin)
} }
test("Common I80 instructions (Intel syntax)") {
test("Intel 8080 instructions") {
EmuUnoptimizedIntel8080Run( EmuUnoptimizedIntel8080Run(
""" """
| #pragma intel_syntax
| asm void main () {
| ret
|
| nop
| lxi b, 0101h
| stax b
| inx b
| inr b
| dcr b
| mvi b,6
| rlc
| dad b
| ldax b
| dcx b
| inr c
| dcr c
| mvi c,0eh
| rrc
|
| lxi d,1111h
| stax d
| inx d
| inr d
| dcr d
| mvi d,16h
| ral
| dad d
| ldax d
| dcx d
| inr d
| dcr d
| mvi e, 1eh
| rar
|
| lxi h, 2121h
| inx h
| inr h
| dcr h
| mvi h,26h
| daa
| dad h
| dcx h
| inr l
| dcr l
| mvi l,2eh
| cma
|
| lxi sp, 3131h
| sta 0fffeh
| inx sp
| inr m
| dcr m
| mvi m, 36h
| stc
| dad sp
| lda 0fffeh
| dcx sp
| inr a
| dcr a
| mvi a, 3eh
| cmc
|
| mov b,b
| mov b,c
| mov b,d
| mov b,e
| mov b,h
| mov b,l
| mov b,m
| mov b,a
|
| mov c,b
| mov c,c
| mov c,d
| mov c,e
| mov c,h
| mov c,l
| mov c,m
| mov c,a
|
| mov d,b
| mov d,c
| mov d,d
| mov d,e
| mov d,h
| mov d,l
| mov d,m
| mov d,a
|
| mov e,b
| mov e,c
| mov e,d
| mov e,e
| mov e,h
| mov e,l
| mov e,m
| mov e,a
|
| mov h,b
| mov h,c
| mov h,d
| mov h,e
| mov h,h
| mov h,l
| mov h,m
| mov h,a
|
| mov l,b
| mov l,c
| mov l,d
| mov l,e
| mov l,h
| mov l,l
| mov l,m
| mov l,a
|
| mov m,b
| mov m,c
| mov m,d
| mov m,e
| mov m,h
| mov m,l
| hlt
| mov m,a
|
| mov a,b
| mov a,c
| mov a,d
| mov a,e
| mov a,h
| mov a,l
| mov a,m
| mov a,a
|
| add b
| add c
| add d
| add e
| add h
| add l
| add m
| add a
|
| adc b
| adc c
| adc d
| adc e
| adc h
| adc l
| adc m
| adc a
|
| sub b
| sub c
| sub d
| sub e
| sub h
| sub l
| sub m
| sub a
|
| sbb b
| sbb c
| sbb d
| sbb e
| sbb h
| sbb l
| sbb m
| sbb a
|
| ana b
| ana c
| ana d
| ana e
| ana h
| ana l
| ana m
| ana a
|
| xra b
| xra c
| xra d
| xra e
| xra h
| xra l
| xra m
| xra a
|
| ora b
| ora c
| ora d
| ora e
| ora h
| ora l
| ora m
| ora a
|
| cmp b
| cmp c
| cmp d
| cmp e
| cmp h
| cmp l
| cmp m
| cmp a
|
| rnz
| pop b
| jnz main
| jmp main
| cnz main
| push b
| adi 1
| rst 0
|
| rz
| ret
| jz main
| cz main
| call main
| aci 1
| rst 1
|
| rnc
| pop d
| jnc main
| cnc main
| push d
| sui 1
| rst 2
|
| rc
| jc main
| cc main
| sbi 1
| rst 3
|
| pop h
| xthl
| push h
| ani 1
| rst 4
|
| pchl
| xri 1
| rst 5
|
| pop psw
| di
| push psw
| ori 1
| rst 6
|
| sphl
| ei
| cpi 1
| rst 7
|
| ret
| }
""".stripMargin)
}
test("Intel 8080 instructions (Zilog syntax)") {
EmuUnoptimizedIntel8080Run(
"""
| #pragma zilog_syntax
| asm void main () { | asm void main () {
| ret | ret
| ld ($fffe),hl | ld ($fffe),hl
@ -302,6 +570,35 @@ class Z80AssemblySuite extends FunSuite with Matchers {
""".stripMargin) """.stripMargin)
} }
test("Intel 8080 instructions (Intel syntax)") {
EmuUnoptimizedIntel8080Run(
"""
| #pragma intel_syntax
| asm void main () {
| ret
| shld 0fffeh
| lhld 0fffeh
| out 1
| in 1
| rpo
| jpo main
| cpo main
| rpe
| jpe main
| xchg
| cpe main
| rp
| jp main
| cp main
| rm
| jm main
| cm main
|
| ret
| }
""".stripMargin)
}
test("Extended I80 instructions") { test("Extended I80 instructions") {
EmuUnoptimizedZ80Run( EmuUnoptimizedZ80Run(
""" """

View File

@ -47,8 +47,8 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}" if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
log.setSource(Some(effectiveSource.lines.toIndexedSeq)) log.setSource(Some(effectiveSource.lines.toIndexedSeq))
val PreprocessingResult(preprocessedSource, features, pragmas) = Preprocessor.preprocessForTest(options, effectiveSource) val PreprocessingResult(preprocessedSource, features, pragmas) = Preprocessor.preprocessForTest(options, effectiveSource)
val parserF = Z80Parser("", preprocessedSource, "", options, features, false) // tests use Intel syntax only when forced to:
//if (pragmas.contains("intel_syntax")) true else if (pragmas.contains("zilog_syntax")) false else options.flag(CompilationFlag.UseIntelSyntax)) val parserF = Z80Parser("", preprocessedSource, "", options, features, pragmas.contains("intel_syntax"))
parserF.toAst match { parserF.toAst match {
case Success(unoptimized, _) => case Success(unoptimized, _) =>
log.assertNoErrors("Parse failed") log.assertNoErrors("Parse failed")
@ -158,7 +158,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
println(f.extra.toString) println(f.extra.toString)
println(f.lastParser.toString) println(f.lastParser.toString)
log.error("Syntax error", Some(parserF.lastPosition)) log.error("Syntax error", Some(parserF.lastPosition))
??? fail("Parsing error")
} }
} }