mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-29 02:31:45 +00:00
Z80: Intel syntax support
This commit is contained in:
parent
e393a3de9c
commit
fab1cafec3
@ -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`.
|
||||
|
||||
* `-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`.
|
||||
|
||||
* `-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.
|
||||
|
||||
* `-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
|
||||
|
||||
* `-q` – Suppress all messages except for errors.
|
||||
|
@ -48,7 +48,7 @@ You may be also interested in the following:
|
||||
* `-fipo` – enable interprocedural optimization
|
||||
|
||||
* `-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
|
||||
|
||||
|
@ -14,6 +14,9 @@ Octal: `0o172`
|
||||
|
||||
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 can be used as either array initializers or expressions of type `pointer`.
|
||||
|
@ -103,4 +103,12 @@ To use such value in other files, consider this:
|
||||
|
||||
#use 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
|
||||
|
||||
|
@ -1,54 +1,56 @@
|
||||
|
||||
#pragma intel_syntax
|
||||
|
||||
inline asm void exit() {
|
||||
? ld c, 0
|
||||
? mvi c, 0
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
inline asm void putchar (byte e) {
|
||||
? ld c, 2
|
||||
? mvi c, 2
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
|
||||
inline asm byte getchar() {
|
||||
? ld c, 1
|
||||
? mvi c, 1
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
|
||||
#if 0
|
||||
inline asm void putstr_cpm(pointer de) {
|
||||
? ld c, 9
|
||||
? mvi c, 9
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
#endif
|
||||
|
||||
inline asm byte readychar() {
|
||||
? ld c, 11
|
||||
? mvi c, 11
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
|
||||
inline asm word cpm_version() {
|
||||
? ld c, 12
|
||||
? mvi c, 12
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
inline asm void reset_disk_system() {
|
||||
? ld c, 13
|
||||
? mvi c, 13
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
inline asm byte current_disk() {
|
||||
? ld c,25
|
||||
? mvi c,25
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
|
||||
#if 0
|
||||
inline asm word console_mode(word de) {
|
||||
? ld c, 109
|
||||
? mvi c, 109
|
||||
call 5
|
||||
? ret
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
#warn stdio_zxspectrum module should be only used on ZX Spectrum-compatible targets
|
||||
#endif
|
||||
|
||||
#pragma zilog_syntax
|
||||
|
||||
import stdio
|
||||
|
||||
void putstr(pointer str, byte len) {
|
||||
|
@ -4,6 +4,8 @@
|
||||
#warn stdlib_i80 module should be only used on Intel 8080-like targets
|
||||
#endif
|
||||
|
||||
#pragma zilog_syntax
|
||||
|
||||
macro asm void poke(word const addr, byte a) {
|
||||
LD (addr), A
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
#warn zxspectrum module should be only used on ZX Spectrum-compatible targets
|
||||
#endif
|
||||
|
||||
#pragma zilog_syntax
|
||||
|
||||
inline asm void putchar(byte a) {
|
||||
rst $10
|
||||
? ret
|
||||
|
@ -210,7 +210,7 @@ object Cpu extends Enumeration {
|
||||
case Sixteen =>
|
||||
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator)
|
||||
case Intel8080 =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, UseIntelSyntaxForOutput)
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||
case Z80 =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
|
||||
case EZ80 =>
|
||||
@ -316,6 +316,7 @@ object CompilationFlag extends Enumeration {
|
||||
"iy_scratch" -> UseIyForScratch,
|
||||
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
|
||||
"output_intel_syntax" -> UseIntelSyntaxForOutput,
|
||||
"input_intel_syntax" -> UseIntelSyntaxForInput,
|
||||
"ipo" -> InterproceduralOptimization,
|
||||
"inline" -> InlineFunctions,
|
||||
"dangerous_optimizations" -> DangerousOptimizations,
|
||||
|
@ -285,6 +285,18 @@ object Main {
|
||||
}
|
||||
}.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.")
|
||||
|
||||
fluff("", "Verbosity options:", "")
|
||||
@ -451,9 +463,6 @@ object Main {
|
||||
flag("--single-threaded").action(c =>
|
||||
c.changeFlag(CompilationFlag.SingleThreaded, true)
|
||||
).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 => {
|
||||
println("millfork version " + BuildInfo.version)
|
||||
|
@ -21,6 +21,8 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
var lastLabel = ""
|
||||
protected val log: Logger = options.log
|
||||
|
||||
def allowIntelHexAtomsInAssembly: Boolean
|
||||
|
||||
def toAst: Parsed[Program] = program.parse(input + "\n\n\n")
|
||||
|
||||
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 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 atomWithIntel: P[Expression] = P(position() ~ (literalAtomWithIntel | variableAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
|
||||
|
||||
val globalVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(true)
|
||||
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
|
||||
|
||||
@ -99,8 +105,8 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
flags <- variableFlags ~ HWS
|
||||
typ <- identifier ~ SWS
|
||||
name <- identifier ~/ HWS ~/ Pass
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1)).?.opaque("<address>") ~ HWS
|
||||
initialValue <- ("=" ~/ HWS ~/ mfExpression(1)).? ~ HWS
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("<address>") ~ HWS
|
||||
initialValue <- ("=" ~/ HWS ~/ mfExpression(1, false)).? ~ HWS
|
||||
_ <- &(EOL) ~/ ""
|
||||
} yield {
|
||||
Seq(VariableDeclarationStatement(name, typ,
|
||||
@ -122,9 +128,9 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
}
|
||||
|
||||
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)
|
||||
("<" ~/ HWS ~ mfExpression(mathLevel, allowIntelHexAtomsInAssembly)).map(e => HalfWordExpression(e, hiByte = false)) |
|
||||
(">" ~/ HWS ~ mfExpression(mathLevel, allowIntelHexAtomsInAssembly)).map(e => HalfWordExpression(e, hiByte = true)) |
|
||||
mfExpression(mathLevel, allowIntelHexAtomsInAssembly)
|
||||
)).map { case (p, e) => e.pos(p) }
|
||||
|
||||
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 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 {
|
||||
_ <- "@" ~/ HWS
|
||||
@ -176,10 +182,10 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
|
||||
def arrayLoopContents: P[ArrayContents] = for {
|
||||
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
|
||||
start <- mfExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass
|
||||
start <- mfExpression(nonStatementLevel, false) ~ HWS ~ "," ~/ HWS ~/ Pass
|
||||
pos <- position("loop direction")
|
||||
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
|
||||
end <- mfExpression(nonStatementLevel)
|
||||
end <- mfExpression(nonStatementLevel, false)
|
||||
body <- AWS ~ arrayContents
|
||||
} yield {
|
||||
val fixedDirection = direction match {
|
||||
@ -208,21 +214,27 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
p <- position()
|
||||
bank <- bankDeclaration
|
||||
name <- "array" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS
|
||||
length <- ("[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~ "]").? ~ HWS
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1)).? ~/ HWS
|
||||
length <- ("[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~ "]").? ~ HWS
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS
|
||||
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
|
||||
} 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)
|
||||
|
||||
def inner: P[SeparatedList[Expression, String]] = {
|
||||
for {
|
||||
head <- tightMfExpression ~/ HWS
|
||||
head <- tightMfExpression(allowIntelHex) ~/ HWS
|
||||
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)))
|
||||
} yield {
|
||||
@ -237,7 +249,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
xs.separators.distinct match {
|
||||
case Nil =>
|
||||
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)
|
||||
case List("+") | List("-") | List("+", "-") | List("-", "+") =>
|
||||
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) =>
|
||||
FunctionCallExpression(op, xs.items.map(value => p(value, level + 1)))
|
||||
case _ =>
|
||||
log.error("Too many different operators")
|
||||
log.error("Too many different operators", xs.head.head.position)
|
||||
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))
|
||||
|
||||
|
||||
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 {
|
||||
p <- position()
|
||||
array <- identifier
|
||||
index <- HWS ~ "[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~/ "]"
|
||||
index <- HWS ~ "[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~/ "]"
|
||||
} yield IndexedExpression(array, index).pos(p)
|
||||
|
||||
def functionCall: P[FunctionCallExpression] = for {
|
||||
def functionCall(allowIntelHex: Boolean): P[FunctionCallExpression] = for {
|
||||
p <- position()
|
||||
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)
|
||||
|
||||
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]] =
|
||||
(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))
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
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 Some((_, Seq())) => DefaultReturnDispatchLabel(None, None)
|
||||
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, _)) =>
|
||||
log.error("Invalid default branch declaration", Some(pos))
|
||||
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 {
|
||||
pos <- position()
|
||||
l <- dispatchLabel ~/ HWS ~/ "@" ~/ HWS
|
||||
f <- tightMfExpressionButNotCall ~/ HWS
|
||||
parameters <- ("(" ~/ position("dispatch actual parameters") ~ AWS ~/ mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
|
||||
f <- tightMfExpressionButNotCall(false) ~/ HWS
|
||||
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)
|
||||
|
||||
val dispatchStatementBody: P[Seq[ExecutableStatement]] = for {
|
||||
indexer <- "[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~/ AWS ~/ "]" ~/ AWS
|
||||
indexer <- "[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~/ AWS ~/ "]" ~/ AWS
|
||||
_ <- position("dispatch statement body")
|
||||
parameters <- ("(" ~/ position("dispatch parameters") ~ AWS ~/ mfLhsExpression.rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
|
||||
_ <- AWS ~/ position("dispatch statement body") ~/ "{" ~/ AWS
|
||||
@ -340,24 +352,24 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
_ <- AWS ~/ "}"
|
||||
} 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 {
|
||||
condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel)
|
||||
condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel, false)
|
||||
thenBranch <- AWS ~/ executableStatements
|
||||
elseBranch <- (AWS ~ "else" ~/ AWS ~/ (ifStatement | executableStatements)).?
|
||||
} yield Seq(IfStatement(condition, thenBranch.toList, elseBranch.getOrElse(Nil).toList))
|
||||
|
||||
def whileStatement: P[Seq[ExecutableStatement]] = for {
|
||||
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel)
|
||||
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel, false)
|
||||
body <- AWS ~ executableStatements
|
||||
} yield Seq(WhileStatement(condition, body.toList, Nil))
|
||||
|
||||
def forStatement: P[Seq[ExecutableStatement]] = for {
|
||||
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
|
||||
end <- mfExpression(nonStatementLevel)
|
||||
end <- mfExpression(nonStatementLevel, false)
|
||||
body <- AWS ~ executableStatements
|
||||
} 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
|
||||
def doWhileStatement: P[Seq[ExecutableStatement]] = for {
|
||||
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))
|
||||
|
||||
val functionDefinition: P[Seq[DeclarationStatement]] = for {
|
||||
@ -377,7 +389,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
if !InvalidReturnTypes(returnType)
|
||||
name <- identifier ~ HWS
|
||||
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
|
||||
} yield {
|
||||
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 {
|
||||
name <- identifier ~/ HWS
|
||||
value <- ("=" ~/ HWS ~/ mfExpression(1)).? ~ HWS
|
||||
value <- ("=" ~/ HWS ~/ mfExpression(1, false)).? ~ HWS
|
||||
} yield name -> value
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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] =
|
||||
for {
|
||||
minus <- "-".!.?
|
||||
|
@ -14,6 +14,8 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
|
||||
|
||||
import MfParser._
|
||||
|
||||
def allowIntelHexAtomsInAssembly: Boolean = false
|
||||
|
||||
// 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))
|
||||
|
||||
@ -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
|
||||
|
||||
|
@ -4,7 +4,8 @@ import java.util.Locale
|
||||
|
||||
import fastparse.all.{parserApi, _}
|
||||
import fastparse.core
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.z80
|
||||
import millfork.{CompilationFlag, CompilationOptions, node}
|
||||
import millfork.assembly.z80.{ZOpcode, _}
|
||||
import millfork.env.{ByZRegister, Constant, ParamPassingConvention}
|
||||
import millfork.error.ConsoleLogger
|
||||
@ -20,12 +21,10 @@ case class Z80Parser(filename: String,
|
||||
featureConstants: Map[String, Long],
|
||||
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._
|
||||
|
||||
def allowIntelHexAtomsInAssembly: Boolean = useIntelSyntax
|
||||
|
||||
private val zero = LiteralExpression(0, 1)
|
||||
|
||||
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
|
||||
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(
|
||||
"A" -> ZRegister.A, "a" -> ZRegister.A,
|
||||
@ -70,6 +69,25 @@ case class Z80Parser(filename: String,
|
||||
"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 {
|
||||
case (VariableExpression("R" | "r"), false) if allowRI => (ZRegister.R, 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))
|
||||
}
|
||||
|
||||
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))
|
||||
@ -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
|
||||
|
||||
|
@ -8,9 +8,10 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
*/
|
||||
class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
|
||||
test("Common I80 instructions") {
|
||||
test("Common I80 instructions (Zilog syntax)") {
|
||||
EmuUnoptimizedIntel8080Run(
|
||||
"""
|
||||
| #pragma zilog_syntax
|
||||
| asm void main () {
|
||||
| ret
|
||||
|
|
||||
@ -58,19 +59,19 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
| ld l,$2e
|
||||
| cpl
|
||||
|
|
||||
| ld hl,$2121
|
||||
| ld sp,$3131
|
||||
| ld ($fffe),a
|
||||
| inc sp
|
||||
| inc (hl)
|
||||
| dec (hl)
|
||||
| ld h,$26
|
||||
| ld (hl),$36
|
||||
| scf
|
||||
| add hl,sp
|
||||
| ld a,($fffe)
|
||||
| dec sp
|
||||
| inc a
|
||||
| dec a
|
||||
| ld l,$2e
|
||||
| ld a,$3e
|
||||
| ccf
|
||||
|
|
||||
| ld b,b
|
||||
@ -273,10 +274,277 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
| }
|
||||
""".stripMargin)
|
||||
}
|
||||
|
||||
test("Intel 8080 instructions") {
|
||||
test("Common I80 instructions (Intel syntax)") {
|
||||
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 () {
|
||||
| ret
|
||||
| ld ($fffe),hl
|
||||
@ -302,6 +570,35 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
""".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") {
|
||||
EmuUnoptimizedZ80Run(
|
||||
"""
|
||||
|
@ -47,8 +47,8 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
|
||||
log.setSource(Some(effectiveSource.lines.toIndexedSeq))
|
||||
val PreprocessingResult(preprocessedSource, features, pragmas) = Preprocessor.preprocessForTest(options, effectiveSource)
|
||||
val parserF = Z80Parser("", preprocessedSource, "", options, features, false)
|
||||
//if (pragmas.contains("intel_syntax")) true else if (pragmas.contains("zilog_syntax")) false else options.flag(CompilationFlag.UseIntelSyntax))
|
||||
// tests use Intel syntax only when forced to:
|
||||
val parserF = Z80Parser("", preprocessedSource, "", options, features, pragmas.contains("intel_syntax"))
|
||||
parserF.toAst match {
|
||||
case Success(unoptimized, _) =>
|
||||
log.assertNoErrors("Parse failed")
|
||||
@ -158,7 +158,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
println(f.extra.toString)
|
||||
println(f.lastParser.toString)
|
||||
log.error("Syntax error", Some(parserF.lastPosition))
|
||||
???
|
||||
fail("Parsing error")
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user