From fab1cafec367a761d2453220b922351661eb9655 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Fri, 3 Aug 2018 13:23:37 +0200 Subject: [PATCH] Z80: Intel syntax support --- docs/api/command-line.md | 15 +- docs/api/getting-started.md | 2 +- docs/lang/literals.md | 3 + docs/lang/preprocessor.md | 8 + include/cpm_bdos.mfk | 20 +- include/stdio_zxspectrum.mfk | 2 + include/stdlib_i80.mfk | 2 + include/zxspectrum.mfk | 2 + .../scala/millfork/CompilationOptions.scala | 3 +- src/main/scala/millfork/Main.scala | 15 +- src/main/scala/millfork/parser/MfParser.scala | 96 ++++-- .../scala/millfork/parser/MosParser.scala | 4 +- .../scala/millfork/parser/Z80Parser.scala | 175 +++++++++- .../millfork/test/Z80AssemblySuite.scala | 309 +++++++++++++++++- .../scala/millfork/test/emu/EmuZ80Run.scala | 6 +- 15 files changed, 592 insertions(+), 70 deletions(-) diff --git a/docs/api/command-line.md b/docs/api/command-line.md index 45db73f3..f98c9999 100644 --- a/docs/api/command-line.md +++ b/docs/api/command-line.md @@ -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 ;` – 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 =` – 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. diff --git a/docs/api/getting-started.md b/docs/api/getting-started.md index 76bfbcff..44facdc9 100644 --- a/docs/api/getting-started.md +++ b/docs/api/getting-started.md @@ -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 diff --git a/docs/lang/literals.md b/docs/lang/literals.md index 672e6186..7e5660d4 100644 --- a/docs/lang/literals.md +++ b/docs/lang/literals.md @@ -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`. diff --git a/docs/lang/preprocessor.md b/docs/lang/preprocessor.md index 2339996a..d7cbfc3d 100644 --- a/docs/lang/preprocessor.md +++ b/docs/lang/preprocessor.md @@ -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 diff --git a/include/cpm_bdos.mfk b/include/cpm_bdos.mfk index 98df0258..d43da432 100644 --- a/include/cpm_bdos.mfk +++ b/include/cpm_bdos.mfk @@ -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 } diff --git a/include/stdio_zxspectrum.mfk b/include/stdio_zxspectrum.mfk index 64e5c489..8eca6733 100644 --- a/include/stdio_zxspectrum.mfk +++ b/include/stdio_zxspectrum.mfk @@ -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) { diff --git a/include/stdlib_i80.mfk b/include/stdlib_i80.mfk index 8f42a306..6dadc1aa 100644 --- a/include/stdlib_i80.mfk +++ b/include/stdlib_i80.mfk @@ -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 } diff --git a/include/zxspectrum.mfk b/include/zxspectrum.mfk index c9f2bf1c..9b4c2526 100644 --- a/include/zxspectrum.mfk +++ b/include/zxspectrum.mfk @@ -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 diff --git a/src/main/scala/millfork/CompilationOptions.scala b/src/main/scala/millfork/CompilationOptions.scala index a56c93e2..9c384ad2 100644 --- a/src/main/scala/millfork/CompilationOptions.scala +++ b/src/main/scala/millfork/CompilationOptions.scala @@ -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, diff --git a/src/main/scala/millfork/Main.scala b/src/main/scala/millfork/Main.scala index 25ecfd23..424b9342 100644 --- a/src/main/scala/millfork/Main.scala +++ b/src/main/scala/millfork/Main.scala @@ -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) diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 28ea0bc0..74c8423d 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -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("
") ~ HWS - initialValue <- ("=" ~/ HWS ~/ mfExpression(1)).? ~ HWS + addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("
") ~ 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("
") ~/ AWS + addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("
") ~/ 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("") + _ <- 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 <- "-".!.? diff --git a/src/main/scala/millfork/parser/MosParser.scala b/src/main/scala/millfork/parser/MosParser.scala index 36e5902c..ec878408 100644 --- a/src/main/scala/millfork/parser/MosParser.scala +++ b/src/main/scala/millfork/parser/MosParser.scala @@ -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 diff --git a/src/main/scala/millfork/parser/Z80Parser.scala b/src/main/scala/millfork/parser/Z80Parser.scala index d88228f1..313ba52a 100644 --- a/src/main/scala/millfork/parser/Z80Parser.scala +++ b/src/main/scala/millfork/parser/Z80Parser.scala @@ -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 diff --git a/src/test/scala/millfork/test/Z80AssemblySuite.scala b/src/test/scala/millfork/test/Z80AssemblySuite.scala index ba09f04e..08dc32dc 100644 --- a/src/test/scala/millfork/test/Z80AssemblySuite.scala +++ b/src/test/scala/millfork/test/Z80AssemblySuite.scala @@ -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( """ diff --git a/src/test/scala/millfork/test/emu/EmuZ80Run.scala b/src/test/scala/millfork/test/emu/EmuZ80Run.scala index 1d5d8833..306ab862 100644 --- a/src/test/scala/millfork/test/emu/EmuZ80Run.scala +++ b/src/test/scala/millfork/test/emu/EmuZ80Run.scala @@ -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") } }