mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-16 16:31:04 +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`.
|
* `-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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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`.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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 <- "-".!.?
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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(
|
||||||
"""
|
"""
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user