From c2fe8d0527a7aa8e297c80a0809b5ff0044f7166 Mon Sep 17 00:00:00 2001 From: Marco Maccaferri Date: Tue, 18 Dec 2018 04:48:29 +0100 Subject: [PATCH] Added Glass Z80 compiler --- LICENSE.glass | 22 + NOTICE.glass | 5 + build.xml | 2 + src-tests/nl/grauw/glass/ParserTest.java | 180 ++++ src-tests/nl/grauw/glass/SourceTest.java | 890 ++++++++++++++++ .../expressions/ExpressionBuilderTest.java | 255 +++++ .../glass/expressions/ExpressionTest.java | 277 +++++ .../glass/instructions/InstructionTest.java | 972 ++++++++++++++++++ src/nl/grauw/glass/Assembler.java | 75 ++ src/nl/grauw/glass/AssemblyException.java | 80 ++ src/nl/grauw/glass/GlobalScope.java | 106 ++ src/nl/grauw/glass/Line.java | 143 +++ src/nl/grauw/glass/LineBuilder.java | 55 + src/nl/grauw/glass/ParameterScope.java | 39 + src/nl/grauw/glass/Parser.java | 666 ++++++++++++ src/nl/grauw/glass/Scope.java | 119 +++ src/nl/grauw/glass/Source.java | 110 ++ src/nl/grauw/glass/SourceBuilder.java | 206 ++++ src/nl/grauw/glass/directives/Directive.java | 13 + src/nl/grauw/glass/directives/Ds.java | 17 + src/nl/grauw/glass/directives/Equ.java | 16 + src/nl/grauw/glass/directives/If.java | 26 + src/nl/grauw/glass/directives/Incbin.java | 25 + src/nl/grauw/glass/directives/Include.java | 5 + .../grauw/glass/directives/Instruction.java | 5 + src/nl/grauw/glass/directives/Irp.java | 21 + src/nl/grauw/glass/directives/Macro.java | 31 + src/nl/grauw/glass/directives/Proc.java | 21 + src/nl/grauw/glass/directives/Rept.java | 21 + src/nl/grauw/glass/directives/Section.java | 24 + src/nl/grauw/glass/expressions/Add.java | 51 + src/nl/grauw/glass/expressions/And.java | 24 + .../grauw/glass/expressions/Annotation.java | 34 + .../glass/expressions/BinaryOperator.java | 36 + .../glass/expressions/CharacterLiteral.java | 48 + .../grauw/glass/expressions/Complement.java | 24 + src/nl/grauw/glass/expressions/Context.java | 11 + .../glass/expressions/ContextLiteral.java | 48 + src/nl/grauw/glass/expressions/Divide.java | 35 + src/nl/grauw/glass/expressions/Equals.java | 24 + .../expressions/EvaluationException.java | 16 + .../grauw/glass/expressions/Expression.java | 117 +++ .../glass/expressions/ExpressionBuilder.java | 390 +++++++ src/nl/grauw/glass/expressions/Flag.java | 81 ++ .../glass/expressions/FlagOrRegister.java | 62 ++ .../glass/expressions/GreaterOrEquals.java | 24 + .../grauw/glass/expressions/GreaterThan.java | 24 + src/nl/grauw/glass/expressions/Group.java | 38 + .../grauw/glass/expressions/Identifier.java | 55 + src/nl/grauw/glass/expressions/IfElse.java | 76 ++ src/nl/grauw/glass/expressions/Index.java | 42 + .../grauw/glass/expressions/Instruction.java | 54 + .../glass/expressions/IntegerLiteral.java | 38 + .../grauw/glass/expressions/LessOrEquals.java | 24 + src/nl/grauw/glass/expressions/LessThan.java | 24 + src/nl/grauw/glass/expressions/Literal.java | 5 + .../grauw/glass/expressions/LogicalAnd.java | 25 + src/nl/grauw/glass/expressions/LogicalOr.java | 25 + src/nl/grauw/glass/expressions/Member.java | 42 + src/nl/grauw/glass/expressions/Modulo.java | 35 + src/nl/grauw/glass/expressions/Multiply.java | 32 + src/nl/grauw/glass/expressions/Negative.java | 24 + src/nl/grauw/glass/expressions/Not.java | 53 + src/nl/grauw/glass/expressions/NotEquals.java | 24 + src/nl/grauw/glass/expressions/Or.java | 24 + .../grauw/glass/expressions/Passthrough.java | 82 ++ src/nl/grauw/glass/expressions/Positive.java | 24 + src/nl/grauw/glass/expressions/Register.java | 186 ++++ src/nl/grauw/glass/expressions/Schema.java | 154 +++ .../grauw/glass/expressions/SchemaType.java | 5 + .../glass/expressions/SectionContext.java | 9 + .../expressions/SectionContextLiteral.java | 22 + src/nl/grauw/glass/expressions/Sequence.java | 57 + src/nl/grauw/glass/expressions/ShiftLeft.java | 24 + .../grauw/glass/expressions/ShiftRight.java | 24 + .../glass/expressions/StringLiteral.java | 63 ++ src/nl/grauw/glass/expressions/Subtract.java | 51 + .../glass/expressions/UnaryOperator.java | 30 + src/nl/grauw/glass/expressions/Xor.java | 24 + src/nl/grauw/glass/instructions/Adc.java | 91 ++ src/nl/grauw/glass/instructions/Add.java | 96 ++ src/nl/grauw/glass/instructions/And.java | 66 ++ .../glass/instructions/ArgumentException.java | 16 + src/nl/grauw/glass/instructions/Bit.java | 46 + src/nl/grauw/glass/instructions/Call.java | 68 ++ src/nl/grauw/glass/instructions/Ccf.java | 36 + src/nl/grauw/glass/instructions/Cp.java | 66 ++ src/nl/grauw/glass/instructions/Cpd.java | 36 + src/nl/grauw/glass/instructions/Cpdr.java | 36 + src/nl/grauw/glass/instructions/Cpi.java | 36 + src/nl/grauw/glass/instructions/Cpir.java | 36 + src/nl/grauw/glass/instructions/Cpl.java | 36 + src/nl/grauw/glass/instructions/Daa.java | 36 + src/nl/grauw/glass/instructions/Db.java | 41 + src/nl/grauw/glass/instructions/Dd.java | 45 + src/nl/grauw/glass/instructions/Dec.java | 67 ++ src/nl/grauw/glass/instructions/Di.java | 36 + src/nl/grauw/glass/instructions/Djnz.java | 42 + src/nl/grauw/glass/instructions/Ds.java | 105 ++ src/nl/grauw/glass/instructions/Dw.java | 43 + src/nl/grauw/glass/instructions/Ei.java | 36 + src/nl/grauw/glass/instructions/Else.java | 18 + src/nl/grauw/glass/instructions/Empty.java | 35 + src/nl/grauw/glass/instructions/End.java | 18 + src/nl/grauw/glass/instructions/Endif.java | 18 + src/nl/grauw/glass/instructions/Endm.java | 18 + src/nl/grauw/glass/instructions/Endp.java | 18 + src/nl/grauw/glass/instructions/Ends.java | 18 + src/nl/grauw/glass/instructions/Equ.java | 18 + src/nl/grauw/glass/instructions/Error.java | 51 + src/nl/grauw/glass/instructions/Ex.java | 83 ++ src/nl/grauw/glass/instructions/Exx.java | 36 + src/nl/grauw/glass/instructions/Halt.java | 36 + src/nl/grauw/glass/instructions/If.java | 77 ++ src/nl/grauw/glass/instructions/Im.java | 47 + src/nl/grauw/glass/instructions/In.java | 68 ++ src/nl/grauw/glass/instructions/Inc.java | 67 ++ src/nl/grauw/glass/instructions/Incbin.java | 98 ++ src/nl/grauw/glass/instructions/Include.java | 24 + src/nl/grauw/glass/instructions/Ind.java | 36 + src/nl/grauw/glass/instructions/Indr.java | 36 + src/nl/grauw/glass/instructions/Ini.java | 36 + src/nl/grauw/glass/instructions/Inir.java | 36 + .../instructions/InstructionFactory.java | 20 + .../glass/instructions/InstructionObject.java | 102 ++ src/nl/grauw/glass/instructions/Irp.java | 51 + src/nl/grauw/glass/instructions/Jp.java | 94 ++ src/nl/grauw/glass/instructions/Jr.java | 74 ++ src/nl/grauw/glass/instructions/Ld.java | 406 ++++++++ src/nl/grauw/glass/instructions/Ldd.java | 36 + src/nl/grauw/glass/instructions/Lddr.java | 36 + src/nl/grauw/glass/instructions/Ldi.java | 36 + src/nl/grauw/glass/instructions/Ldir.java | 36 + src/nl/grauw/glass/instructions/Macro.java | 75 ++ .../glass/instructions/MacroInstruction.java | 50 + src/nl/grauw/glass/instructions/Mulub.java | 39 + src/nl/grauw/glass/instructions/Muluw.java | 39 + src/nl/grauw/glass/instructions/Neg.java | 36 + src/nl/grauw/glass/instructions/Nop.java | 36 + src/nl/grauw/glass/instructions/Or.java | 66 ++ src/nl/grauw/glass/instructions/Org.java | 46 + src/nl/grauw/glass/instructions/Otdr.java | 37 + src/nl/grauw/glass/instructions/Otir.java | 36 + src/nl/grauw/glass/instructions/Out.java | 64 ++ src/nl/grauw/glass/instructions/Outd.java | 36 + src/nl/grauw/glass/instructions/Outi.java | 36 + src/nl/grauw/glass/instructions/Pop.java | 41 + src/nl/grauw/glass/instructions/Proc.java | 40 + src/nl/grauw/glass/instructions/Push.java | 41 + src/nl/grauw/glass/instructions/Rept.java | 71 ++ src/nl/grauw/glass/instructions/Res.java | 46 + src/nl/grauw/glass/instructions/Ret.java | 61 ++ src/nl/grauw/glass/instructions/Reti.java | 36 + src/nl/grauw/glass/instructions/Retn.java | 36 + src/nl/grauw/glass/instructions/Rl.java | 41 + src/nl/grauw/glass/instructions/Rla.java | 36 + src/nl/grauw/glass/instructions/Rlc.java | 41 + src/nl/grauw/glass/instructions/Rlca.java | 36 + src/nl/grauw/glass/instructions/Rld.java | 36 + src/nl/grauw/glass/instructions/Rr.java | 41 + src/nl/grauw/glass/instructions/Rra.java | 36 + src/nl/grauw/glass/instructions/Rrc.java | 41 + src/nl/grauw/glass/instructions/Rrca.java | 36 + src/nl/grauw/glass/instructions/Rrd.java | 36 + src/nl/grauw/glass/instructions/Rst.java | 42 + src/nl/grauw/glass/instructions/Sbc.java | 91 ++ src/nl/grauw/glass/instructions/Scf.java | 36 + src/nl/grauw/glass/instructions/Section.java | 46 + src/nl/grauw/glass/instructions/Set.java | 46 + src/nl/grauw/glass/instructions/Sla.java | 41 + src/nl/grauw/glass/instructions/Sra.java | 41 + src/nl/grauw/glass/instructions/Srl.java | 41 + src/nl/grauw/glass/instructions/Sub.java | 66 ++ src/nl/grauw/glass/instructions/Warning.java | 39 + src/nl/grauw/glass/instructions/Xor.java | 66 ++ 175 files changed, 11673 insertions(+) create mode 100644 LICENSE.glass create mode 100644 NOTICE.glass create mode 100644 src-tests/nl/grauw/glass/ParserTest.java create mode 100644 src-tests/nl/grauw/glass/SourceTest.java create mode 100644 src-tests/nl/grauw/glass/expressions/ExpressionBuilderTest.java create mode 100644 src-tests/nl/grauw/glass/expressions/ExpressionTest.java create mode 100644 src-tests/nl/grauw/glass/instructions/InstructionTest.java create mode 100644 src/nl/grauw/glass/Assembler.java create mode 100644 src/nl/grauw/glass/AssemblyException.java create mode 100644 src/nl/grauw/glass/GlobalScope.java create mode 100644 src/nl/grauw/glass/Line.java create mode 100644 src/nl/grauw/glass/LineBuilder.java create mode 100644 src/nl/grauw/glass/ParameterScope.java create mode 100644 src/nl/grauw/glass/Parser.java create mode 100644 src/nl/grauw/glass/Scope.java create mode 100644 src/nl/grauw/glass/Source.java create mode 100644 src/nl/grauw/glass/SourceBuilder.java create mode 100644 src/nl/grauw/glass/directives/Directive.java create mode 100644 src/nl/grauw/glass/directives/Ds.java create mode 100644 src/nl/grauw/glass/directives/Equ.java create mode 100644 src/nl/grauw/glass/directives/If.java create mode 100644 src/nl/grauw/glass/directives/Incbin.java create mode 100644 src/nl/grauw/glass/directives/Include.java create mode 100644 src/nl/grauw/glass/directives/Instruction.java create mode 100644 src/nl/grauw/glass/directives/Irp.java create mode 100644 src/nl/grauw/glass/directives/Macro.java create mode 100644 src/nl/grauw/glass/directives/Proc.java create mode 100644 src/nl/grauw/glass/directives/Rept.java create mode 100644 src/nl/grauw/glass/directives/Section.java create mode 100644 src/nl/grauw/glass/expressions/Add.java create mode 100644 src/nl/grauw/glass/expressions/And.java create mode 100644 src/nl/grauw/glass/expressions/Annotation.java create mode 100644 src/nl/grauw/glass/expressions/BinaryOperator.java create mode 100644 src/nl/grauw/glass/expressions/CharacterLiteral.java create mode 100644 src/nl/grauw/glass/expressions/Complement.java create mode 100644 src/nl/grauw/glass/expressions/Context.java create mode 100644 src/nl/grauw/glass/expressions/ContextLiteral.java create mode 100644 src/nl/grauw/glass/expressions/Divide.java create mode 100644 src/nl/grauw/glass/expressions/Equals.java create mode 100644 src/nl/grauw/glass/expressions/EvaluationException.java create mode 100644 src/nl/grauw/glass/expressions/Expression.java create mode 100644 src/nl/grauw/glass/expressions/ExpressionBuilder.java create mode 100644 src/nl/grauw/glass/expressions/Flag.java create mode 100644 src/nl/grauw/glass/expressions/FlagOrRegister.java create mode 100644 src/nl/grauw/glass/expressions/GreaterOrEquals.java create mode 100644 src/nl/grauw/glass/expressions/GreaterThan.java create mode 100644 src/nl/grauw/glass/expressions/Group.java create mode 100644 src/nl/grauw/glass/expressions/Identifier.java create mode 100644 src/nl/grauw/glass/expressions/IfElse.java create mode 100644 src/nl/grauw/glass/expressions/Index.java create mode 100644 src/nl/grauw/glass/expressions/Instruction.java create mode 100644 src/nl/grauw/glass/expressions/IntegerLiteral.java create mode 100644 src/nl/grauw/glass/expressions/LessOrEquals.java create mode 100644 src/nl/grauw/glass/expressions/LessThan.java create mode 100644 src/nl/grauw/glass/expressions/Literal.java create mode 100644 src/nl/grauw/glass/expressions/LogicalAnd.java create mode 100644 src/nl/grauw/glass/expressions/LogicalOr.java create mode 100644 src/nl/grauw/glass/expressions/Member.java create mode 100644 src/nl/grauw/glass/expressions/Modulo.java create mode 100644 src/nl/grauw/glass/expressions/Multiply.java create mode 100644 src/nl/grauw/glass/expressions/Negative.java create mode 100644 src/nl/grauw/glass/expressions/Not.java create mode 100644 src/nl/grauw/glass/expressions/NotEquals.java create mode 100644 src/nl/grauw/glass/expressions/Or.java create mode 100644 src/nl/grauw/glass/expressions/Passthrough.java create mode 100644 src/nl/grauw/glass/expressions/Positive.java create mode 100644 src/nl/grauw/glass/expressions/Register.java create mode 100644 src/nl/grauw/glass/expressions/Schema.java create mode 100644 src/nl/grauw/glass/expressions/SchemaType.java create mode 100644 src/nl/grauw/glass/expressions/SectionContext.java create mode 100644 src/nl/grauw/glass/expressions/SectionContextLiteral.java create mode 100644 src/nl/grauw/glass/expressions/Sequence.java create mode 100644 src/nl/grauw/glass/expressions/ShiftLeft.java create mode 100644 src/nl/grauw/glass/expressions/ShiftRight.java create mode 100644 src/nl/grauw/glass/expressions/StringLiteral.java create mode 100644 src/nl/grauw/glass/expressions/Subtract.java create mode 100644 src/nl/grauw/glass/expressions/UnaryOperator.java create mode 100644 src/nl/grauw/glass/expressions/Xor.java create mode 100644 src/nl/grauw/glass/instructions/Adc.java create mode 100644 src/nl/grauw/glass/instructions/Add.java create mode 100644 src/nl/grauw/glass/instructions/And.java create mode 100644 src/nl/grauw/glass/instructions/ArgumentException.java create mode 100644 src/nl/grauw/glass/instructions/Bit.java create mode 100644 src/nl/grauw/glass/instructions/Call.java create mode 100644 src/nl/grauw/glass/instructions/Ccf.java create mode 100644 src/nl/grauw/glass/instructions/Cp.java create mode 100644 src/nl/grauw/glass/instructions/Cpd.java create mode 100644 src/nl/grauw/glass/instructions/Cpdr.java create mode 100644 src/nl/grauw/glass/instructions/Cpi.java create mode 100644 src/nl/grauw/glass/instructions/Cpir.java create mode 100644 src/nl/grauw/glass/instructions/Cpl.java create mode 100644 src/nl/grauw/glass/instructions/Daa.java create mode 100644 src/nl/grauw/glass/instructions/Db.java create mode 100644 src/nl/grauw/glass/instructions/Dd.java create mode 100644 src/nl/grauw/glass/instructions/Dec.java create mode 100644 src/nl/grauw/glass/instructions/Di.java create mode 100644 src/nl/grauw/glass/instructions/Djnz.java create mode 100644 src/nl/grauw/glass/instructions/Ds.java create mode 100644 src/nl/grauw/glass/instructions/Dw.java create mode 100644 src/nl/grauw/glass/instructions/Ei.java create mode 100644 src/nl/grauw/glass/instructions/Else.java create mode 100644 src/nl/grauw/glass/instructions/Empty.java create mode 100644 src/nl/grauw/glass/instructions/End.java create mode 100644 src/nl/grauw/glass/instructions/Endif.java create mode 100644 src/nl/grauw/glass/instructions/Endm.java create mode 100644 src/nl/grauw/glass/instructions/Endp.java create mode 100644 src/nl/grauw/glass/instructions/Ends.java create mode 100644 src/nl/grauw/glass/instructions/Equ.java create mode 100644 src/nl/grauw/glass/instructions/Error.java create mode 100644 src/nl/grauw/glass/instructions/Ex.java create mode 100644 src/nl/grauw/glass/instructions/Exx.java create mode 100644 src/nl/grauw/glass/instructions/Halt.java create mode 100644 src/nl/grauw/glass/instructions/If.java create mode 100644 src/nl/grauw/glass/instructions/Im.java create mode 100644 src/nl/grauw/glass/instructions/In.java create mode 100644 src/nl/grauw/glass/instructions/Inc.java create mode 100644 src/nl/grauw/glass/instructions/Incbin.java create mode 100644 src/nl/grauw/glass/instructions/Include.java create mode 100644 src/nl/grauw/glass/instructions/Ind.java create mode 100644 src/nl/grauw/glass/instructions/Indr.java create mode 100644 src/nl/grauw/glass/instructions/Ini.java create mode 100644 src/nl/grauw/glass/instructions/Inir.java create mode 100644 src/nl/grauw/glass/instructions/InstructionFactory.java create mode 100644 src/nl/grauw/glass/instructions/InstructionObject.java create mode 100644 src/nl/grauw/glass/instructions/Irp.java create mode 100644 src/nl/grauw/glass/instructions/Jp.java create mode 100644 src/nl/grauw/glass/instructions/Jr.java create mode 100644 src/nl/grauw/glass/instructions/Ld.java create mode 100644 src/nl/grauw/glass/instructions/Ldd.java create mode 100644 src/nl/grauw/glass/instructions/Lddr.java create mode 100644 src/nl/grauw/glass/instructions/Ldi.java create mode 100644 src/nl/grauw/glass/instructions/Ldir.java create mode 100644 src/nl/grauw/glass/instructions/Macro.java create mode 100644 src/nl/grauw/glass/instructions/MacroInstruction.java create mode 100644 src/nl/grauw/glass/instructions/Mulub.java create mode 100644 src/nl/grauw/glass/instructions/Muluw.java create mode 100644 src/nl/grauw/glass/instructions/Neg.java create mode 100644 src/nl/grauw/glass/instructions/Nop.java create mode 100644 src/nl/grauw/glass/instructions/Or.java create mode 100644 src/nl/grauw/glass/instructions/Org.java create mode 100644 src/nl/grauw/glass/instructions/Otdr.java create mode 100644 src/nl/grauw/glass/instructions/Otir.java create mode 100644 src/nl/grauw/glass/instructions/Out.java create mode 100644 src/nl/grauw/glass/instructions/Outd.java create mode 100644 src/nl/grauw/glass/instructions/Outi.java create mode 100644 src/nl/grauw/glass/instructions/Pop.java create mode 100644 src/nl/grauw/glass/instructions/Proc.java create mode 100644 src/nl/grauw/glass/instructions/Push.java create mode 100644 src/nl/grauw/glass/instructions/Rept.java create mode 100644 src/nl/grauw/glass/instructions/Res.java create mode 100644 src/nl/grauw/glass/instructions/Ret.java create mode 100644 src/nl/grauw/glass/instructions/Reti.java create mode 100644 src/nl/grauw/glass/instructions/Retn.java create mode 100644 src/nl/grauw/glass/instructions/Rl.java create mode 100644 src/nl/grauw/glass/instructions/Rla.java create mode 100644 src/nl/grauw/glass/instructions/Rlc.java create mode 100644 src/nl/grauw/glass/instructions/Rlca.java create mode 100644 src/nl/grauw/glass/instructions/Rld.java create mode 100644 src/nl/grauw/glass/instructions/Rr.java create mode 100644 src/nl/grauw/glass/instructions/Rra.java create mode 100644 src/nl/grauw/glass/instructions/Rrc.java create mode 100644 src/nl/grauw/glass/instructions/Rrca.java create mode 100644 src/nl/grauw/glass/instructions/Rrd.java create mode 100644 src/nl/grauw/glass/instructions/Rst.java create mode 100644 src/nl/grauw/glass/instructions/Sbc.java create mode 100644 src/nl/grauw/glass/instructions/Scf.java create mode 100644 src/nl/grauw/glass/instructions/Section.java create mode 100644 src/nl/grauw/glass/instructions/Set.java create mode 100644 src/nl/grauw/glass/instructions/Sla.java create mode 100644 src/nl/grauw/glass/instructions/Sra.java create mode 100644 src/nl/grauw/glass/instructions/Srl.java create mode 100644 src/nl/grauw/glass/instructions/Sub.java create mode 100644 src/nl/grauw/glass/instructions/Warning.java create mode 100644 src/nl/grauw/glass/instructions/Xor.java diff --git a/LICENSE.glass b/LICENSE.glass new file mode 100644 index 0000000..4b0034e --- /dev/null +++ b/LICENSE.glass @@ -0,0 +1,22 @@ +Copyright (c) 2014, Laurens Holst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/NOTICE.glass b/NOTICE.glass new file mode 100644 index 0000000..59a9f43 --- /dev/null +++ b/NOTICE.glass @@ -0,0 +1,5 @@ +Glass Z80 assembler +Copyright 2013 Laurens Holst + +This product includes software developed by +Laurens Holst . diff --git a/build.xml b/build.xml index 69672f0..4857049 100644 --- a/build.xml +++ b/build.xml @@ -224,6 +224,8 @@ + + diff --git a/src-tests/nl/grauw/glass/ParserTest.java b/src-tests/nl/grauw/glass/ParserTest.java new file mode 100644 index 0000000..b73aa07 --- /dev/null +++ b/src-tests/nl/grauw/glass/ParserTest.java @@ -0,0 +1,180 @@ +package nl.grauw.glass; + +import static org.junit.Assert.*; + +import java.io.LineNumberReader; +import java.io.StringReader; + +import nl.grauw.glass.Parser.SyntaxError; +import nl.grauw.glass.expressions.CharacterLiteral; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.ExpressionBuilder.ExpressionError; +import nl.grauw.glass.expressions.Flag; +import nl.grauw.glass.expressions.IntegerLiteral; + +import org.junit.Test; + +public class ParserTest { + + @Test + public void testLabel() { + assertEquals("test_label1", parse("test_label1:").getLabel()); + } + + @Test + public void testLabelNoColon() { + assertEquals("test_label1", parse("test_label1").getLabel()); + } + + @Test + public void testLabelIndented() { + assertEquals("test_label", parse(" test_label:").getLabel()); + } + + @Test + public void testLabelIndentedWithMnemonic() { + assertEquals("test_label", parse(" test_label:exx").getLabel()); + assertEquals("exx", parse(" test_label:exx").getMnemonic()); + } + + @Test + public void testMnemonic() { + assertEquals("exx", parse(" exx").getMnemonic()); + } + + @Test + public void testArguments() { + assertTrue(parse("\tcp 0H").getArguments() instanceof IntegerLiteral); + } + + @Test + public void testComment() { + assertEquals("test comment", parse(";test comment").getComment()); + } + + @Test + public void testParser1() { + assertEquals(";test comment", parse(" ;test comment").toString()); + } + + @Test + public void testParser2() { + assertEquals("test_label1: ;test", parse("test_label1:;test").toString()); + } + + @Test + public void testParser3() { + assertEquals("test_label1: ;test", parse("test_label1;test").toString()); + } + + @Test + public void testParser4() { + assertEquals("test_label1: exx ;test", parse("test_label1:exx;test").toString()); + } + + @Test + public void testParser5() { + assertEquals("test_label1: push af ;test", parse("test_label1: push af ;test").toString()); + } + + @Test + public void testParser6() { + assertEquals("test_label1: ex af, af' ;test", parse("test_label1: ex af,af';test").toString()); + } + + @Test + public void testCharacterLiteral() { + assertEquals('x', ((CharacterLiteral)parseExpression("'x'")).getCharacter()); + } + + @Test + public void testCharacterLiteralEscape() { + assertEquals('"', ((CharacterLiteral)parseExpression("'\\\"'")).getCharacter()); + } + + @Test(expected=SyntaxError.class) + public void testCharacterLiteralTooLong() { + parse("'xx'"); + } + + @Test(expected=SyntaxError.class) + public void testCharacterLiteralTooShort() { + parse("''"); + } + + @Test(expected=SyntaxError.class) + public void testCharacterLiteralUnclosed() { + parse("'"); + } + + @Test(expected=SyntaxError.class) + public void testCharacterLiteralUnclosedEscape() { + parse("'\\"); + } + + @Test(expected=SyntaxError.class) + public void testHexNumberTooShort() { + parseExpression("0x"); + } + + @Test(expected=ExpressionError.class) + public void testHexNumberWrong() { + parseExpression("003x0"); + } + + @Test(expected=ExpressionError.class) + public void testHexNumberWrong2() { + parseExpression("0x0x0"); + } + + @Test(expected=ExpressionError.class) + public void testHexNumberWrong3() { + parseExpression("3x0"); + } + + @Test + public void testNumber() { + assertEquals(127, parseExpression("127").getInteger()); + assertEquals(4095, parseExpression("0FFFH").getInteger()); + assertEquals(4095, parseExpression("#0FFF").getInteger()); + assertEquals(4095, parseExpression("$0FFF").getInteger()); + assertEquals(171, parseExpression("10101011B").getInteger()); + assertEquals(171, parseExpression("%10101011").getInteger()); + assertEquals(255, parseExpression("0xFF").getInteger()); + assertEquals(50, parseExpression("0X032").getInteger()); + } + + @Test + public void testFlag() { + assertEquals(Flag.NZ, parseExpression("nz").getFlag()); + assertEquals(Flag.Z, parseExpression("z").getFlag()); + assertEquals(Flag.NC, parseExpression("nc").getFlag()); + assertEquals(Flag.C, parseExpression("c").getFlag()); + assertEquals(Flag.PO, parseExpression("po").getFlag()); + assertEquals(Flag.PE, parseExpression("pe").getFlag()); + assertEquals(Flag.P, parseExpression("p").getFlag()); + assertEquals(Flag.M, parseExpression("m").getFlag()); + } + + @Test + public void testFlagNegate() { + assertEquals(Flag.Z, parseExpression("!nz").getFlag()); + assertEquals(Flag.NZ, parseExpression("!z").getFlag()); + assertEquals(Flag.C, parseExpression("!nc").getFlag()); + assertEquals(Flag.NC, parseExpression("!c").getFlag()); + assertEquals(Flag.PE, parseExpression("!po").getFlag()); + assertEquals(Flag.PO, parseExpression("!pe").getFlag()); + assertEquals(Flag.M, parseExpression("!p").getFlag()); + assertEquals(Flag.P, parseExpression("!m").getFlag()); + } + + public Line parse(String text) { + LineNumberReader reader = new LineNumberReader(new StringReader(text)); + return new Parser().parse(reader, new Scope(), null); + } + + public Expression parseExpression(String text) { + return parse(" test " + text).getArguments(); + } + +} diff --git a/src-tests/nl/grauw/glass/SourceTest.java b/src-tests/nl/grauw/glass/SourceTest.java new file mode 100644 index 0000000..b7f6993 --- /dev/null +++ b/src-tests/nl/grauw/glass/SourceTest.java @@ -0,0 +1,890 @@ +package nl.grauw.glass; + +import static org.junit.Assert.*; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; + +import nl.grauw.glass.Scope.SymbolNotFoundException; +import nl.grauw.glass.expressions.EvaluationException; +import nl.grauw.glass.instructions.ArgumentException; +import nl.grauw.glass.instructions.Error.ErrorDirectiveException; + +import org.junit.Test; + +public class SourceTest { + + @Test + public void testNops() { + assertArrayEquals(b(0x00, 0x00), assemble( + " nop", + " nop" + )); + } + + @Test + public void testJumpSelf() { + assertArrayEquals(b(0x00, 0xC3, 0x01, 0x00), assemble( + " nop", + " jp $" + )); + } + + @Test + public void testJumpLabelSelf() { + assertArrayEquals(b(0x00, 0x00, 0xC3, 0x01, 0x00), assemble( + " nop", + "label: nop", + " jp label" + )); + } + + @Test + public void testJumpLabelBackward() { + assertArrayEquals(b(0x00, 0x00, 0xC3, 0x01, 0x00), assemble( + " nop", + "label: nop", + " jp label" + )); + } + + @Test + public void testJumpLabelForward() { + assertArrayEquals(b(0xC3, 0x03, 0x00), assemble( + " jp label", + "label:" + )); + } + + @Test + public void testEqu() { + assertArrayEquals(b(0x3E, 0x10), assemble( + " ld a,label", + "label: equ 10H" + )); + } + + @Test + public void testEquRegister() { + assertArrayEquals(b(0x78), assemble( + " ld a,label", + "label: equ b" + )); + } + + @Test + public void testEquIndirectRegister() { + assertArrayEquals(b(0x7E), assemble( + " ld a,label", + "label: equ (hl)" + )); + } + + @Test + public void testOrg() { + assertArrayEquals(b(0xC3, 0x32, 0x40, 0x00, 0x18, 0xFD), assemble( + " jp label", + " org 4032H", + "label:", + " nop", + " jr label" + )); + } + + @Test + public void testOrgLabel() { + assertArrayEquals(b(0xC3, 0x03, 0x00, 0x00), assemble( + " jp label", + "label: org 4032H", + " nop" + )); + } + + @Test + public void testOrgLabelBefore() { + assertArrayEquals(b(0xC3, 0x03, 0x00, 0x00), assemble( + " jp label", + "label:", + " org 4032H", + " nop" + )); + } + + @Test + public void testOrgSelfAddress() { + assertArrayEquals(b(0xC3, 0x04, 0x01, 0x00), assemble( + " jp label", + " org $ + 101H", + "label:", + " nop" + )); + } + + @Test + public void testRelativeJumpAssembly() { + assertArrayEquals(b(0x18, 0x05, 0x28, 0x03, 0x10, 0x01, 0x00), assemble( + " org 100H", + " jr label", // Because uninitialised labels use value 0, these + " jr z,label", // would be out of range if they would generate + " djnz label", // actual object code in the first pass. + " nop", + "label:" + )); + } + + @Test + public void testIndexDoubleAdd() { + assertArrayEquals(b( + 0xDD, 0xA6, 0x03, + 0xDD, 0xA6, 0x05, + 0xDD, 0xA6, 0xFD, + 0xDD, 0xA6, 0xFB, + 0xDD, 0xA6, 0x06 + ), assemble( + " and (ix + 1 + 2)", + " and (ix + 7 - 2)", + " and (ix - 1 - 2)", + " and (ix - 7 + 2)", + " and (ix + 3 * 2)" + ) + ); + } + + @Test + public void testMacro() { + assertArrayEquals(b(0x00, 0x00), assemble( + "test: MACRO", + " nop", + " ENDM", + " test", + " test" + )); + } + + @Test + public void testMacroAddress() { + assertArrayEquals(b(0x00, 0xC3, 0x01, 0x00, 0xC3, 0x04, 0x00), assemble( + " nop", + "test: MACRO", + " jp $", + " ENDM", + " test", + " test" + )); + } + + @Test + public void testMacroArguments() { + assertArrayEquals(b(0x3E, 0x10, 0x3E, 0x20), assemble( + "test: MACRO arg", + " ld a,arg", + " ENDM", + " test 10H", + " test 20H" + )); + } + + @Test + public void testMacroArgumentsTwo() { + assertArrayEquals(b(0x3E, 0x30, 0x3E, 0x77), assemble( + "test: MACRO arg1, arg2", + " ld a,arg1 + arg2", + " ENDM", + " test 10H, 20H", + " test 33H, 44H" + )); + } + + @Test(expected=ArgumentException.class) + public void testMacroTooManyArguments() { + assemble( + "test: MACRO", + " ENDM", + " test 10H" + ); + } + + @Test(expected=ArgumentException.class) + public void testMacroTooFewArguments() { + assemble( + "test: MACRO arg", + " ENDM", + " test" + ); + } + + @Test(expected=ArgumentException.class) + public void testMacroNonIdentifierArguments() { + assemble( + "test: MACRO (arg)", + " ENDM" + ); + } + + @Test + public void testMacroDefaultArgument() { + assertArrayEquals(b(0x3E, 0x10, 0x3E, 0x20), assemble( + "test: MACRO arg = 10H", + " ld a,arg", + " ENDM", + " test", + " test 20H" + )); + } + + @Test + public void testMacroDefaultFlagArgument() { + assertArrayEquals(b(0xC8, 0xC0), assemble( + "test: MACRO arg = z", + " ret arg", + " ENDM", + " test", + " test nz" + )); + } + + @Test(expected=AssemblyException.class) + public void testMacroNoEnd() { + assemble( + "test: MACRO" + ); + } + + @Test + public void testMacroLabels() { + assertArrayEquals(b(0x00, 0x21, 0x04, 0x00, 0x21, 0x07, 0x00), assemble( + " nop", + "test: MACRO", + " ld hl,test2", + "test2:", + " ENDM", + " test", + " test" + )); + } + + @Test + public void testMacroOuterScope() { + assertArrayEquals(b(0x3E, 0x11), assemble( + "test: MACRO arg", + " ld a,value + arg", + " ENDM", + " test 1H", + "value: equ 10H" + )); + } + + @Test + public void testMacroNesting() { + assertArrayEquals(b(0x3E, 0x13, 0x3E, 0x23), assemble( + "test: MACRO arg", + "test: MACRO arg", + " ld a,20H + arg + value", + " ENDM", + " ld a,10H + arg + value", + " test arg", + "value: equ 2", + " ENDM", + " test 1H" + )); + } + + @Test + public void testMacroTwiceWithLocalReferences() { + assertArrayEquals(b(0x3E, 0x14, 0x3E, 0x23), assemble( + "test: MACRO arg", + " ld a,10H + arg + value", + " test2 arg", + "value: equ 3", + " ENDM", + "test2: MACRO arg", + " ld a,20H + arg + value", + "value: equ 2", + " ENDM", + " test 1H" + )); + } + + @Test + public void testMacroClosure() { + assertArrayEquals(b(0x3E, 0x14, 0x3E, 0x25), assemble( + "test: MACRO arg", + " ld a,10H + arg + value", + " test2 arg", + "value: equ 3", + " ENDM", + "test2: MACRO arg", + " ld a,20H + arg + value", + " ENDM", + "value: equ 4", + " test 1H" + )); + } + + @Test(expected=SymbolNotFoundException.class) + public void testMacroUnboundReference() { + assertArrayEquals(b(0x3E, 0x14, 0x3E, 0x24), assemble( + "test: MACRO arg", + " ld a,10H + arg + value", + " test2 arg", + "value: equ 3", + " ENDM", + "test2: MACRO arg", + " ld a,20H + arg + value", + " ENDM", + " test 1H" + )); + } + + @Test + public void testMacroLabelsDereference() { + assertArrayEquals(b(0x11, 0x09, 0x00, 0x21, 0x06, 0x00, 0x21, 0x09, 0x00), assemble( + " ld de,test.z.test2", + "test: MACRO", + " ld hl,test2", + "test2:", + " ENDM", + " test", + "test.z: test" + )); + } + + @Test + public void testMacroDefinitionDereference() { + assertArrayEquals(b(0x11, 0x03, 0x00), assemble( + " ld de,test.test2", + "test: MACRO", + " ld hl,test2", + "test2:", + " ENDM" + )); + } + + @Test + public void testMacroDefinitionWithArgumentsDereference() { + assertArrayEquals(b(0x11, 0x03, 0x00), assemble( + " ld de,test.test2", + "test: MACRO arg", + " ld hl,arg", + "test2:", + " ENDM" + )); + } + + @Test + public void testMacroDefinitionWithDefaultArgumentsDereference() { + assertArrayEquals(b(0x11, 0x03, 0x00), assemble( + " ld de,test.test2", + "test: MACRO arg = 0", + " ld hl,arg", + "test2:", + " ENDM" + )); + } + + @Test + public void testMacroDefinitionWithNonIntegerArgumentDereference() { + assertArrayEquals(b(0x11, 0x03, 0x00), assemble( + " ld de,test.test2", + "test: MACRO arg1, arg2", + " ld hl,arg1", + "test2:", + " ret arg2", + " ENDM" + )); + } + + @Test(expected=EvaluationException.class) + public void testMacroDefinitionWithNonIntegerArgumentBeforeDereference() { + assertArrayEquals(b(0x11, 0x03, 0x00), assemble( + " ld de,test.test2", + "test: MACRO arg1, arg2", + " ld hl,arg1", + " ret arg2", + "test2:", + " ENDM" + )); + } + + @Test + public void testMacroDefinitionWithDefaultFlagArgumentBeforeDereference() { + assertArrayEquals(b(0x11, 0x01, 0x00), assemble( + " ld de,test.test2", + "test: MACRO arg = z", + " ret arg", + "test2:", + " ENDM" + )); + } + + @Test + public void testMacroContextArgumentDereference() { + assertArrayEquals(b(0x03), assemble( + "macro1: MACRO", + " ld hl,0", + "test:", + " ENDM", + "macro2: MACRO ?arg", + " db ?arg.test", + " ENDM", + " macro2 macro1" + )); + } + + @Test + public void testMacroContextArgumentDereference2() { + assertArrayEquals(b(0x3E, 0x05, 0x21, 0x00, 0x00), assemble( + "macro1: MACRO", + " ld hl,0", + "test:", + " ENDM", + "macro2: MACRO ?arg", + " ld a,?arg.test", + " ENDM", + " macro2 (m1)", + "m1: macro1" + )); + } + + @Test + public void testMacroInstructionArgument() { + assertArrayEquals(b(0x3E, 0x10, 0xC9), assemble( + "test: MACRO arg", + " ld a,10H", + " arg", + " ENDM", + " test ret" + )); + } + + @Test + public void testRept() { + assertArrayEquals(b(0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF), assemble( + " REPT 3", + " nop", + " rst 38H", + " ENDM" + )); + } + + @Test + public void testReptIndirect() { + assertArrayEquals(b(0x00, 0x00, 0x00, 0x00), assemble( + " REPT count", + " nop", + " ENDM", + "count: equ 4" + )); + } + + @Test + public void testReptParameter() { + assertArrayEquals(b(0x3E, 0x00, 0x3E, 0x01, 0x3E, 0x02), assemble( + " REPT 3, ?value", + " ld a,?value", + " ENDM" + )); + } + + @Test + public void testReptParameterStart() { + assertArrayEquals(b(0x3E, 0x10, 0x3E, 0x11, 0x3E, 0x12), assemble( + " REPT 3, ?value, 10H", + " ld a,?value", + " ENDM" + )); + } + + @Test + public void testReptParameterStartStep() { + assertArrayEquals(b(0x3E, 0x10, 0x3E, 0x13, 0x3E, 0x16), assemble( + " REPT 3, ?value, 10H, 3", + " ld a,?value", + " ENDM" + )); + } + + @Test + public void testReptWithIndex() { + assertArrayEquals(b(0x21, 0x05, 0x00, 0x00, 0x00, 0x00), assemble( + " ld hl,test.2", + "test: REPT 3", + " nop", + "test: ENDM" + )); + } + + @Test + public void testReptWithLabel() { + assertArrayEquals(b(0x21, 0x06, 0x00, 0x00, 0x00, 0x00), assemble( + " ld hl,test.2.test", + "test: REPT 3", + " nop", + "test: ENDM" + )); + } + + @Test(expected=SymbolNotFoundException.class) + public void testReptWithLabelNoIndex() { + assemble( + " nop", + "test: REPT 2", + " nop", + "test: nop", + " ENDM", + " ld hl,test.test" + ); + } + + @Test + public void testReptNested() { + assertArrayEquals(b(0x00, 0x01, 0x02, 0x10, 0x11, 0x12), assemble( + " REPT 2, ?value, 0, 10H", + " REPT 3, ?value2", + " db ?value + ?value2", + " ENDM", + " ENDM" + )); + } + + @Test + public void testReptNoBody() { + assertArrayEquals(b(), assemble( + " REPT 3", + " ENDM" + )); + } + + @Test(expected=ArgumentException.class) + public void testReptNoCount() { + assemble( + " REPT", + " ENDM" + ); + } + + @Test + public void testIrp() { + assertArrayEquals(b(0x3E, 0x10, 0xFF, 0x3E, 0x20, 0xFF, 0x3E, 0x30, 0xFF), assemble( + " IRP ?value, 10H, 20H, 30H", + " ld a,?value", + " rst 38H", + " ENDM" + )); + } + + @Test + public void testIrpNoValues() { + assertArrayEquals(b(), assemble( + " IRP ?value", + " ld a,?value", + " ENDM" + )); + } + + @Test + public void testIrpNoBody() { + assertArrayEquals(b(), assemble( + " IRP ?value, 1, 2, 3", + " ENDM" + )); + } + + @Test + public void testIrpRegisters() { + assertArrayEquals(b(0xC5, 0xD5, 0xE5, 0xF5), assemble( + " IRP ?register, bc, de, hl, af", + " push ?register", + " ENDM" + )); + } + + @Test(expected=ArgumentException.class) + public void testIrpNoIdentifier() { + assertArrayEquals(b(), assemble( + " IRP", + " nop", + " ENDM" + )); + } + + @Test + public void testIrpWithIndex() { + assertArrayEquals(b(0x21, 0x05, 0x00, 0x10, 0x20, 0x30), assemble( + " ld hl,test.2", + "test: IRP ?value, 10H, 20H, 30H", + "test: db ?value", + " ENDM" + )); + } + + @Test + public void testIrpWithLabel() { + assertArrayEquals(b(0x21, 0x05, 0x00, 0x10, 0x20, 0x30), assemble( + " ld hl,test.2.test", + "test: IRP ?value, 10H, 20H, 30H", + "test: db ?value", + " ENDM" + )); + } + + @Test(expected=SymbolNotFoundException.class) + public void testIrpWithLabelNoIndex() { + assemble( + " nop", + "test: IRP ?value, 10H, 20H, 30H", + " nop", + "test: nop", + " ENDM", + " ld hl,test.test" + ); + } + + @Test + public void testProc() { + assertArrayEquals(b(0x21, 0x0A, 0x00, 0xC3, 0x06, 0x00, 0xFF, 0xC3, 0x0A, 0x00, 0xFF), assemble( + " ld hl,test2.test", + "test1: PROC", + " jp test", + "test: rst 38H", + " ENDP", + "test2: PROC", + " jp test", + "test: rst 38H", + " ENDP" + )); + } + + @Test + public void testIf() { + assertArrayEquals(b(0x00), assemble( + " IF 1", + " nop", + " ENDIF" + )); + } + + @Test + public void testIfThen() { + assertArrayEquals(b(0x00), assemble( + " IF 1 = 1", + " nop", + " ELSE", + " rst 38H", + " ENDIF" + )); + } + + @Test + public void testIfThenElse() { + assertArrayEquals(b(0xFF), assemble( + " IF 0 > 1", + " nop", + " ELSE", + " rst 38H", + " ENDIF" + )); + } + + @Test + public void testIfInsideRept() { + assertArrayEquals(b(0xFF, 0x00, 0xFF), assemble( + " IRP ?test, 00H, 10H, 11H", + " IF ?test = 10H", + " nop", + " ELSE", + " rst 38H", + " ENDIF", + " ENDM" + )); + } + + @Test + public void testIfWithEqu() { + assertArrayEquals(b(0xC3, 0x10, 0x00), assemble( + " IF 1", + "test: equ 10H", + " ELSE", + "test: equ 20H", + " ENDIF", + " jp test" + )); + } + + @Test + public void testIfWithLabel() { + assertArrayEquals(b(0x22, 0x22, 0xC3, 0x02, 0x00), assemble( + " IF 0", + " db 11H", + "test: ELSE", + " dw 2222H", + "test: ENDIF", + " jp test" + )); + } + + @Test(expected=SymbolNotFoundException.class) + public void testIfWithEquForward() { + assemble( + " jp test", + " IF 1", + "test: equ 10H", + " ENDIF" + ); + } + + @Test(expected=ErrorDirectiveException.class) + public void testError() { + assemble( + " ERROR" + ); + } + + @Test + public void testErrorWithMessage() { + try { + assemble( + " ERROR \"Test\"" + ); + } catch (ErrorDirectiveException e) { + assertEquals("Test", e.getPlainMessage()); + } + } + + @Test(expected=ArgumentException.class) + public void testAnnotationNotSupported() { + assemble( + " or A 0" + ); + } + + @Test + public void testDsVirtual() { + assertArrayEquals(b(0x3E, 0x86, 0x21, 0x12, 0x00), assemble( + " ld a,86H", + " ds VIRTUAL 10H", + " ld hl,$" + )); + } + + @Test(expected=ArgumentException.class) + public void testDsVirtualWithFill() { + assemble( + " ds VIRTUAL 10H, 0" + ); + } + + @Test(expected=ArgumentException.class) + public void testDsUnknownAnnotation() { + assemble( + " ds UNKNOWN 10H" + ); + } + + @Test + public void testSection() { + assertArrayEquals(b(0x00, 0x21, 0x07, 0x00, 0x21, 0x04, 0x00, 0x86, 0x86, 0x00, 0x11, 0x07, 0x00), assemble( + " nop", + "ROM: ds 8H, 86H", + " nop", + " SECTION ROM", + " ld hl,label", + " ld hl,$", + "label: ENDS", + " ld de,label" + )); + } + + @Test + public void testSectionVirtual() { + assertArrayEquals(b(0x00, 0x00, 0x11, 0x07, 0x00), assemble( + " nop", + "RAM: ds VIRTUAL 8H", + " nop", + " SECTION RAM", + " ld hl,label", + " ld hl,$", + "label: ENDS", + " ld de,label" + )); + } + + @Test + public void testSectionFitsSpace() { + assemble( + "ROM: ds 3H", + " SECTION ROM", + " ld hl,$", + " ENDS" + ); + } + + @Test(expected=AssemblyException.class) + public void testSectionExceedsSpace() { + assemble( + "ROM: ds 2H", + " SECTION ROM", + " ld hl,$", + " ENDS" + ); + } + + @Test + public void testSectionIndirect() { + assemble( + "ROM2: ds 3H", + "ROM: equ ROM2", + " SECTION ROM", + " ld hl,$", + " ENDS" + ); + } + + @Test(expected=AssemblyException.class) + public void testEndm() { + assemble( + " ENDM" + ); + } + + @Test(expected=AssemblyException.class) + public void testEndp() { + assemble( + " ENDP" + ); + } + + @Test(expected=AssemblyException.class) + public void testEnds() { + assemble( + " ENDS" + ); + } + + public byte[] assemble(String... sourceLines) { + StringBuilder builder = new StringBuilder(); + for (String lineText : sourceLines) + builder.append(lineText).append("\n"); + SourceBuilder sourceBuilder = new SourceBuilder(new ArrayList()); + Source source = sourceBuilder.parse(new StringReader(builder.toString()), null); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try { + source.assemble(output); + } catch (IOException e) { + throw new RuntimeException(e); + } + return output.toByteArray(); + } + + public byte[] b(int... values) { + byte[] bytes = new byte[values.length]; + for (int i = 0; i < values.length; i++) + bytes[i] = (byte)values[i]; + return bytes; + } + + +} diff --git a/src-tests/nl/grauw/glass/expressions/ExpressionBuilderTest.java b/src-tests/nl/grauw/glass/expressions/ExpressionBuilderTest.java new file mode 100644 index 0000000..7a950fa --- /dev/null +++ b/src-tests/nl/grauw/glass/expressions/ExpressionBuilderTest.java @@ -0,0 +1,255 @@ +package nl.grauw.glass.expressions; + +import static org.junit.Assert.*; + +import java.io.LineNumberReader; +import java.io.StringReader; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Parser; +import nl.grauw.glass.Parser.SyntaxError; +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.ExpressionBuilder.ExpressionError; + +import org.junit.Test; + +public class ExpressionBuilderTest { + + @Test + public void testSingleValue() { + assertEquals("a", parse("a")); + } + + @Test + public void testAddition() { + assertEquals("{a + 1H}", parse("a + 1H")); + } + + @Test + public void testAddition2() { + assertEquals("{{a + 1H} + 2H}", parse("a + 1H + 2H")); + } + + @Test + public void testPrecedence() { + assertEquals("{a + {1H * 2H}}", parse("a + 1H * 2H")); + } + + @Test + public void testPrecedence2() { + assertEquals("{{a + {1H * 2H}} + b}", parse("a + 1H * 2H + b")); + } + + @Test + public void testGrouping() { + assertEquals("{a + {({1H + 2H}) * 3H}}", parse("a + (1H + 2H) * 3H")); + } + + @Test + public void testGrouping2() { + assertEquals("{{10H + ({15H * ({5H - 2H})})} + 4H}", parse("10H + (15H * (5H - 2H)) + 4H")); + } + + @Test(expected=ExpressionError.class) + public void testGrouping3() { + parse("10H + (15H * (5H - 2H) + 4H"); + } + + @Test(expected=ExpressionError.class) + public void testGrouping4() { + parse("10H + 15H * (5H - 2H)) + 4H"); + } + + @Test + public void testMember() { + assertEquals("{($).member}", parse("($).member")); + } + + @Test + public void testMemberCombinedIdentifier() { + assertEquals("object.member", parse("object.member")); + } + + @Test + public void testIndex() { + assertEquals("{({4H, 5H})[0H]}", parse("(4H, 5H)[0H]")); + } + + @Test + public void testIndexPrecedence() { + assertEquals("{{$.member}[0H]}", parse("$.member[0]")); + } + + @Test + public void testTernaryIfElse() { + assertEquals("{a ? 1H : 2H}", parse("a ? 1H : 2H")); + } + + @Test + public void testTernaryIfElseNested1() { + assertEquals("{a ? 1H : {b ? 2H : 3H}}", parse("a ? 1H : b ? 2H : 3H")); + } + + @Test + public void testTernaryIfElseNested2() { + assertEquals("{a ? {b ? 1H : 2H} : 3H}", parse("a ? b ? 1H : 2H : 3H")); + } + + @Test + public void testTernaryIfElseNested3() { + assertEquals("{a ? {b ? 1H : 2H} : 3H}", parse("a ? b ? 1H : 2H : 3H")); + } + + @Test + public void testTernaryIfElseNested4() { + assertEquals("{{ANN 1H}, {{ANN {a ? {b ? {2H + 3H} : {{c < d} ? 4H : {5H - 6H}}} : 7H}}, {e ? 8H : 9H}}}", + parse("ANN 1H, ANN a ? b ? 2H + 3H : c < d ? 4H : 5H - 6H : 7H, e ? 8H : 9H")); + } + + @Test(expected=ExpressionError.class) + public void testTernaryIfWithoutElse() { + parse("a ? 1H"); + } + + @Test(expected=ExpressionError.class) + public void testTernaryElseWithoutIf() { + parse("a : b"); + } + + @Test + public void testTernaryIfElseHigherPrecedence() { + assertEquals("{{a < 1H} ? {x + 1H} : {y + 2H}}", parse("a < 1H ? x + 1H : y + 2H")); + } + + @Test + public void testTernaryIfElseLowerPrecedence() { + assertEquals("{0H, {{a ? 1H : 2H}, 3H}}", parse("0H, a ? 1H : 2H, 3H")); + } + + @Test(expected=ExpressionError.class) + public void testTernaryIfElseLowerPrecedenceNegative() { + parse("a ? 1H, 2H : 3H"); + } + + @Test + public void testTernaryIfElseLowerPrecedenceGroup() { + assertEquals("{a ? ({1H, 2H}) : 3H}", parse("a ? (1H, 2H) : 3H")); + } + + @Test + public void testSequence() { + assertEquals("{a, 1H}", parse("a, 1H")); + } + + @Test + public void testSequence2() { + assertEquals("{{a + 1H}, {b + 2H}}", parse("a + 1H, b + 2H")); + } + + @Test + public void testSequence3() { + assertEquals("{a, {{1H + 2H}, {3H + 4H}}}", parse("a, 1H + 2H, 3H + 4H")); + } + + @Test + public void testSequenceInGroup() { + assertEquals("{1H + {({a, {2H, 3H}}) * b}}", parse("1H + (a, 2H, 3H) * b")); + } + + @Test + public void testSequenceWithDoubleGroup() { + assertEquals("{a, {{({10H + 15H}) * ({5H - 2H})} + 4H}}", parse("a, (10H + 15H) * (5H - 2H) + 4H")); + } + + @Test + public void testAnnotation() { + assertEquals("{a 1H}", parse("a 1H")); + } + + @Test + public void testAnnotationTwice() { + assertEquals("{a {b 1H}}", parse("a b 1H")); + } + + @Test + public void testAnnotationGroup() { + assertEquals("{a (1H)}", parse("a (1H)")); + } + + @Test + public void testAnnotationNot() { + assertEquals("{a !1H}", parse("a !1H")); + } + + @Test + public void testAnnotationComplement() { + assertEquals("{a ~1H}", parse("a ~1H")); + } + + @Test + public void testAnnotationSubtract() { + assertEquals("{a {1H - 2H}}", parse("a 1H - 2H")); + } + + @Test + public void testAnnotationLogicalOr() { + assertEquals("{a {1H || 2H}}", parse("a 1H || 2H")); + } + + @Test + public void testAnnotationSequence() { + assertEquals("{{a 1H}, {b 2H}}", parse("a 1H, b 2H")); + } + + @Test + public void testAnnotationInGroup() { + assertEquals("{a {1H || ({b 2H})}}", parse("a 1H || (b 2H)")); + } + + @Test(expected=ExpressionError.class) + public void testAnnotationInTheMiddle() { + parse("a 1H || b 2H"); + } + + @Test(expected=ExpressionError.class) + public void testAnnotationNotAnIdentifier() { + parse("0 1H"); + } + + @Test(expected=ExpressionError.class) + public void testAnnotationNotAnIdentifier2() { + parse("a 0 1H"); + } + + @Test + public void testMultiline() { + assertEquals("{a + 1H}", parse("a +\n1H")); + } + + @Test + public void testMultiline2() { + assertEquals("{a, 1H}", parse("a, ;\n 1H")); + } + + @Test + public void testMultiline3() { + assertEquals("a", parse("a \n + 1H")); + } + + @Test(expected=ExpressionError.class) + public void testMultilineLabel() { + assertEquals(null, parse("a +\ntest: 1H")); + } + + @Test(expected=SyntaxError.class) + public void testIncomplete() { + assertEquals(null, parse("a,")); + } + + public String parse(String text) { + LineNumberReader reader = new LineNumberReader(new StringReader(" test " + text)); + Line line = new Parser().parse(reader, new Scope(), null); + return line.getArguments().toDebugString(); + } + +} diff --git a/src-tests/nl/grauw/glass/expressions/ExpressionTest.java b/src-tests/nl/grauw/glass/expressions/ExpressionTest.java new file mode 100644 index 0000000..adba583 --- /dev/null +++ b/src-tests/nl/grauw/glass/expressions/ExpressionTest.java @@ -0,0 +1,277 @@ +package nl.grauw.glass.expressions; + +import static org.junit.Assert.*; + +import java.io.LineNumberReader; +import java.io.StringReader; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Parser; +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.ExpressionBuilder.ExpressionError; + +import org.junit.Test; + +public class ExpressionTest { + + @Test + public void testPositive() { + assertEquals(100, parse("+100").getInteger()); + } + + @Test + public void testPositiveTwice() { + assertEquals(100, parse("++100").getInteger()); + } + + @Test + public void testNegative() { + assertEquals(-100, parse("-100").getInteger()); + } + + @Test + public void testNegativeTwice() { + assertEquals(100, parse("--100").getInteger()); + } + + @Test + public void testComplement() { + assertEquals(-5, parse("~4").getInteger()); + } + + @Test + public void testComplementTwice() { + assertEquals(4, parse("~~4").getInteger()); + } + + @Test + public void testNot() { + assertEquals(0, parse("!100").getInteger()); + } + + @Test + public void testNotTwice() { + assertEquals(-1, parse("!!100").getInteger()); + } + + @Test + public void testMultiply() { + assertEquals(99, parse("9 * 11").getInteger()); + } + + @Test + public void testDivide() { + assertEquals(3, parse("11 / 3").getInteger()); + } + + @Test(expected=EvaluationException.class) + public void testDivideByZero() { + parse("1 / 0").getInteger(); + } + + @Test + public void testModulo() { + assertEquals(2, parse("11 % 3").getInteger()); + } + + @Test(expected=EvaluationException.class) + public void testModuloByZero() { + parse("1 % 0").getInteger(); + } + + @Test + public void testAdd() { + assertEquals(9, parse("4 + 5").getInteger()); + } + + @Test + public void testSubtract() { + assertEquals(-4, parse("5 - 9").getInteger()); + } + + @Test + public void testShiftLeft() { + assertEquals(192, parse("3 << 6").getInteger()); + } + + @Test + public void testShiftRight() { + assertEquals(3, parse("193 >> 6").getInteger()); + } + + @Test + public void testShiftRightSign() { + assertEquals(-1, parse("-1 >> 16").getInteger()); + } + + @Test + public void testLessThan() { + assertEquals(-1, parse("3 < 4").getInteger()); + assertEquals(0, parse("4 < 4").getInteger()); + assertEquals(0, parse("5 < 4").getInteger()); + } + + @Test + public void testLessOrEquals() { + assertEquals(-1, parse("3 <= 4").getInteger()); + assertEquals(-1, parse("4 <= 4").getInteger()); + assertEquals(0, parse("5 <= 4").getInteger()); + } + + @Test + public void testGreaterThan() { + assertEquals(0, parse("3 > 4").getInteger()); + assertEquals(0, parse("4 > 4").getInteger()); + assertEquals(-1, parse("5 > 4").getInteger()); + } + + @Test + public void testGreaterOrEquals() { + assertEquals(0, parse("3 >= 4").getInteger()); + assertEquals(-1, parse("4 >= 4").getInteger()); + assertEquals(-1, parse("5 >= 4").getInteger()); + } + + @Test + public void testEquals() { + assertEquals(0, parse("3 = 4").getInteger()); + assertEquals(-1, parse("4 = 4").getInteger()); + assertEquals(0, parse("5 = 4").getInteger()); + } + + @Test + public void testNotEquals() { + assertEquals(-1, parse("3 != 4").getInteger()); + assertEquals(0, parse("4 != 4").getInteger()); + assertEquals(-1, parse("5 != 4").getInteger()); + } + + @Test + public void testAnd() { + assertEquals(2, parse("6 & 3").getInteger()); + } + + @Test + public void testXor() { + assertEquals(5, parse("6 ^ 3").getInteger()); + } + + @Test + public void testOr() { + assertEquals(7, parse("6 | 3").getInteger()); + } + + @Test + public void testLogicalAnd() { + assertEquals(0, parse("0 && 0").getInteger()); + assertEquals(0, parse("0 && 8").getInteger()); + assertEquals(0, parse("-1 && 0").getInteger()); + assertEquals(3, parse("1 && 3").getInteger()); + } + + @Test + public void testLogicalOr() { + assertEquals(0, parse("0 || 0").getInteger()); + assertEquals(8, parse("0 || 8").getInteger()); + assertEquals(-1, parse("-1 || 0").getInteger()); + assertEquals(1, parse("1 || 3").getInteger()); + } + + @Test + public void testAnnotation() { + assertEquals("VIRTUAL", parse("VIRTUAL 15").getAnnotation().getName()); + assertEquals(15, parse("VIRTUAL 15").getAnnotee().getInteger()); + } + + @Test + public void testSequence() { + assertEquals(3, parse("4, 5, 6").getList().size()); + assertEquals(4, parse("4, 5, 6").getElement(0).getInteger()); + assertEquals(5, parse("4, 5, 6").getElement(1).getInteger()); + assertEquals(6, parse("4, 5, 6").getElement(2).getInteger()); + assertEquals(null, parse("4, 5, 6").getElement(3)); + } + + @Test + public void testTernaryIfElse() { + assertEquals(2, parse("1 ? 2 : 3").getInteger()); + assertEquals(3, parse("0 ? 2 : 3").getInteger()); + } + + @Test + public void testGroup() { + assertEquals(9, parse("(1 + 2) * 3").getInteger()); + } + + @Test + public void testIdentifier() { + Scope scope = new Scope(); + scope.addSymbol("symbol", new IntegerLiteral(11)); + assertEquals(11, parse("symbol", scope).getInteger()); + } + + @Test + public void testMember() { + Scope objectScope = new Scope(); + objectScope.addSymbol("symbol", new IntegerLiteral(11)); + Scope scope = new Scope(); + scope.addSymbol("object", new ContextLiteral(objectScope)); + assertEquals(11, parse("object.symbol", scope).getInteger()); + } + + @Test + public void testThisMember() { + Scope scope = new Scope(); + scope.addSymbol("symbol", new IntegerLiteral(11)); + assertEquals(11, parse("$.symbol", scope).getInteger()); + } + + @Test + public void testMemberOfExpression() { + Scope scope = new Scope(); + scope.addSymbol("symbol", new IntegerLiteral(11)); + assertEquals(11, parse("($).symbol", scope).getInteger()); + } + + @Test(expected=EvaluationException.class) + public void testMemberNoContext() { + parse("1.symbol").getInteger(); + } + + @Test(expected=ExpressionError.class) + public void testMemberNoIdentifier() { + parse("($).1").getInteger(); + } + + @Test + public void testIndex() { + assertEquals(4, parse("(4H, 5H)[0]").getInteger()); + assertEquals(5, parse("(4H, 5H)[1]").getInteger()); + } + + @Test + public void testIndexNoSequence() { + assertEquals(4, parse("4H[0]").getInteger()); + } + + @Test(expected=EvaluationException.class) + public void testIndexOutOfBounds() { + parse("(4H, 5H)[2]").getInteger(); + } + + @Test(expected=EvaluationException.class) + public void testIndexNoSequenceOutOfBounds() { + parse("4H[1]").getInteger(); + } + + public Expression parse(String text) { + return parse(text, new Scope()); + } + + public Expression parse(String text, Scope scope) { + LineNumberReader reader = new LineNumberReader(new StringReader(" test " + text)); + Line line = new Parser().parse(reader, scope, null); + return line.getArguments(); + } + +} diff --git a/src-tests/nl/grauw/glass/instructions/InstructionTest.java b/src-tests/nl/grauw/glass/instructions/InstructionTest.java new file mode 100644 index 0000000..7d7913b --- /dev/null +++ b/src-tests/nl/grauw/glass/instructions/InstructionTest.java @@ -0,0 +1,972 @@ +package nl.grauw.glass.instructions; + +import static org.junit.Assert.*; + +import java.io.LineNumberReader; +import java.io.StringReader; + +import nl.grauw.glass.GlobalScope; +import nl.grauw.glass.Line; +import nl.grauw.glass.Parser; +import nl.grauw.glass.Scope; + +import org.junit.Test; + +public class InstructionTest { + + @Test + public void testAdcA() { + assertArrayEquals(b(0x88), parse("adc a,b")); + assertArrayEquals(b(0x89), parse("adc a,c")); + assertArrayEquals(b(0x8A), parse("adc a,d")); + assertArrayEquals(b(0x8B), parse("adc a,e")); + assertArrayEquals(b(0x8C), parse("adc a,h")); + assertArrayEquals(b(0x8D), parse("adc a,l")); + assertArrayEquals(b(0x8E), parse("adc a,(hl)")); + assertArrayEquals(b(0x8F), parse("adc a,a")); + assertArrayEquals(b(0xDD, 0x8C), parse("adc a,ixh")); + assertArrayEquals(b(0xFD, 0x8D), parse("adc a,iyl")); + assertArrayEquals(b(0xDD, 0x8E, 0x47), parse("adc a,(ix + 47H)")); + assertArrayEquals(b(0xFD, 0x8E, 0x86), parse("adc a,(iy - 7AH)")); + } + + @Test + public void testAdcAN() { + assertArrayEquals(b(0xCE, 0x86), parse("adc a,86H")); + } + + @Test + public void testAdcHL() { + assertArrayEquals(b(0xED, 0x4A), parse("adc hl,bc")); + assertArrayEquals(b(0xED, 0x5A), parse("adc hl,de")); + assertArrayEquals(b(0xED, 0x6A), parse("adc hl,hl")); + assertArrayEquals(b(0xED, 0x7A), parse("adc hl,sp")); + } + + @Test + public void testAddA() { + assertArrayEquals(b(0x80), parse("add a,b")); + assertArrayEquals(b(0x81), parse("add a,c")); + assertArrayEquals(b(0x82), parse("add a,d")); + assertArrayEquals(b(0x83), parse("add a,e")); + assertArrayEquals(b(0x84), parse("add a,h")); + assertArrayEquals(b(0x85), parse("add a,l")); + assertArrayEquals(b(0x86), parse("add a,(hl)")); + assertArrayEquals(b(0x87), parse("add a,a")); + assertArrayEquals(b(0xDD, 0x84), parse("add a,ixh")); + assertArrayEquals(b(0xFD, 0x85), parse("add a,iyl")); + assertArrayEquals(b(0xDD, 0x86, 0x47), parse("add a,(ix + 47H)")); + assertArrayEquals(b(0xFD, 0x86, 0x86), parse("add a,(iy - 7AH)")); + } + + @Test + public void testAddAN() { + assertArrayEquals(b(0xC6, 0x86), parse("add a,86H")); + } + + @Test + public void testAddHL() { + assertArrayEquals(b(0x09), parse("add hl,bc")); + assertArrayEquals(b(0x19), parse("add hl,de")); + assertArrayEquals(b(0x29), parse("add hl,hl")); + assertArrayEquals(b(0x39), parse("add hl,sp")); + assertArrayEquals(b(0xDD, 0x09), parse("add ix,bc")); + assertArrayEquals(b(0xDD, 0x19), parse("add ix,de")); + assertArrayEquals(b(0xDD, 0x29), parse("add ix,ix")); + assertArrayEquals(b(0xDD, 0x39), parse("add ix,sp")); + assertArrayEquals(b(0xFD, 0x09), parse("add iy,bc")); + assertArrayEquals(b(0xFD, 0x19), parse("add iy,de")); + assertArrayEquals(b(0xFD, 0x29), parse("add iy,iy")); + assertArrayEquals(b(0xFD, 0x39), parse("add iy,sp")); + } + + @Test + public void testAnd() { + assertArrayEquals(b(0xA0), parse("and b")); + assertArrayEquals(b(0xA1), parse("and c")); + assertArrayEquals(b(0xA2), parse("and d")); + assertArrayEquals(b(0xA3), parse("and e")); + assertArrayEquals(b(0xA4), parse("and h")); + assertArrayEquals(b(0xA5), parse("and l")); + assertArrayEquals(b(0xA6), parse("and (hl)")); + assertArrayEquals(b(0xA7), parse("and a")); + assertArrayEquals(b(0xDD, 0xA4), parse("and ixh")); + assertArrayEquals(b(0xFD, 0xA5), parse("and iyl")); + assertArrayEquals(b(0xDD, 0xA6, 0x47), parse("and (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xA6, 0x86), parse("and (iy - 7AH)")); + } + + @Test + public void testAndN() { + assertArrayEquals(b(0xE6, 0x86), parse("and 86H")); + } + + @Test + public void testBit() { + assertArrayEquals(b(0xCB, 0x78), parse("bit 7,b")); + assertArrayEquals(b(0xCB, 0x71), parse("bit 6,c")); + assertArrayEquals(b(0xCB, 0x6A), parse("bit 5,d")); + assertArrayEquals(b(0xCB, 0x63), parse("bit 4,e")); + assertArrayEquals(b(0xCB, 0x5C), parse("bit 3,h")); + assertArrayEquals(b(0xCB, 0x55), parse("bit 2,l")); + assertArrayEquals(b(0xCB, 0x4E), parse("bit 1,(hl)")); + assertArrayEquals(b(0xCB, 0x47), parse("bit 0,a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x5E), parse("bit 3,(ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x66), parse("bit 4,(iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testBit_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x5C), parse("bit 3,ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x55), parse("bit 2,iyl")); + } + + @Test + public void testCall() { + assertArrayEquals(b(0xCD, 0x86, 0x47), parse("call 4786H")); + } + + @Test + public void testCall_F() { + assertArrayEquals(b(0xC4, 0x86, 0x47), parse("call nz,4786H")); + assertArrayEquals(b(0xCC, 0x86, 0x47), parse("call z,4786H")); + assertArrayEquals(b(0xD4, 0x86, 0x47), parse("call nc,4786H")); + assertArrayEquals(b(0xDC, 0x86, 0x47), parse("call c,4786H")); + assertArrayEquals(b(0xE4, 0x86, 0x47), parse("call po,4786H")); + assertArrayEquals(b(0xEC, 0x86, 0x47), parse("call pe,4786H")); + assertArrayEquals(b(0xF4, 0x86, 0x47), parse("call p,4786H")); + assertArrayEquals(b(0xFC, 0x86, 0x47), parse("call m,4786H")); + } + + @Test + public void testCcf() { + assertArrayEquals(b(0x3F), parse("ccf")); + } + + @Test + public void testCp() { + assertArrayEquals(b(0xB8), parse("cp b")); + assertArrayEquals(b(0xB9), parse("cp c")); + assertArrayEquals(b(0xBA), parse("cp d")); + assertArrayEquals(b(0xBB), parse("cp e")); + assertArrayEquals(b(0xBC), parse("cp h")); + assertArrayEquals(b(0xBD), parse("cp l")); + assertArrayEquals(b(0xBE), parse("cp (hl)")); + assertArrayEquals(b(0xBF), parse("cp a")); + assertArrayEquals(b(0xDD, 0xBC), parse("cp ixh")); + assertArrayEquals(b(0xFD, 0xBD), parse("cp iyl")); + assertArrayEquals(b(0xDD, 0xBE, 0x47), parse("cp (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xBE, 0x86), parse("cp (iy - 7AH)")); + } + + @Test + public void testCpN() { + assertArrayEquals(b(0xFE, 0x86), parse("cp 86H")); + } + + @Test + public void testCpd() { + assertArrayEquals(b(0xED, 0xA9), parse("cpd")); + } + + @Test + public void testCpdr() { + assertArrayEquals(b(0xED, 0xB9), parse("cpdr")); + } + + @Test + public void testCpi() { + assertArrayEquals(b(0xED, 0xA1), parse("cpi")); + } + + @Test + public void testCpir() { + assertArrayEquals(b(0xED, 0xB1), parse("cpir")); + } + + @Test + public void testCpl() { + assertArrayEquals(b(0x2F), parse("cpl")); + } + + @Test + public void testDaa() { + assertArrayEquals(b(0x27), parse("daa")); + } + + @Test + public void testDb() { + assertArrayEquals(b(0x86), parse("db 86H")); + assertArrayEquals(b(0x11, 0x22, 0x33, 0x44), parse("db 11H, 22H, 33H, 44H")); + assertArrayEquals(b(0x11, 0x61, 0x62, 0x63, 0x22), parse("db 11H, \"abc\", 22H")); + } + + @Test + public void testDd() { + assertArrayEquals(b(0xCC, 0xDD, 0xEE, 0xFF), parse("dd 0FFEEDDCCH")); + assertArrayEquals(b(0x00, 0x00, 0x00, 0x80), parse("dd -80000000H")); + assertArrayEquals(b(0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55), parse("dd 11223344H, 55667788H")); + } + + @Test + public void testDec() { + assertArrayEquals(b(0x05), parse("dec b")); + assertArrayEquals(b(0x0D), parse("dec c")); + assertArrayEquals(b(0x15), parse("dec d")); + assertArrayEquals(b(0x1D), parse("dec e")); + assertArrayEquals(b(0x25), parse("dec h")); + assertArrayEquals(b(0x2D), parse("dec l")); + assertArrayEquals(b(0x35), parse("dec (hl)")); + assertArrayEquals(b(0x3D), parse("dec a")); + assertArrayEquals(b(0xDD, 0x25), parse("dec ixh")); + assertArrayEquals(b(0xFD, 0x2D), parse("dec iyl")); + assertArrayEquals(b(0xDD, 0x35, 0x47), parse("dec (ix + 47H)")); + assertArrayEquals(b(0xFD, 0x35, 0x86), parse("dec (iy - 7AH)")); + } + + @Test + public void testDec_RR() { + assertArrayEquals(b(0x0B), parse("dec bc")); + assertArrayEquals(b(0x1B), parse("dec de")); + assertArrayEquals(b(0x2B), parse("dec hl")); + assertArrayEquals(b(0x3B), parse("dec sp")); + assertArrayEquals(b(0xDD, 0x2B), parse("dec ix")); + assertArrayEquals(b(0xFD, 0x2B), parse("dec iy")); + } + + @Test + public void testDi() { + assertArrayEquals(b(0xF3), parse("di")); + } + + @Test + public void testDjnz() { + assertArrayEquals(b(0x10, 0xFE), parse("djnz $")); + } + + @Test + public void testDs() { + assertArrayEquals(b(0x00, 0x00, 0x00, 0x00, 0x00), parse("ds 5H")); + assertArrayEquals(b(0x47, 0x47, 0x47, 0x47, 0x47), parse("ds 5H,47H")); + } + + @Test + public void testDw() { + assertArrayEquals(b(0x86, 0x47), parse("dw 4786H")); + assertArrayEquals(b(0x22, 0x11, 0x44, 0x33, 0x66, 0x55), parse("dw 1122H, 3344H, 5566H")); + assertArrayEquals(b(0x22, 0x11, 0xAC, 0x20, 0x31, 0x00, 0x30, 0x00), parse("dw 1122H, \"€10\"")); + } + + @Test + public void testEi() { + assertArrayEquals(b(0xFB), parse("ei")); + } + + @Test + public void testExAF() { + assertArrayEquals(b(0x08), parse("ex af,af'")); + } + + @Test + public void testExDEHL() { + assertArrayEquals(b(0xEB), parse("ex de,hl")); + } + + @Test + public void testExSP() { + assertArrayEquals(b(0xE3), parse("ex (sp),hl")); + assertArrayEquals(b(0xDD, 0xE3), parse("ex (sp),ix")); + assertArrayEquals(b(0xFD, 0xE3), parse("ex (sp),iy")); + } + + @Test + public void testExx() { + assertArrayEquals(b(0xD9), parse("exx")); + } + + @Test + public void testHalt() { + assertArrayEquals(b(0x76), parse("halt")); + } + + @Test + public void testIm() { + assertArrayEquals(b(0xED, 0x46), parse("im 0")); + assertArrayEquals(b(0xED, 0x56), parse("im 1")); + assertArrayEquals(b(0xED, 0x5E), parse("im 2")); + } + + @Test + public void testIn_C() { + assertArrayEquals(b(0xED, 0x40), parse("in b,(c)")); + assertArrayEquals(b(0xED, 0x48), parse("in c,(c)")); + assertArrayEquals(b(0xED, 0x50), parse("in d,(c)")); + assertArrayEquals(b(0xED, 0x58), parse("in e,(c)")); + assertArrayEquals(b(0xED, 0x60), parse("in h,(c)")); + assertArrayEquals(b(0xED, 0x68), parse("in l,(c)")); + assertArrayEquals(b(0xED, 0x70), parse("in (c)")); + assertArrayEquals(b(0xED, 0x78), parse("in a,(c)")); + } + + @Test + public void testIn_N() { + assertArrayEquals(b(0xDB, 0x86), parse("in a,(86H)")); + } + + @Test + public void testInc() { + assertArrayEquals(b(0x04), parse("inc b")); + assertArrayEquals(b(0x0C), parse("inc c")); + assertArrayEquals(b(0x14), parse("inc d")); + assertArrayEquals(b(0x1C), parse("inc e")); + assertArrayEquals(b(0x24), parse("inc h")); + assertArrayEquals(b(0x2C), parse("inc l")); + assertArrayEquals(b(0x34), parse("inc (hl)")); + assertArrayEquals(b(0x3C), parse("inc a")); + assertArrayEquals(b(0xDD, 0x24), parse("inc ixh")); + assertArrayEquals(b(0xFD, 0x2C), parse("inc iyl")); + assertArrayEquals(b(0xDD, 0x34, 0x47), parse("inc (ix + 47H)")); + assertArrayEquals(b(0xFD, 0x34, 0x86), parse("inc (iy - 7AH)")); + } + + @Test + public void testInc_RR() { + assertArrayEquals(b(0x03), parse("inc bc")); + assertArrayEquals(b(0x13), parse("inc de")); + assertArrayEquals(b(0x23), parse("inc hl")); + assertArrayEquals(b(0x33), parse("inc sp")); + assertArrayEquals(b(0xDD, 0x23), parse("inc ix")); + assertArrayEquals(b(0xFD, 0x23), parse("inc iy")); + } + + @Test + public void testInd() { + assertArrayEquals(b(0xED, 0xAA), parse("ind")); + } + + @Test + public void testIndr() { + assertArrayEquals(b(0xED, 0xBA), parse("indr")); + } + + @Test + public void testIni() { + assertArrayEquals(b(0xED, 0xA2), parse("ini")); + } + + @Test + public void testInir() { + assertArrayEquals(b(0xED, 0xB2), parse("inir")); + } + + @Test + public void testJp() { + assertArrayEquals(b(0xC3, 0x86, 0x47), parse("jp 4786H")); + } + + @Test + public void testJp_F() { + assertArrayEquals(b(0xC2, 0x86, 0x47), parse("jp nz,4786H")); + assertArrayEquals(b(0xCA, 0x86, 0x47), parse("jp z,4786H")); + assertArrayEquals(b(0xD2, 0x86, 0x47), parse("jp nc,4786H")); + assertArrayEquals(b(0xDA, 0x86, 0x47), parse("jp c,4786H")); + assertArrayEquals(b(0xE2, 0x86, 0x47), parse("jp po,4786H")); + assertArrayEquals(b(0xEA, 0x86, 0x47), parse("jp pe,4786H")); + assertArrayEquals(b(0xF2, 0x86, 0x47), parse("jp p,4786H")); + assertArrayEquals(b(0xFA, 0x86, 0x47), parse("jp m,4786H")); + } + + @Test + public void testJp_HL() { + assertArrayEquals(b(0xE9), parse("jp (hl)")); + assertArrayEquals(b(0xDD, 0xE9), parse("jp (ix)")); + assertArrayEquals(b(0xFD, 0xE9), parse("jp (iy)")); + } + + @Test + public void testJr() { + assertArrayEquals(b(0x18, 0xFE), parse("jr $")); + } + + @Test + public void testJr_F() { + assertArrayEquals(b(0x20, 0xFE), parse("jr nz,$")); + assertArrayEquals(b(0x28, 0xFE), parse("jr z,$")); + assertArrayEquals(b(0x30, 0xFE), parse("jr nc,$")); + assertArrayEquals(b(0x38, 0xFE), parse("jr c,$")); + } + + @Test + public void testLd_R_R() { + assertArrayEquals(b(0x78), parse("ld a,b")); + assertArrayEquals(b(0x71), parse("ld (hl),c")); + assertArrayEquals(b(0x6A), parse("ld l,d")); + assertArrayEquals(b(0x63), parse("ld h,e")); + assertArrayEquals(b(0x5C), parse("ld e,h")); + assertArrayEquals(b(0x55), parse("ld d,l")); + assertArrayEquals(b(0x4E), parse("ld c,(hl)")); + assertArrayEquals(b(0x47), parse("ld b,a")); + assertArrayEquals(b(0xDD, 0x6C), parse("ld ixl,ixh")); + assertArrayEquals(b(0xFD, 0x55), parse("ld d,iyl")); + assertArrayEquals(b(0xDD, 0x6E, 0x47), parse("ld l,(ix + 47H)")); + assertArrayEquals(b(0xFD, 0x74, 0x86), parse("ld (iy - 7AH),h")); + } + + @Test + public void testLd_R_N() { + assertArrayEquals(b(0x06, 0x86), parse("ld b,86H")); + assertArrayEquals(b(0x0E, 0x86), parse("ld c,86H")); + assertArrayEquals(b(0x16, 0x86), parse("ld d,86H")); + assertArrayEquals(b(0x1E, 0x86), parse("ld e,86H")); + assertArrayEquals(b(0x26, 0x86), parse("ld h,86H")); + assertArrayEquals(b(0x2E, 0x86), parse("ld l,86H")); + assertArrayEquals(b(0x36, 0x86), parse("ld (hl),86H")); + assertArrayEquals(b(0x3E, 0x86), parse("ld a,86H")); + assertArrayEquals(b(0xDD, 0x26, 0x86), parse("ld ixh,86H")); + assertArrayEquals(b(0xFD, 0x2E, 0x86), parse("ld iyl,86H")); + assertArrayEquals(b(0xDD, 0x36, 0x47, 0x86), parse("ld (ix + 47H),86H")); + assertArrayEquals(b(0xFD, 0x36, 0x86, 0x47), parse("ld (iy - 7AH),47H")); + } + + @Test + public void testLd_A_IR() { + assertArrayEquals(b(0xED, 0x57), parse("ld a,i")); + assertArrayEquals(b(0xED, 0x5F), parse("ld a,r")); + } + + @Test + public void testLd_IR_A() { + assertArrayEquals(b(0xED, 0x47), parse("ld i,a")); + assertArrayEquals(b(0xED, 0x4F), parse("ld r,a")); + } + + @Test + public void testLd_A_BCDE() { + assertArrayEquals(b(0x0A), parse("ld a,(bc)")); + assertArrayEquals(b(0x1A), parse("ld a,(de)")); + } + + @Test + public void testLd_A_NN() { + assertArrayEquals(b(0x3A, 0x86, 0x47), parse("ld a,(4786H)")); + } + + @Test + public void testLd_HL_NN() { + assertArrayEquals(b(0x2A, 0x86, 0x47), parse("ld hl,(4786H)")); + assertArrayEquals(b(0xDD, 0x2A, 0x86, 0x47), parse("ld ix,(4786H)")); + assertArrayEquals(b(0xFD, 0x2A, 0x86, 0x47), parse("ld iy,(4786H)")); + } + + @Test + public void testLd_RR_NN() { + assertArrayEquals(b(0xED, 0x4B, 0x86, 0x47), parse("ld bc,(4786H)")); + assertArrayEquals(b(0xED, 0x5B, 0x86, 0x47), parse("ld de,(4786H)")); + assertArrayEquals(b(0xED, 0x7B, 0x86, 0x47), parse("ld sp,(4786H)")); + } + + @Test + public void testLd_BCDE_A() { + assertArrayEquals(b(0x02), parse("ld (bc),a")); + assertArrayEquals(b(0x12), parse("ld (de),a")); + } + + @Test + public void testLd_NN_A() { + assertArrayEquals(b(0x32, 0x86, 0x47), parse("ld (4786H),a")); + } + + @Test + public void testLd_NN_HL() { + assertArrayEquals(b(0x22, 0x86, 0x47), parse("ld (4786H),hl")); + assertArrayEquals(b(0xDD, 0x22, 0x86, 0x47), parse("ld (4786H),ix")); + assertArrayEquals(b(0xFD, 0x22, 0x86, 0x47), parse("ld (4786H),iy")); + } + + @Test + public void testLd_NN_RR() { + assertArrayEquals(b(0xED, 0x43, 0x86, 0x47), parse("ld (4786H),bc")); + assertArrayEquals(b(0xED, 0x53, 0x86, 0x47), parse("ld (4786H),de")); + assertArrayEquals(b(0xED, 0x73, 0x86, 0x47), parse("ld (4786H),sp")); + } + + @Test + public void testLd_RR_N() { + assertArrayEquals(b(0x01, 0x86, 0x47), parse("ld bc,4786H")); + assertArrayEquals(b(0x11, 0x86, 0x47), parse("ld de,4786H")); + assertArrayEquals(b(0x21, 0x86, 0x47), parse("ld hl,4786H")); + assertArrayEquals(b(0x31, 0x86, 0x47), parse("ld sp,4786H")); + assertArrayEquals(b(0xDD, 0x21, 0x86, 0x47), parse("ld ix,4786H")); + assertArrayEquals(b(0xFD, 0x21, 0x86, 0x47), parse("ld iy,4786H")); + } + + @Test + public void testLd_SP_HL() { + assertArrayEquals(b(0xF9), parse("ld sp,hl")); + assertArrayEquals(b(0xDD, 0xF9), parse("ld sp,ix")); + assertArrayEquals(b(0xFD, 0xF9), parse("ld sp,iy")); + } + + @Test + public void testLdd() { + assertArrayEquals(b(0xED, 0xA8), parse("ldd")); + } + + @Test + public void testLddr() { + assertArrayEquals(b(0xED, 0xB8), parse("lddr")); + } + + @Test + public void testLdi() { + assertArrayEquals(b(0xED, 0xA0), parse("ldi")); + } + + @Test + public void testLdir() { + assertArrayEquals(b(0xED, 0xB0), parse("ldir")); + } + + @Test + public void testMulub() { + assertArrayEquals(b(0xED, 0xC1), parse("mulub a,b")); + assertArrayEquals(b(0xED, 0xC9), parse("mulub a,c")); + assertArrayEquals(b(0xED, 0xD1), parse("mulub a,d")); + assertArrayEquals(b(0xED, 0xD9), parse("mulub a,e")); + } + + @Test + public void testMuluw() { + assertArrayEquals(b(0xED, 0xC3), parse("muluw hl,bc")); + assertArrayEquals(b(0xED, 0xF3), parse("muluw hl,sp")); + } + + @Test + public void testNeg() { + assertArrayEquals(b(0xED, 0x44), parse("neg")); + } + + @Test + public void testNop() { + assertArrayEquals(b(0x00), parse("nop")); + } + + @Test + public void testOr() { + assertArrayEquals(b(0xB0), parse("or b")); + assertArrayEquals(b(0xB1), parse("or c")); + assertArrayEquals(b(0xB2), parse("or d")); + assertArrayEquals(b(0xB3), parse("or e")); + assertArrayEquals(b(0xB4), parse("or h")); + assertArrayEquals(b(0xB5), parse("or l")); + assertArrayEquals(b(0xB6), parse("or (hl)")); + assertArrayEquals(b(0xB7), parse("or a")); + assertArrayEquals(b(0xDD, 0xB4), parse("or ixh")); + assertArrayEquals(b(0xFD, 0xB5), parse("or iyl")); + assertArrayEquals(b(0xDD, 0xB6, 0x47), parse("or (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xB6, 0x86), parse("or (iy - 7AH)")); + } + + @Test + public void testOrN() { + assertArrayEquals(b(0xF6, 0x86), parse("or 86H")); + } + + @Test + public void testOtdr() { + assertArrayEquals(b(0xED, 0xBB), parse("otdr")); + } + + @Test + public void testOtir() { + assertArrayEquals(b(0xED, 0xB3), parse("otir")); + } + + @Test + public void testOut_C() { + assertArrayEquals(b(0xED, 0x41), parse("out (c),b")); + assertArrayEquals(b(0xED, 0x49), parse("out (c),c")); + assertArrayEquals(b(0xED, 0x51), parse("out (c),d")); + assertArrayEquals(b(0xED, 0x59), parse("out (c),e")); + assertArrayEquals(b(0xED, 0x61), parse("out (c),h")); + assertArrayEquals(b(0xED, 0x69), parse("out (c),l")); + assertArrayEquals(b(0xED, 0x79), parse("out (c),a")); + } + + @Test + public void testOut_N() { + assertArrayEquals(b(0xD3, 0x86), parse("out (86H),a")); + } + + @Test + public void testOutd() { + assertArrayEquals(b(0xED, 0xAB), parse("outd")); + } + + @Test + public void testOuti() { + assertArrayEquals(b(0xED, 0xA3), parse("outi")); + } + + @Test + public void testPop() { + assertArrayEquals(b(0xC1), parse("pop bc")); + assertArrayEquals(b(0xD1), parse("pop de")); + assertArrayEquals(b(0xE1), parse("pop hl")); + assertArrayEquals(b(0xF1), parse("pop af")); + assertArrayEquals(b(0xDD, 0xE1), parse("pop ix")); + assertArrayEquals(b(0xFD, 0xE1), parse("pop iy")); + } + + @Test + public void testPush() { + assertArrayEquals(b(0xC5), parse("push bc")); + assertArrayEquals(b(0xD5), parse("push de")); + assertArrayEquals(b(0xE5), parse("push hl")); + assertArrayEquals(b(0xF5), parse("push af")); + assertArrayEquals(b(0xDD, 0xE5), parse("push ix")); + assertArrayEquals(b(0xFD, 0xE5), parse("push iy")); + } + + @Test + public void testRes() { + assertArrayEquals(b(0xCB, 0xB8), parse("res 7,b")); + assertArrayEquals(b(0xCB, 0xB1), parse("res 6,c")); + assertArrayEquals(b(0xCB, 0xAA), parse("res 5,d")); + assertArrayEquals(b(0xCB, 0xA3), parse("res 4,e")); + assertArrayEquals(b(0xCB, 0x9C), parse("res 3,h")); + assertArrayEquals(b(0xCB, 0x95), parse("res 2,l")); + assertArrayEquals(b(0xCB, 0x8E), parse("res 1,(hl)")); + assertArrayEquals(b(0xCB, 0x87), parse("res 0,a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x9E), parse("res 3,(ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0xA6), parse("res 4,(iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testRes_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x9C), parse("res 3,ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x95), parse("res 2,iyl")); + } + + @Test + public void testRet() { + assertArrayEquals(b(0xC9), parse("ret")); + } + + @Test + public void testRetF() { + assertArrayEquals(b(0xC0), parse("ret nz")); + assertArrayEquals(b(0xC8), parse("ret z")); + assertArrayEquals(b(0xD0), parse("ret nc")); + assertArrayEquals(b(0xD8), parse("ret c")); + assertArrayEquals(b(0xE0), parse("ret po")); + assertArrayEquals(b(0xE8), parse("ret pe")); + assertArrayEquals(b(0xF0), parse("ret p")); + assertArrayEquals(b(0xF8), parse("ret m")); + } + + @Test + public void testReti() { + assertArrayEquals(b(0xED, 0x4D), parse("reti")); + } + + @Test + public void testRetn() { + assertArrayEquals(b(0xED, 0x45), parse("retn")); + } + + @Test + public void testRl() { + assertArrayEquals(b(0xCB, 0x10), parse("rl b")); + assertArrayEquals(b(0xCB, 0x11), parse("rl c")); + assertArrayEquals(b(0xCB, 0x12), parse("rl d")); + assertArrayEquals(b(0xCB, 0x13), parse("rl e")); + assertArrayEquals(b(0xCB, 0x14), parse("rl h")); + assertArrayEquals(b(0xCB, 0x15), parse("rl l")); + assertArrayEquals(b(0xCB, 0x16), parse("rl (hl)")); + assertArrayEquals(b(0xCB, 0x17), parse("rl a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x16), parse("rl (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x16), parse("rl (iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testRl_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x14), parse("rl ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x15), parse("rl iyl")); + } + + @Test + public void testRla() { + assertArrayEquals(b(0x17), parse("rla")); + } + + @Test + public void testRlc() { + assertArrayEquals(b(0xCB, 0x00), parse("rlc b")); + assertArrayEquals(b(0xCB, 0x01), parse("rlc c")); + assertArrayEquals(b(0xCB, 0x02), parse("rlc d")); + assertArrayEquals(b(0xCB, 0x03), parse("rlc e")); + assertArrayEquals(b(0xCB, 0x04), parse("rlc h")); + assertArrayEquals(b(0xCB, 0x05), parse("rlc l")); + assertArrayEquals(b(0xCB, 0x06), parse("rlc (hl)")); + assertArrayEquals(b(0xCB, 0x07), parse("rlc a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x06), parse("rlc (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x06), parse("rlc (iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testRlc_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x04), parse("rlc ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x05), parse("rlc iyl")); + } + + @Test + public void testRlca() { + assertArrayEquals(b(0x07), parse("rlca")); + } + + @Test + public void testRld() { + assertArrayEquals(b(0xED, 0x6F), parse("rld")); + } + + @Test + public void testRr() { + assertArrayEquals(b(0xCB, 0x18), parse("rr b")); + assertArrayEquals(b(0xCB, 0x19), parse("rr c")); + assertArrayEquals(b(0xCB, 0x1A), parse("rr d")); + assertArrayEquals(b(0xCB, 0x1B), parse("rr e")); + assertArrayEquals(b(0xCB, 0x1C), parse("rr h")); + assertArrayEquals(b(0xCB, 0x1D), parse("rr l")); + assertArrayEquals(b(0xCB, 0x1E), parse("rr (hl)")); + assertArrayEquals(b(0xCB, 0x1F), parse("rr a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x1E), parse("rr (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x1E), parse("rr (iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testRr_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x1C), parse("rr ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x1D), parse("rr iyl")); + } + + @Test + public void testRra() { + assertArrayEquals(b(0x1F), parse("rra")); + } + + @Test + public void testRrc() { + assertArrayEquals(b(0xCB, 0x08), parse("rrc b")); + assertArrayEquals(b(0xCB, 0x09), parse("rrc c")); + assertArrayEquals(b(0xCB, 0x0A), parse("rrc d")); + assertArrayEquals(b(0xCB, 0x0B), parse("rrc e")); + assertArrayEquals(b(0xCB, 0x0C), parse("rrc h")); + assertArrayEquals(b(0xCB, 0x0D), parse("rrc l")); + assertArrayEquals(b(0xCB, 0x0E), parse("rrc (hl)")); + assertArrayEquals(b(0xCB, 0x0F), parse("rrc a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x0E), parse("rrc (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x0E), parse("rrc (iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testRrc_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x0C), parse("rrc ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x0D), parse("rrc iyl")); + } + + @Test + public void testRrca() { + assertArrayEquals(b(0x0F), parse("rrca")); + } + + @Test + public void testRrd() { + assertArrayEquals(b(0xED, 0x67), parse("rrd")); + } + + @Test + public void testRst() { + assertArrayEquals(b(0xC7), parse("rst 00H")); + assertArrayEquals(b(0xCF), parse("rst 08H")); + assertArrayEquals(b(0xD7), parse("rst 10H")); + assertArrayEquals(b(0xDF), parse("rst 18H")); + assertArrayEquals(b(0xE7), parse("rst 20H")); + assertArrayEquals(b(0xEF), parse("rst 28H")); + assertArrayEquals(b(0xF7), parse("rst 30H")); + assertArrayEquals(b(0xFF), parse("rst 38H")); + } + + @Test + public void testSbcA() { + assertArrayEquals(b(0x98), parse("sbc a,b")); + assertArrayEquals(b(0x99), parse("sbc a,c")); + assertArrayEquals(b(0x9A), parse("sbc a,d")); + assertArrayEquals(b(0x9B), parse("sbc a,e")); + assertArrayEquals(b(0x9C), parse("sbc a,h")); + assertArrayEquals(b(0x9D), parse("sbc a,l")); + assertArrayEquals(b(0x9E), parse("sbc a,(hl)")); + assertArrayEquals(b(0x9F), parse("sbc a,a")); + assertArrayEquals(b(0xDD, 0x9C), parse("sbc a,ixh")); + assertArrayEquals(b(0xFD, 0x9D), parse("sbc a,iyl")); + assertArrayEquals(b(0xDD, 0x9E, 0x47), parse("sbc a,(ix + 47H)")); + assertArrayEquals(b(0xFD, 0x9E, 0x86), parse("sbc a,(iy - 7AH)")); + } + + @Test + public void testSbcAN() { + assertArrayEquals(b(0xDE, 0x86), parse("sbc a,86H")); + } + + @Test + public void testSbcHL() { + assertArrayEquals(b(0xED, 0x42), parse("sbc hl,bc")); + assertArrayEquals(b(0xED, 0x52), parse("sbc hl,de")); + assertArrayEquals(b(0xED, 0x62), parse("sbc hl,hl")); + assertArrayEquals(b(0xED, 0x72), parse("sbc hl,sp")); + } + + @Test + public void testScf() { + assertArrayEquals(b(0x37), parse("scf")); + } + + @Test + public void testSet() { + assertArrayEquals(b(0xCB, 0xF8), parse("set 7,b")); + assertArrayEquals(b(0xCB, 0xF1), parse("set 6,c")); + assertArrayEquals(b(0xCB, 0xEA), parse("set 5,d")); + assertArrayEquals(b(0xCB, 0xE3), parse("set 4,e")); + assertArrayEquals(b(0xCB, 0xDC), parse("set 3,h")); + assertArrayEquals(b(0xCB, 0xD5), parse("set 2,l")); + assertArrayEquals(b(0xCB, 0xCE), parse("set 1,(hl)")); + assertArrayEquals(b(0xCB, 0xC7), parse("set 0,a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0xDE), parse("set 3,(ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0xE6), parse("set 4,(iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testSet_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0xDC), parse("set 3,ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0xD5), parse("set 2,iyl")); + } + + @Test + public void testSla() { + assertArrayEquals(b(0xCB, 0x20), parse("sla b")); + assertArrayEquals(b(0xCB, 0x21), parse("sla c")); + assertArrayEquals(b(0xCB, 0x22), parse("sla d")); + assertArrayEquals(b(0xCB, 0x23), parse("sla e")); + assertArrayEquals(b(0xCB, 0x24), parse("sla h")); + assertArrayEquals(b(0xCB, 0x25), parse("sla l")); + assertArrayEquals(b(0xCB, 0x26), parse("sla (hl)")); + assertArrayEquals(b(0xCB, 0x27), parse("sla a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x26), parse("sla (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x26), parse("sla (iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testSla_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x24), parse("sla ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x25), parse("sla iyl")); + } + + @Test + public void testSra() { + assertArrayEquals(b(0xCB, 0x28), parse("sra b")); + assertArrayEquals(b(0xCB, 0x29), parse("sra c")); + assertArrayEquals(b(0xCB, 0x2A), parse("sra d")); + assertArrayEquals(b(0xCB, 0x2B), parse("sra e")); + assertArrayEquals(b(0xCB, 0x2C), parse("sra h")); + assertArrayEquals(b(0xCB, 0x2D), parse("sra l")); + assertArrayEquals(b(0xCB, 0x2E), parse("sra (hl)")); + assertArrayEquals(b(0xCB, 0x2F), parse("sra a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x2E), parse("sra (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x2E), parse("sra (iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testSra_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x2C), parse("sra ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x2D), parse("sra iyl")); + } + + @Test + public void testSrl() { + assertArrayEquals(b(0xCB, 0x38), parse("srl b")); + assertArrayEquals(b(0xCB, 0x39), parse("srl c")); + assertArrayEquals(b(0xCB, 0x3A), parse("srl d")); + assertArrayEquals(b(0xCB, 0x3B), parse("srl e")); + assertArrayEquals(b(0xCB, 0x3C), parse("srl h")); + assertArrayEquals(b(0xCB, 0x3D), parse("srl l")); + assertArrayEquals(b(0xCB, 0x3E), parse("srl (hl)")); + assertArrayEquals(b(0xCB, 0x3F), parse("srl a")); + assertArrayEquals(b(0xDD, 0xCB, 0x47, 0x3E), parse("srl (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xCB, 0x86, 0x3E), parse("srl (iy - 7AH)")); + } + + @Test(expected=ArgumentException.class) + public void testSrl_Invalid() { + assertArrayEquals(b(0xDD, 0xCB, 0x3C), parse("srl ixh")); + assertArrayEquals(b(0xFD, 0xCB, 0x3D), parse("srl iyl")); + } + + @Test + public void testSub() { + assertArrayEquals(b(0x90), parse("sub b")); + assertArrayEquals(b(0x91), parse("sub c")); + assertArrayEquals(b(0x92), parse("sub d")); + assertArrayEquals(b(0x93), parse("sub e")); + assertArrayEquals(b(0x94), parse("sub h")); + assertArrayEquals(b(0x95), parse("sub l")); + assertArrayEquals(b(0x96), parse("sub (hl)")); + assertArrayEquals(b(0x97), parse("sub a")); + assertArrayEquals(b(0xDD, 0x94), parse("sub ixh")); + assertArrayEquals(b(0xFD, 0x95), parse("sub iyl")); + assertArrayEquals(b(0xDD, 0x96, 0x47), parse("sub (ix + 47H)")); + assertArrayEquals(b(0xFD, 0x96, 0x86), parse("sub (iy - 7AH)")); + } + + @Test + public void testSubN() { + assertArrayEquals(b(0xD6, 0x86), parse("sub 86H")); + } + + @Test + public void testXor() { + assertArrayEquals(b(0xA8), parse("xor b")); + assertArrayEquals(b(0xA9), parse("xor c")); + assertArrayEquals(b(0xAA), parse("xor d")); + assertArrayEquals(b(0xAB), parse("xor e")); + assertArrayEquals(b(0xAC), parse("xor h")); + assertArrayEquals(b(0xAD), parse("xor l")); + assertArrayEquals(b(0xAE), parse("xor (hl)")); + assertArrayEquals(b(0xAF), parse("xor a")); + assertArrayEquals(b(0xDD, 0xAC), parse("xor ixh")); + assertArrayEquals(b(0xFD, 0xAD), parse("xor iyl")); + assertArrayEquals(b(0xDD, 0xAE, 0x47), parse("xor (ix + 47H)")); + assertArrayEquals(b(0xFD, 0xAE, 0x86), parse("xor (iy - 7AH)")); + } + + @Test + public void testXorN() { + assertArrayEquals(b(0xEE, 0x86), parse("xor 86H")); + } + + public byte[] parse(String string) { + LineNumberReader reader = new LineNumberReader(new StringReader(" " + string)); + Line line = new Parser().parse(reader, new Scope(new GlobalScope()), null); + line.resolve(0x4321); + byte[] bytes = line.getBytes(); + assertEquals(bytes.length, line.getSize()); + return bytes; + } + + public byte[] b(int... values) { + byte[] bytes = new byte[values.length]; + for (int i = 0; i < values.length; i++) + bytes[i] = (byte)values[i]; + return bytes; + } + +} diff --git a/src/nl/grauw/glass/Assembler.java b/src/nl/grauw/glass/Assembler.java new file mode 100644 index 0000000..e89d2e7 --- /dev/null +++ b/src/nl/grauw/glass/Assembler.java @@ -0,0 +1,75 @@ +package nl.grauw.glass; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +public class Assembler { + + private static Assembler instance; + private final Source source; + + /** + * @param args + */ + public static void main(String[] args) { + if (args.length == 0) { + System.out.println("Usage: java -jar glass.jar [OPTION] SOURCE [OBJECT] [SYMBOL]"); + System.exit(1); + } + + File sourcePath = null; + File objectPath = null; + File symbolPath = null; + List includePaths = new ArrayList(); + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-I") && (i + 1) < args.length) { + includePaths.add(new File(args[++i])); + } else if (sourcePath == null) { + sourcePath = new File(args[i]); + } else if (objectPath == null) { + objectPath = new File(args[i]); + } else if (symbolPath == null) { + symbolPath = new File(args[i]); + } else { + throw new AssemblyException("Too many arguments."); + } + } + + instance = new Assembler(sourcePath, includePaths); + instance.writeObject(objectPath); + if (symbolPath != null) + instance.writeSymbols(symbolPath); + } + + public Assembler(File sourcePath, List includePaths) { + source = new SourceBuilder(includePaths).parse(sourcePath); + } + + public void writeObject(File objectPath) { + try (OutputStream output = objectPath != null ? new FileOutputStream(objectPath) : new NullOutputStream()) { + source.assemble(output); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void writeSymbols(File symbolPath) { + try (PrintStream symbolOutput = new PrintStream(symbolPath)) { + symbolOutput.print(source.getScope().serializeSymbols()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static class NullOutputStream extends OutputStream { + public void write(int b) throws IOException {} + public void write(byte[] b) throws IOException {} + public void write(byte[] b, int off, int len) throws IOException {} + } + +} diff --git a/src/nl/grauw/glass/AssemblyException.java b/src/nl/grauw/glass/AssemblyException.java new file mode 100644 index 0000000..ef69588 --- /dev/null +++ b/src/nl/grauw/glass/AssemblyException.java @@ -0,0 +1,80 @@ +package nl.grauw.glass; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class AssemblyException extends RuntimeException { + private static final long serialVersionUID = 1L; + + private final List contexts = new ArrayList<>(); + + public AssemblyException() { + this((Throwable)null); + } + + public AssemblyException(String message) { + this(message, null); + } + + public AssemblyException(Throwable cause) { + this("Error during assembly.", null); + } + + public AssemblyException(String message, Throwable cause) { + super(message, cause); + } + + public void addContext(Line line) { + addContext(line.getSourceFile(), line.getLineNumber(), -1, line.getSourceText()); + } + + public void addContext(File file, int line, int column, String text) { + contexts.add(new Context(file, line, column, text)); + } + + @Override + public String getMessage() { + String message = super.getMessage(); + + for (Context context : contexts) + message += "\n" + context; + + return message; + } + + public String getPlainMessage() { + return super.getMessage(); + } + + private static class Context { + + private final File file; + private final int line; + private final int column; + private final String text; + + public Context(File file, int line, int column, String text) { + this.file = file; + this.line = line; + this.column = column; + this.text = text; + } + + @Override + public String toString() { + String prefix = "[at " + file + ":" + line + (column != -1 ? "," + column : "") + "]\n"; + String context = prefix + text; + + if (column >= 0) { + int start = Math.min(context.lastIndexOf('\n') + 1, context.length()); + int end = Math.min(start + column, context.length()); + context += "\n" + context.substring(start, end).replaceAll("[^\t]", " ") + "^"; + } + + return context; + } + + } + +} diff --git a/src/nl/grauw/glass/GlobalScope.java b/src/nl/grauw/glass/GlobalScope.java new file mode 100644 index 0000000..9173a4a --- /dev/null +++ b/src/nl/grauw/glass/GlobalScope.java @@ -0,0 +1,106 @@ +package nl.grauw.glass; + +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Instruction; +import nl.grauw.glass.instructions.*; +import nl.grauw.glass.instructions.Error; + +public class GlobalScope extends Scope { + + public GlobalScope() { + super(); + setAddress(0); + + addBuiltInSymbol("adc", new Instruction(new Adc())); + addBuiltInSymbol("add", new Instruction(new Add())); + addBuiltInSymbol("and", new Instruction(new And())); + addBuiltInSymbol("bit", new Instruction(new Bit())); + addBuiltInSymbol("call", new Instruction(new Call())); + addBuiltInSymbol("ccf", new Instruction(new Ccf())); + addBuiltInSymbol("cp", new Instruction(new Cp())); + addBuiltInSymbol("cpd", new Instruction(new Cpd())); + addBuiltInSymbol("cpdr", new Instruction(new Cpdr())); + addBuiltInSymbol("cpi", new Instruction(new Cpi())); + addBuiltInSymbol("cpir", new Instruction(new Cpir())); + addBuiltInSymbol("cpl", new Instruction(new Cpl())); + addBuiltInSymbol("daa", new Instruction(new Daa())); + addBuiltInSymbol("db", new Instruction(new Db())); + addBuiltInSymbol("dd", new Instruction(new Dd())); + addBuiltInSymbol("dec", new Instruction(new Dec())); + addBuiltInSymbol("di", new Instruction(new Di())); + addBuiltInSymbol("djnz", new Instruction(new Djnz())); + addBuiltInSymbol("ds", new Instruction(new Ds())); + addBuiltInSymbol("dw", new Instruction(new Dw())); + addBuiltInSymbol("ei", new Instruction(new Ei())); + addBuiltInSymbol("ex", new Instruction(new Ex())); + addBuiltInSymbol("exx", new Instruction(new Exx())); + addBuiltInSymbol("halt", new Instruction(new Halt())); + addBuiltInSymbol("im", new Instruction(new Im())); + addBuiltInSymbol("in", new Instruction(new In())); + addBuiltInSymbol("inc", new Instruction(new Inc())); + addBuiltInSymbol("ind", new Instruction(new Ind())); + addBuiltInSymbol("indr", new Instruction(new Indr())); + addBuiltInSymbol("ini", new Instruction(new Ini())); + addBuiltInSymbol("inir", new Instruction(new Inir())); + addBuiltInSymbol("jp", new Instruction(new Jp())); + addBuiltInSymbol("jr", new Instruction(new Jr())); + addBuiltInSymbol("ld", new Instruction(new Ld())); + addBuiltInSymbol("ldd", new Instruction(new Ldd())); + addBuiltInSymbol("lddr", new Instruction(new Lddr())); + addBuiltInSymbol("ldi", new Instruction(new Ldi())); + addBuiltInSymbol("ldir", new Instruction(new Ldir())); + addBuiltInSymbol("mulub", new Instruction(new Mulub())); + addBuiltInSymbol("muluw", new Instruction(new Muluw())); + addBuiltInSymbol("neg", new Instruction(new Neg())); + addBuiltInSymbol("nop", new Instruction(new Nop())); + addBuiltInSymbol("or", new Instruction(new Or())); + addBuiltInSymbol("otdr", new Instruction(new Otdr())); + addBuiltInSymbol("otir", new Instruction(new Otir())); + addBuiltInSymbol("out", new Instruction(new Out())); + addBuiltInSymbol("outi", new Instruction(new Outi())); + addBuiltInSymbol("outd", new Instruction(new Outd())); + addBuiltInSymbol("pop", new Instruction(new Pop())); + addBuiltInSymbol("push", new Instruction(new Push())); + addBuiltInSymbol("res", new Instruction(new Res())); + addBuiltInSymbol("ret", new Instruction(new Ret())); + addBuiltInSymbol("reti", new Instruction(new Reti())); + addBuiltInSymbol("retn", new Instruction(new Retn())); + addBuiltInSymbol("rl", new Instruction(new Rl())); + addBuiltInSymbol("rla", new Instruction(new Rla())); + addBuiltInSymbol("rlc", new Instruction(new Rlc())); + addBuiltInSymbol("rlca", new Instruction(new Rlca())); + addBuiltInSymbol("rld", new Instruction(new Rld())); + addBuiltInSymbol("rr", new Instruction(new Rr())); + addBuiltInSymbol("rra", new Instruction(new Rra())); + addBuiltInSymbol("rrc", new Instruction(new Rrc())); + addBuiltInSymbol("rrca", new Instruction(new Rrca())); + addBuiltInSymbol("rrd", new Instruction(new Rrd())); + addBuiltInSymbol("rst", new Instruction(new Rst())); + addBuiltInSymbol("sbc", new Instruction(new Sbc())); + addBuiltInSymbol("scf", new Instruction(new Scf())); + addBuiltInSymbol("set", new Instruction(new Set())); + addBuiltInSymbol("sla", new Instruction(new Sla())); + addBuiltInSymbol("sra", new Instruction(new Sra())); + addBuiltInSymbol("srl", new Instruction(new Srl())); + addBuiltInSymbol("sub", new Instruction(new Sub())); + addBuiltInSymbol("xor", new Instruction(new Xor())); + + addBuiltInSymbol("include", new Instruction(new Include())); + addBuiltInSymbol("equ", new Instruction(new Equ())); + addBuiltInSymbol("org", new Instruction(new Org())); + addBuiltInSymbol("endm", new Instruction(new Endm())); + addBuiltInSymbol("endp", new Instruction(new Endp())); + addBuiltInSymbol("ends", new Instruction(new Ends())); + addBuiltInSymbol("end", new Instruction(new End())); + addBuiltInSymbol("endif", new Instruction(new Endif())); + addBuiltInSymbol("else", new Instruction(new Else())); + addBuiltInSymbol("error", new Instruction(new Error())); + addBuiltInSymbol("warning", new Instruction(new Warning())); + } + + private void addBuiltInSymbol(String symbol, Expression value) { + addSymbol(symbol, value); + addSymbol(symbol.toUpperCase(), value); + } + +} diff --git a/src/nl/grauw/glass/Line.java b/src/nl/grauw/glass/Line.java new file mode 100644 index 0000000..5309a1b --- /dev/null +++ b/src/nl/grauw/glass/Line.java @@ -0,0 +1,143 @@ +package nl.grauw.glass; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import nl.grauw.glass.directives.Directive; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.instructions.Empty; +import nl.grauw.glass.instructions.InstructionFactory; +import nl.grauw.glass.instructions.InstructionObject; + +public class Line { + + private final Scope scope; + private final String label; + private final String mnemonic; + private final Expression arguments; + private final String comment; + private final File sourceFile; + private final int lineNumber; + private final String sourceText; + + private InstructionFactory instruction; + private InstructionObject instructionObject; + private Directive directive; + + public Line(Scope scope, String label, String mnemonic, Expression arguments, String comment, File sourceFile, int lineNumber, String sourceText) { + this.scope = scope; + this.label = label; + this.mnemonic = mnemonic; + this.arguments = arguments; + this.comment = comment; + this.sourceFile = sourceFile; + this.lineNumber = lineNumber; + this.sourceText = sourceText; + } + + public Line(Scope scope, Line other) { + this(scope, other.label, other.mnemonic, other.arguments != null ? other.arguments.copy(scope) : null, + other.comment, other.sourceFile, other.lineNumber, other.sourceText); + directive = other.directive; + } + + public Scope getScope() { + return scope; + } + + public String getLabel() { + return label; + } + + public String getMnemonic() { + return mnemonic; + } + + public Expression getArguments() { + return arguments; + } + + public String getComment() { + return comment; + } + + public File getSourceFile() { + return sourceFile; + } + + public int getLineNumber() { + return lineNumber; + } + + public String getSourceText() { + return sourceText; + } + + public void setDirective(Directive directive) { + this.directive = directive; + } + + public void setInstruction(InstructionFactory instruction) { + this.instruction = instruction; + } + + public InstructionFactory getInstruction() { + if (instruction == null) + instruction = mnemonic != null ? scope.getSymbol(mnemonic).getInstruction() : Empty.INSTANCE; + return instruction; + } + + public void register(Scope sourceScope) { + try { + directive.register(sourceScope, this); + } catch (AssemblyException e) { + e.addContext(this); + throw e; + } + } + + public List expand() { + try { + return getInstruction().expand(this); + } catch (AssemblyException e) { + e.addContext(this); + throw e; + } + } + + public int resolve(int address) { + try { + instructionObject = getInstruction().createObject(scope, arguments); + return instructionObject.resolve(address); + } catch (AssemblyException e) { + e.addContext(this); + throw e; + } + } + + public void generateObjectCode(OutputStream output) throws IOException { + try { + instructionObject.generateObjectCode(output); + } catch (AssemblyException e) { + e.addContext(this); + throw e; + } + } + + public int getSize() { + return instructionObject.getSize(); + } + + public byte[] getBytes() { + return instructionObject.getBytes(); + } + + public String toString() { + return (label != null ? label + ":" : "") + + (mnemonic != null ? (label != null ? " " : "\t") + mnemonic + (arguments != null ? " " + arguments : "") : "") + + (comment != null ? (label != null || mnemonic != null ? " ;" : ";") + comment : ""); + } + +} diff --git a/src/nl/grauw/glass/LineBuilder.java b/src/nl/grauw/glass/LineBuilder.java new file mode 100644 index 0000000..ac54251 --- /dev/null +++ b/src/nl/grauw/glass/LineBuilder.java @@ -0,0 +1,55 @@ +package nl.grauw.glass; + +import java.io.File; + +import nl.grauw.glass.expressions.Expression; + +public class LineBuilder { + + private String label; + private String mnemonic; + private Expression arguments; + private String comment; + private String sourceText; + + public void setLabel(String label) { + if (this.label != null) + throw new AssemblyException("Label already set."); + this.label = label; + } + + public void setMnemonic(String mnemonic) { + if (this.mnemonic != null) + throw new AssemblyException("Mnemonic already set."); + this.mnemonic = mnemonic; + } + + public void setArguments(Expression arguments) { + if (this.arguments != null) + throw new AssemblyException("Arguments already set."); + this.arguments = arguments; + } + + public void setComment(String comment) { + this.comment = this.comment == null ? comment : this.comment + "\n" + comment; + } + + public void setSourceText(String sourceText) { + if (this.sourceText != null) + throw new AssemblyException("Source text already set."); + this.sourceText = sourceText; + } + + public Line getLine(Scope scope, File sourceFile, int lineNumber) { + Line line = new Line(scope, label, mnemonic, arguments, comment, sourceFile, lineNumber, sourceText); + + label = null; + mnemonic = null; + arguments = null; + comment = null; + sourceText = null; + + return line; + } + +} diff --git a/src/nl/grauw/glass/ParameterScope.java b/src/nl/grauw/glass/ParameterScope.java new file mode 100644 index 0000000..2fa3d3e --- /dev/null +++ b/src/nl/grauw/glass/ParameterScope.java @@ -0,0 +1,39 @@ +package nl.grauw.glass; + +import nl.grauw.glass.expressions.Equals; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Identifier; +import nl.grauw.glass.instructions.ArgumentException; + +public class ParameterScope extends Scope { + + public ParameterScope(Scope parent, Expression parameters, Expression arguments) { + super(parent); + + while (parameters != null) { + Expression parameter = parameters.getElement(); + Expression argument; + + if (parameter instanceof Equals) { + argument = arguments != null ? arguments.getElement() : ((Equals)parameter).getTerm2(); + parameter = ((Equals)parameter).getTerm1(); + } else { + if (arguments == null) + throw new ArgumentException("Not enough arguments."); + argument = arguments.getElement(); + } + + if (!(parameter instanceof Identifier)) + throw new ArgumentException("Parameter must be an identifier."); + + addSymbol(((Identifier)parameter).getName(), argument); + + parameters = parameters.getNext(); + if (arguments != null) + arguments = arguments.getNext(); + } + if (arguments != null) + throw new ArgumentException("Too many arguments."); + } + +} diff --git a/src/nl/grauw/glass/Parser.java b/src/nl/grauw/glass/Parser.java new file mode 100644 index 0000000..6852e02 --- /dev/null +++ b/src/nl/grauw/glass/Parser.java @@ -0,0 +1,666 @@ +package nl.grauw.glass; + +import java.io.File; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.ArrayList; + +import nl.grauw.glass.expressions.CharacterLiteral; +import nl.grauw.glass.expressions.ExpressionBuilder; +import nl.grauw.glass.expressions.Identifier; +import nl.grauw.glass.expressions.IntegerLiteral; +import nl.grauw.glass.expressions.StringLiteral; + +public class Parser { + + private Scope scope; + private LineBuilder lineBuilder = new LineBuilder(); + + private State state; + private StringBuilder accumulator = new StringBuilder(); + private ExpressionBuilder expressionBuilder = new ExpressionBuilder(); + + public Line parse(LineNumberReader reader, Scope scope, File sourceFile) { + this.scope = scope; + state = labelStartState; + + final int firstLineNumber = reader.getLineNumber(); + int lineNumber = firstLineNumber; + int column = 0; + ArrayList sourceLines = new ArrayList(); + try { + while (state != endState) + { + String sourceLine = reader.readLine(); + if (sourceLine == null) { + state = state.parse('\0'); + if (state != endState) + throw new AssemblyException("Unexpected end of file."); + if (sourceLines.size() > 0) + break; // return null (parsing end) the next time + return null; + } + sourceLines.add(sourceLine); + lineNumber = reader.getLineNumber(); + column = 0; + + for (int i = 0, length = sourceLine.length(); i < length; i++) { + column = i; + state = state.parse(sourceLine.charAt(i)); + } + column = sourceLine.length(); + state = state.parse('\n'); + } + + if (accumulator.length() > 0) + throw new AssemblyException("Accumulator not consumed. Value: " + accumulator.toString()); + } catch(AssemblyException e) { + e.addContext(sourceFile, lineNumber, column, String.join("\n", sourceLines)); + throw e; + } catch (IOException e) { + throw new AssemblyException(e); + } + + lineBuilder.setSourceText(String.join("\n", sourceLines)); + + return lineBuilder.getLine(scope, sourceFile, firstLineNumber); + } + + private abstract class State { + public abstract State parse(char character); + + public boolean isWhitespace(char character) { + return character == ' ' || character == '\t'; + } + + public boolean isIdentifier(char character) { + return isIdentifierStart(character) || character >= '0' && character <= '9' || + character == '\'' || character == '$'; + } + + public boolean isIdentifierStart(char character) { + return character >= 'a' && character <= 'z' || character >= 'A' && character <= 'Z' || + character == '_' || character == '.' || character == '?' || character == '@'; + } + + } + + private LabelStartState labelStartState = new LabelStartState(); + private class LabelStartState extends State { + public State parse(char character) { + if (isIdentifierStart(character)) { + accumulator.append(character); + return labelReadState; + } else if (isWhitespace(character)) { + return statementStartState; + } else if (character == ';') { + return commentReadState; + } else if (character == '\n' || character == '\0') { + return endState; + } + throw new SyntaxError(); + } + } + + private LabelReadState labelReadState = new LabelReadState(); + private class LabelReadState extends State { + public State parse(char character) { + if (isIdentifier(character)) { + accumulator.append(character); + return labelReadState; + } else { + lineBuilder.setLabel(accumulator.toString()); + accumulator.setLength(0); + if (character == ':' || isWhitespace(character)) { + return statementStartState; + } else if (character == ';') { + return commentReadState; + } else if (character == '\n' || character == '\0') { + return endState; + } + } + throw new SyntaxError(); + } + } + + private StatementStartState statementStartState = new StatementStartState(); + private class StatementStartState extends State { + public State parse(char character) { + if (isIdentifierStart(character)) { + accumulator.append(character); + return statementReadState; + } else if (isWhitespace(character)) { + return statementStartState; + } else if (character == ';') { + return commentReadState; + } else if (character == '\n' || character == '\0') { + return endState; + } + throw new SyntaxError(); + } + } + + private StatementReadState statementReadState = new StatementReadState(); + private class StatementReadState extends State { + public State parse(char character) { + if (isIdentifier(character)) { + accumulator.append(character); + return statementReadState; + } if (character == ':') { + lineBuilder.setLabel(accumulator.toString()); + accumulator.setLength(0); + return statementStartState; + } else { + lineBuilder.setMnemonic(accumulator.toString()); + accumulator.setLength(0); + if (isWhitespace(character)) { + return argumentStartState; + } else if (character == ';') { + return commentReadState; + } else if (character == '\n' || character == '\0') { + return endState; + } + } + throw new SyntaxError(); + } + } + + private ArgumentStartState argumentStartState = new ArgumentStartState(); + private class ArgumentStartState extends State { + public State parse(char character) { + if (character == ';') { + return commentReadState; + } else if (character == '\n' || character == '\0') { + return endState; + } else if (isWhitespace(character)) { + return argumentStartState; + } else { + return argumentValueState.parse(character); + } + } + } + + private ArgumentValueState argumentValueState = new ArgumentValueState(); + private class ArgumentValueState extends State { + public State parse(char character) { + if (isIdentifierStart(character)) { + accumulator.append(character); + return argumentIdentifierState; + } else if (character == '0') { + accumulator.append(character); + return argumentZeroState; + } else if (character >= '1' && character <= '9') { + accumulator.append(character); + return argumentNumberState; + } else if (character == '#') { + return argumentHexadecimalState; + } else if (character == '$') { + return argumentDollarState; + } else if (character == '%') { + return argumentBinaryState; + } else if (character == '"') { + return argumentStringState; + } else if (character == '\'') { + return argumentCharacterState; + } else if (character == '+') { + expressionBuilder.addOperatorToken(expressionBuilder.POSITIVE); + return argumentValueState; + } else if (character == '-') { + expressionBuilder.addOperatorToken(expressionBuilder.NEGATIVE); + return argumentValueState; + } else if (character == '~') { + expressionBuilder.addOperatorToken(expressionBuilder.COMPLEMENT); + return argumentValueState; + } else if (character == '!') { + expressionBuilder.addOperatorToken(expressionBuilder.NOT); + return argumentValueState; + } else if (character == '(') { + expressionBuilder.addOperatorToken(expressionBuilder.GROUP_OPEN); + return argumentValueState; + } else if (isWhitespace(character)) { + return argumentValueState; + } else if (character == ';') { + return commentReadThenArgumentState; + } else if (character == '\n') { + return argumentValueState; + } + throw new SyntaxError(); + } + } + + private ArgumentIdentifierState argumentIdentifierState = new ArgumentIdentifierState(); + private class ArgumentIdentifierState extends State { + public State parse(char character) { + if (isIdentifier(character)) { + accumulator.append(character); + return argumentIdentifierState; + } else { + expressionBuilder.addValueToken(new Identifier(accumulator.toString(), scope)); + accumulator.setLength(0); + return argumentOperatorState.parse(character); + } + } + } + + private ArgumentStringState argumentStringState = new ArgumentStringState(); + private class ArgumentStringState extends State { + public State parse(char character) { + if (character == '"') { + expressionBuilder.addValueToken(new StringLiteral(accumulator.toString())); + accumulator.setLength(0); + return argumentOperatorState; + } else if (character == '\\') { + return argumentStringEscapeState; + } else if (character == '\n' || character == '\0') { + throw new SyntaxError(); + } else { + accumulator.append(character); + return argumentStringState; + } + } + } + + private ArgumentStringEscapeState argumentStringEscapeState = new ArgumentStringEscapeState(); + private class ArgumentStringEscapeState extends State { + public State parse(char character) { + if (character == '0') { + accumulator.append('\0'); + return argumentStringState; + } else if (character == 'a') { + accumulator.append('\7'); + return argumentStringState; + } else if (character == 't') { + accumulator.append('\t'); + return argumentStringState; + } else if (character == 'n') { + accumulator.append('\n'); + return argumentStringState; + } else if (character == 'f') { + accumulator.append('\f'); + return argumentStringState; + } else if (character == 'r') { + accumulator.append('\r'); + return argumentStringState; + } else if (character == 'e') { + accumulator.append('\33'); + return argumentStringState; + } else if (character == '"') { + accumulator.append('"'); + return argumentStringState; + } else if (character == '\'') { + accumulator.append('\''); + return argumentStringState; + } else if (character == '\\') { + accumulator.append('\\'); + return argumentStringState; + } else if (character == '\n' || character == '\0') { + throw new SyntaxError(); + } else { + throw new SyntaxError(); + } + } + } + + private ArgumentCharacterState argumentCharacterState = new ArgumentCharacterState(); + private class ArgumentCharacterState extends State { + public State parse(char character) { + if (character == '\\') { + return argumentCharacterEscapeState; + } else if (character == '\'' || character == '\n' || character == '\0') { + throw new SyntaxError(); + } else { + accumulator.append(character); + return argumentCharacterEndState; + } + } + } + + private ArgumentCharacterEscapeState argumentCharacterEscapeState = new ArgumentCharacterEscapeState(); + private class ArgumentCharacterEscapeState extends State { + public State parse(char character) { + State state = argumentStringEscapeState.parse(character); + if (state == argumentStringState) + return argumentCharacterEndState; + throw new AssemblyException("Unexpected state."); + } + } + + private ArgumentCharacterEndState argumentCharacterEndState = new ArgumentCharacterEndState(); + private class ArgumentCharacterEndState extends State { + public State parse(char character) { + if (character == '\'') { + expressionBuilder.addValueToken(new CharacterLiteral(accumulator.charAt(0))); + accumulator.setLength(0); + return argumentOperatorState; + } else { + throw new SyntaxError(); + } + } + } + + private ArgumentZeroState argumentZeroState = new ArgumentZeroState(); + private class ArgumentZeroState extends State { + public State parse(char character) { + if (character == 'x' || character == 'X') { + accumulator.setLength(0); + return argumentHexadecimalState; + } else { + return argumentNumberState.parse(character); + } + } + } + + private ArgumentNumberState argumentNumberState = new ArgumentNumberState(); + private class ArgumentNumberState extends State { + public State parse(char character) { + if (character >= '0' && character <= '9' || character >= 'A' && character <= 'F' || + character >= 'a' && character <= 'f') { + accumulator.append(character); + return argumentNumberState; + } else { + String string = accumulator.toString(); + if (character == 'H' || character == 'h') { + int value = parseInt(string, 16); + expressionBuilder.addValueToken(new IntegerLiteral(value)); + accumulator.setLength(0); + return argumentOperatorState; + } else if (character == 'O' || character == 'o') { + int value = parseInt(string, 8); + expressionBuilder.addValueToken(new IntegerLiteral(value)); + accumulator.setLength(0); + return argumentOperatorState; + } else { + if (string.endsWith("B") || string.endsWith("b")) { + int value = parseInt(string.substring(0, string.length() - 1), 2); + expressionBuilder.addValueToken(new IntegerLiteral(value)); + accumulator.setLength(0); + } else { + int value = parseInt(string, 10); + expressionBuilder.addValueToken(new IntegerLiteral(value)); + accumulator.setLength(0); + } + return argumentOperatorState.parse(character); + } + } + } + } + + private ArgumentDollarState argumentDollarState = new ArgumentDollarState(); + private class ArgumentDollarState extends State { + public State parse(char character) { + if (character >= '0' && character <= '9' || character >= 'A' && character <= 'F' || + character >= 'a' && character <= 'f') { + accumulator.append(character); + return argumentHexadecimalState; + } else { + expressionBuilder.addValueToken(new Identifier("$", scope)); + accumulator.setLength(0); + return argumentOperatorState.parse(character); + } + } + } + + private ArgumentHexadecimalState argumentHexadecimalState = new ArgumentHexadecimalState(); + private class ArgumentHexadecimalState extends State { + public State parse(char character) { + if (character >= '0' && character <= '9' || character >= 'A' && character <= 'F' || + character >= 'a' && character <= 'f') { + accumulator.append(character); + return argumentHexadecimalState; + } else { + int value = parseInt(accumulator.toString(), 16); + expressionBuilder.addValueToken(new IntegerLiteral(value)); + accumulator.setLength(0); + return argumentOperatorState.parse(character); + } + } + } + + private ArgumentBinaryState argumentBinaryState = new ArgumentBinaryState(); + private class ArgumentBinaryState extends State { + public State parse(char character) { + if (character >= '0' && character <= '1') { + accumulator.append(character); + return argumentBinaryState; + } else { + int value = parseInt(accumulator.toString(), 2); + expressionBuilder.addValueToken(new IntegerLiteral(value)); + accumulator.setLength(0); + return argumentOperatorState.parse(character); + } + } + } + + private ArgumentOperatorState argumentOperatorState = new ArgumentOperatorState(); + private class ArgumentOperatorState extends State { + public State parse(char character) { + if (character == ')') { + expressionBuilder.addOperatorToken(expressionBuilder.GROUP_CLOSE); + return argumentOperatorState; + } else if (character == '[') { + expressionBuilder.addOperatorToken(expressionBuilder.INDEX_OPEN); + return argumentValueState; + } else if (character == ']') { + expressionBuilder.addOperatorToken(expressionBuilder.INDEX_CLOSE); + return argumentOperatorState; + } else if (character == '.') { + expressionBuilder.addOperatorToken(expressionBuilder.MEMBER); + return argumentValueState; + } else if (character == '*') { + expressionBuilder.addOperatorToken(expressionBuilder.MULTIPLY); + return argumentValueState; + } else if (character == '/') { + expressionBuilder.addOperatorToken(expressionBuilder.DIVIDE); + return argumentValueState; + } else if (character == '%') { + expressionBuilder.addOperatorToken(expressionBuilder.MODULO); + return argumentValueState; + } else if (character == '+') { + expressionBuilder.addOperatorToken(expressionBuilder.ADD); + return argumentValueState; + } else if (character == '-') { + expressionBuilder.addOperatorToken(expressionBuilder.SUBTRACT); + return argumentValueState; + } else if (character == '<') { + return argumentLessThanState; + } else if (character == '>') { + return argumentGreaterThanState; + } else if (character == '=') { + expressionBuilder.addOperatorToken(expressionBuilder.EQUALS); + return argumentValueState; + } else if (character == '!') { + return argumentNotEqualsState; + } else if (character == '&') { + return argumentAndState; + } else if (character == '^') { + expressionBuilder.addOperatorToken(expressionBuilder.XOR); + return argumentValueState; + } else if (character == '|') { + return argumentOrState; + } else if (character == '?') { + expressionBuilder.addOperatorToken(expressionBuilder.TERNARYIF); + return argumentValueState; + } else if (character == ':') { + expressionBuilder.addOperatorToken(expressionBuilder.TERNARYELSE); + return argumentValueState; + } else if (character == ',') { + expressionBuilder.addOperatorToken(expressionBuilder.SEQUENCE); + return argumentValueState; + } else if (isWhitespace(character)) { + return argumentOperatorState; + } else if (character == ';') { + if (!expressionBuilder.hasOpenGroup()) { + lineBuilder.setArguments(expressionBuilder.getExpression()); + return commentReadState; + } else { + return commentReadThenOperatorState; + } + } else if (character == '\n' || character == '\0') { + if (!expressionBuilder.hasOpenGroup() || character == '\0') { + lineBuilder.setArguments(expressionBuilder.getExpression()); + return endState; + } else { + return argumentOperatorState; + } + } else { + expressionBuilder.addOperatorToken(expressionBuilder.ANNOTATION); + return argumentValueState.parse(character); + } + } + } + + private ArgumentLessThanState argumentLessThanState = new ArgumentLessThanState(); + private class ArgumentLessThanState extends State { + public State parse(char character) { + if (character == '<') { + expressionBuilder.addOperatorToken(expressionBuilder.SHIFT_LEFT); + return argumentValueState; + } else if (character == '=') { + expressionBuilder.addOperatorToken(expressionBuilder.LESS_OR_EQUALS); + return argumentValueState; + } else { + expressionBuilder.addOperatorToken(expressionBuilder.LESS_THAN); + return argumentValueState.parse(character); + } + } + } + + private ArgumentGreaterThanState argumentGreaterThanState = new ArgumentGreaterThanState(); + private class ArgumentGreaterThanState extends State { + public State parse(char character) { + if (character == '>') { + expressionBuilder.addOperatorToken(expressionBuilder.SHIFT_RIGHT); + return argumentValueState; + } else if (character == '=') { + expressionBuilder.addOperatorToken(expressionBuilder.GREATER_OR_EQUALS); + return argumentValueState; + } else { + expressionBuilder.addOperatorToken(expressionBuilder.GREATER_THAN); + return argumentValueState.parse(character); + } + } + } + + private ArgumentNotEqualsState argumentNotEqualsState = new ArgumentNotEqualsState(); + private class ArgumentNotEqualsState extends State { + public State parse(char character) { + if (character == '=') { + expressionBuilder.addOperatorToken(expressionBuilder.NOT_EQUALS); + return argumentValueState; + } else { + expressionBuilder.addOperatorToken(expressionBuilder.ANNOTATION); + expressionBuilder.addOperatorToken(expressionBuilder.NOT); + return argumentValueState.parse(character); + } + } + } + + private ArgumentAndState argumentAndState = new ArgumentAndState(); + private class ArgumentAndState extends State { + public State parse(char character) { + if (character == '&') { + expressionBuilder.addOperatorToken(expressionBuilder.LOGICAL_AND); + return argumentValueState; + } else { + expressionBuilder.addOperatorToken(expressionBuilder.AND); + return argumentValueState.parse(character); + } + } + } + + private ArgumentOrState argumentOrState = new ArgumentOrState(); + private class ArgumentOrState extends State { + public State parse(char character) { + if (character == '|') { + expressionBuilder.addOperatorToken(expressionBuilder.LOGICAL_OR); + return argumentValueState; + } else { + expressionBuilder.addOperatorToken(expressionBuilder.OR); + return argumentValueState.parse(character); + } + } + } + + private CommentReadState commentReadState = new CommentReadState(); + private class CommentReadState extends State { + public State parse(char character) { + if (character == '\n' || character == '\0') { + lineBuilder.setComment(accumulator.toString()); + accumulator.setLength(0); + return endState; + } else { + accumulator.append(character); + return commentReadState; + } + } + } + + private CommentReadThenArgumentState commentReadThenArgumentState = new CommentReadThenArgumentState(); + private class CommentReadThenArgumentState extends State { + public State parse(char character) { + if (character == '\n' || character == '\0') { + lineBuilder.setComment(accumulator.toString()); + accumulator.setLength(0); + if (character == '\0') { + throw new SyntaxError(); + } else { + return argumentValueState; + } + } else { + accumulator.append(character); + return commentReadState; + } + } + } + + private CommentReadThenOperatorState commentReadThenOperatorState = new CommentReadThenOperatorState(); + private class CommentReadThenOperatorState extends State { + public State parse(char character) { + if (character == '\n' || character == '\0') { + lineBuilder.setComment(accumulator.toString()); + accumulator.setLength(0); + if (character == '\0') { + lineBuilder.setArguments(expressionBuilder.getExpression()); + return endState; + } else { + return argumentOperatorState; + } + } else { + accumulator.append(character); + return commentReadState; + } + } + } + + private EndState endState = new EndState(); + private class EndState extends State { + public State parse(char character) { + throw new AssemblyException("End state reached but not all characters consumed."); + } + } + + private static int parseInt(String string, int radix) { + try { + long value = Long.parseLong(string, radix); + if (value > 0xFFFFFFFFL) + throw new SyntaxError(); + return (int)value; + } catch (NumberFormatException e) { + throw new SyntaxError(); + } + } + + public static class SyntaxError extends AssemblyException { + private static final long serialVersionUID = 1L; + + public SyntaxError() { + this(null); + } + + public SyntaxError(Throwable cause) { + super("Syntax error.", cause); + } + + } + +} diff --git a/src/nl/grauw/glass/Scope.java b/src/nl/grauw/glass/Scope.java new file mode 100644 index 0000000..c833f35 --- /dev/null +++ b/src/nl/grauw/glass/Scope.java @@ -0,0 +1,119 @@ +package nl.grauw.glass; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import nl.grauw.glass.expressions.Context; +import nl.grauw.glass.expressions.ContextLiteral; +import nl.grauw.glass.expressions.EvaluationException; +import nl.grauw.glass.expressions.Expression; + +public class Scope implements Context { + + private boolean set = false; + private final Scope parent; + private final Map symbols = new HashMap<>(); + private int address = 0; + + public Scope() { + this(null); + } + + public Scope(Scope parent) { + this.parent = parent; + addSymbol("$", this); + } + + public Scope getParent() { + return parent; + } + + @Override + public int getAddress() { + if (!set) + throw new EvaluationException("Address not initialized."); + return address; + } + + public void setAddress(int address) { + if (set) + throw new AssemblyException("Address was already set."); + this.address = address; + this.set = true; + } + + public void addSymbol(String name, Expression value) { + if (name == null || value == null) + throw new AssemblyException("Symbol name and value must not be null."); + if (symbols.containsKey(name)) + throw new AssemblyException("Can not redefine symbol: " + name); + symbols.put(name, value); + } + + public void addSymbol(String name, Scope context) { + addSymbol(name, new ContextLiteral(context)); + } + + public boolean hasSymbol(String name) { + return getLocalSymbol(name) != null || parent != null && parent.hasSymbol(name); + } + + public Expression getSymbol(String name) { + Expression value = getLocalSymbol(name); + if (value != null) + return value; + if (parent != null) + return parent.getSymbol(name); + throw new SymbolNotFoundException(name); + } + + private Expression getLocalSymbol(String name) { + Expression value = symbols.get(name); + if (value != null) + return value; + + int index = name.length(); + while ((index = name.lastIndexOf('.', index - 1)) != -1) { + Expression result = symbols.get(name.substring(0, index)); + if (result != null && result.isContext()) + return ((Scope)result.getContext()).getLocalSymbol(name.substring(index + 1)); + } + return null; + } + + public static class SymbolNotFoundException extends AssemblyException { + private static final long serialVersionUID = 1L; + + public SymbolNotFoundException(String name) { + super("Symbol not found: " + name); + } + } + + public String serializeSymbols() { + return serializeSymbols(""); + } + + public String serializeSymbols(String namePrefix) { + StringBuilder builder = new StringBuilder(); + TreeMap sortedMap = new TreeMap<>(symbols); + for (Map.Entry entry : sortedMap.entrySet()) { + if (entry.getValue() instanceof ContextLiteral && !"$".equals(entry.getKey())) { + String name = namePrefix + entry.getKey(); + try { + builder.append(name + ": equ " + entry.getValue().getHexValue() + "\n"); + } catch (EvaluationException e) { + // ignore + } + Scope context = (Scope)((ContextLiteral)entry.getValue()).getContext(); + builder.append(context.serializeSymbols(name + ".")); + } + } + return builder.toString(); + } + + public String toString() { + return serializeSymbols(); + } + +} diff --git a/src/nl/grauw/glass/Source.java b/src/nl/grauw/glass/Source.java new file mode 100644 index 0000000..f330ec8 --- /dev/null +++ b/src/nl/grauw/glass/Source.java @@ -0,0 +1,110 @@ +package nl.grauw.glass; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public class Source { + + private final Scope scope; + private List lines = new ArrayList(); + + public Source() { + scope = new GlobalScope(); + } + + public Source(Scope scope) { + this.scope = scope; + } + + public Source(Scope scope, Source other) { + this(scope); + for (Line line : other.lines) + addLine(new Line(new Scope(scope), line)); + } + + public Scope getScope() { + return scope; + } + + public List getLines() { + return lines; + } + + public Line getLastLine() { + return lines.size() > 0 ? lines.get(lines.size() - 1) : null; + } + + public List getLineCopies(Scope newParent) { + List lineCopies = new ArrayList<>(); + for (Line line : lines) + lineCopies.add(new Line(new Scope(newParent), line)); + return lineCopies; + } + + public Line addLine(Line line) { + lines.add(line); + return line; + } + + public List addLines(List lines) { + this.lines.addAll(lines); + return lines; + } + + public void assemble(OutputStream output) throws IOException { + register(); + expand(); + resolve(); + generateObjectCode(output); + } + + public void register() { + for (Line line : lines) + line.register(scope); + } + + public void expand() { + List newLines = new ArrayList<>(); + for (Line line : lines) + newLines.addAll(line.expand()); + lines = newLines; + } + + public int resolve() { + return resolve(0); + } + + public int resolve(int address) { + for (Line line : lines) + address = line.resolve(address); + return address; + } + + public void generateObjectCode(OutputStream output) throws IOException { + for (Line line : lines) + line.generateObjectCode(output); + } + + public byte[] generateObjectCode() { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try { + generateObjectCode(bytes); + } catch (IOException e) { + throw new AssemblyException(e); + } + return bytes.toByteArray(); + } + + public String toString() { + StringBuilder string = new StringBuilder(); + for (Line line : lines) { + string.append(line); + string.append('\n'); + } + return string.toString(); + } + +} diff --git a/src/nl/grauw/glass/SourceBuilder.java b/src/nl/grauw/glass/SourceBuilder.java new file mode 100644 index 0000000..61f0d8e --- /dev/null +++ b/src/nl/grauw/glass/SourceBuilder.java @@ -0,0 +1,206 @@ +package nl.grauw.glass; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import nl.grauw.glass.directives.Directive; +import nl.grauw.glass.directives.Ds; +import nl.grauw.glass.directives.Equ; +import nl.grauw.glass.directives.If; +import nl.grauw.glass.directives.Incbin; +import nl.grauw.glass.directives.Include; +import nl.grauw.glass.directives.Instruction; +import nl.grauw.glass.directives.Irp; +import nl.grauw.glass.directives.Macro; +import nl.grauw.glass.directives.Proc; +import nl.grauw.glass.directives.Rept; +import nl.grauw.glass.directives.Section; +import nl.grauw.glass.expressions.Annotation; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Sequence; + +public class SourceBuilder { + + public static final List END_TERMINATORS = Arrays.asList(new String[] { "end", "END" }); + public static final List ENDM_TERMINATORS = Arrays.asList(new String[] { "endm", "ENDM" }); + public static final List ENDP_TERMINATORS = Arrays.asList(new String[] { "endp", "ENDP" }); + public static final List ENDS_TERMINATORS = Arrays.asList(new String[] { "ends", "ENDS" }); + public static final List ELSE_TERMINATORS = Arrays.asList(new String[] { "else", "ELSE", "endif", "ENDIF" }); + public static final List ENDIF_TERMINATORS = Arrays.asList(new String[] { "endif", "ENDIF" }); + + private final Source source; + private final List terminators; + private final List includePaths; + private final Parser parser = new Parser(); + + private static final List sourceFiles = new ArrayList(); + + public SourceBuilder(List includePaths) { + this.source = new Source(); + this.terminators = END_TERMINATORS; + this.includePaths = includePaths; + } + + public SourceBuilder(Source source, List terminators, List includePaths) { + this.source = source; + this.terminators = terminators; + this.includePaths = includePaths; + } + + public boolean hasLoadedSourceFile(File file) { + try { + for (int i = 0; i < sourceFiles.size(); i++) + if (file.getCanonicalPath().equals(sourceFiles.get(i).getCanonicalPath())) + return true; + } catch (IOException e) { + } + return false; + } + + public Source parse(File sourceFile) { + try { + parse(new FileInputStream(sourceFile), sourceFile); + } catch (FileNotFoundException e) { + throw new AssemblyException(e); + } + return source; + } + + private Source parseInclude(File sourceFile, File basePath, boolean once) { + File fullPath = new File(basePath.getParent(), sourceFile.getPath()); + if (fullPath.exists()) { + if (once && hasLoadedSourceFile(fullPath)) + return null; + return parse(fullPath); + } + return parseInclude(sourceFile, once); + } + + private Source parseInclude(File sourceFile, boolean once) { + for (File includePath : includePaths) { + File fullPath = new File(includePath, sourceFile.getPath()); + if (fullPath.exists()) { + if (once && hasLoadedSourceFile(fullPath)) + return null; + return parse(fullPath); + } + } + throw new AssemblyException("Include file not found: " + sourceFile); + } + + public Source parse(InputStream reader, File sourceFile) { + return parse(new InputStreamReader(reader, Charset.forName("ISO-8859-1")), sourceFile); + } + + public Source parse(Reader reader, File sourceFile) { + return parse(new LineNumberReader(reader), sourceFile); + } + + private Source parse(LineNumberReader reader, File sourceFile) { + sourceFiles.add(sourceFile); + while (true) { + Line line = parser.parse(reader, new Scope(source.getScope()), sourceFile); + if (line == null) + break; + + try { + line.setDirective(getDirective(line, reader, sourceFile)); + source.addLine(line); + if (line.getMnemonic() != null && terminators.contains(line.getMnemonic())) + return source; + } catch (AssemblyException e) { + e.addContext(line); + throw e; + } + } + if (terminators != END_TERMINATORS) + throw new AssemblyException("Unexpected end of file. Expecting: " + terminators.toString()); + return source; + } + + public Directive getDirective(Line line, LineNumberReader reader, File sourceFile) { + if (line.getMnemonic() == null) + return new Instruction(); + + switch (line.getMnemonic()) { + case "equ": + case "EQU": + return new Equ(); + case "include": + case "INCLUDE": + return getIncludeDirective(line, sourceFile); + case "incbin": + case "INCBIN": + return new Incbin(sourceFile, includePaths); + case "macro": + case "MACRO": + return new Macro(parseBlock(line.getScope(), ENDM_TERMINATORS, reader, sourceFile)); + case "rept": + case "REPT": + return new Rept(parseBlock(line.getScope(), ENDM_TERMINATORS, reader, sourceFile)); + case "irp": + case "IRP": + return new Irp(parseBlock(line.getScope(), ENDM_TERMINATORS, reader, sourceFile)); + case "proc": + case "PROC": + return new Proc(parseBlock(line.getScope(), ENDP_TERMINATORS, reader, sourceFile)); + case "if": + case "IF": + Source thenBlock = parseBlock(source.getScope(), ELSE_TERMINATORS, reader, sourceFile); + Source elseBlock = !ENDIF_TERMINATORS.contains(thenBlock.getLastLine().getMnemonic()) ? + parseBlock(source.getScope(), ENDIF_TERMINATORS, reader, sourceFile) : null; + return new If(thenBlock, elseBlock); + case "section": + case "SECTION": + return new Section(parseBlock(source.getScope(), ENDS_TERMINATORS, reader, sourceFile)); + case "ds": + case "DS": + return new Ds(); + case "endm": + case "ENDM": + case "endp": + case "ENDP": + case "ends": + case "ENDS": + if (!terminators.contains(line.getMnemonic())) + throw new AssemblyException("Unexpected " + line.getMnemonic() + "."); + return new Instruction(); + default: + return new Instruction(); + } + } + + private Directive getIncludeDirective(Line line, File sourceFile) { + boolean once = false; + Expression argument = line.getArguments(); + if (argument instanceof Annotation) { + String annotation = argument.getAnnotation().getName(); + if ("once".equals(annotation) || "ONCE".equals(annotation)) { + argument = argument.getAnnotee(); + once = true; + } + } + if (line.getArguments() instanceof Sequence) + throw new AssemblyException("Include only accepts 1 argument."); + if (!argument.isString()) + throw new AssemblyException("A string literal is expected."); + SourceBuilder sourceBuilder = new SourceBuilder(source, END_TERMINATORS, includePaths); + sourceBuilder.parseInclude(new File(argument.getString()), sourceFile, once); + return new Include(); + } + + private Source parseBlock(Scope scope, List terminators, LineNumberReader reader, File sourceFile) { + return new SourceBuilder(new Source(scope), terminators, includePaths).parse(reader, sourceFile); + } + +} diff --git a/src/nl/grauw/glass/directives/Directive.java b/src/nl/grauw/glass/directives/Directive.java new file mode 100644 index 0000000..cd91ee6 --- /dev/null +++ b/src/nl/grauw/glass/directives/Directive.java @@ -0,0 +1,13 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; + +public abstract class Directive { + + public void register(Scope scope, Line line) { + if (line.getLabel() != null) + scope.addSymbol(line.getLabel(), line.getScope()); + } + +} diff --git a/src/nl/grauw/glass/directives/Ds.java b/src/nl/grauw/glass/directives/Ds.java new file mode 100644 index 0000000..2da9e21 --- /dev/null +++ b/src/nl/grauw/glass/directives/Ds.java @@ -0,0 +1,17 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.SectionContextLiteral; + +public class Ds extends Directive { + + @Override + public void register(Scope scope, Line line) { + nl.grauw.glass.instructions.Ds ds = new nl.grauw.glass.instructions.Ds(); + line.setInstruction(ds); + if (line.getLabel() != null) + scope.addSymbol(line.getLabel(), new SectionContextLiteral(line.getScope(), ds)); + } + +} diff --git a/src/nl/grauw/glass/directives/Equ.java b/src/nl/grauw/glass/directives/Equ.java new file mode 100644 index 0000000..4d49927 --- /dev/null +++ b/src/nl/grauw/glass/directives/Equ.java @@ -0,0 +1,16 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.AssemblyException; +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; + +public class Equ extends Directive { + + @Override + public void register(Scope scope, Line line) { + if (line.getLabel() == null) + throw new AssemblyException("Equ without label."); + scope.addSymbol(line.getLabel(), line.getArguments()); + } + +} diff --git a/src/nl/grauw/glass/directives/If.java b/src/nl/grauw/glass/directives/If.java new file mode 100644 index 0000000..0be7f92 --- /dev/null +++ b/src/nl/grauw/glass/directives/If.java @@ -0,0 +1,26 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; + +public class If extends Directive { + + private final Source thenSource; + private final Source elseSource; + + public If(Source thenSource, Source elseSource) { + this.thenSource = thenSource; + this.elseSource = elseSource; + } + + @Override + public void register(Scope scope, Line line) { + line.setInstruction(new nl.grauw.glass.instructions.If( + new Source(scope, thenSource), + elseSource != null ? new Source(scope, elseSource) : null + )); + super.register(scope, line); + } + +} diff --git a/src/nl/grauw/glass/directives/Incbin.java b/src/nl/grauw/glass/directives/Incbin.java new file mode 100644 index 0000000..1ee9f56 --- /dev/null +++ b/src/nl/grauw/glass/directives/Incbin.java @@ -0,0 +1,25 @@ +package nl.grauw.glass.directives; + +import java.io.File; +import java.util.List; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; + +public class Incbin extends Directive { + + private final File sourceFile; + private final List includePaths; + + public Incbin(File sourceFile, List includePaths) { + this.sourceFile = sourceFile; + this.includePaths = includePaths; + } + + @Override + public void register(Scope scope, Line line) { + line.setInstruction(new nl.grauw.glass.instructions.Incbin(sourceFile, includePaths)); + super.register(scope, line); + } + +} diff --git a/src/nl/grauw/glass/directives/Include.java b/src/nl/grauw/glass/directives/Include.java new file mode 100644 index 0000000..d4040bb --- /dev/null +++ b/src/nl/grauw/glass/directives/Include.java @@ -0,0 +1,5 @@ +package nl.grauw.glass.directives; + +public class Include extends Directive { + +} diff --git a/src/nl/grauw/glass/directives/Instruction.java b/src/nl/grauw/glass/directives/Instruction.java new file mode 100644 index 0000000..cade15b --- /dev/null +++ b/src/nl/grauw/glass/directives/Instruction.java @@ -0,0 +1,5 @@ +package nl.grauw.glass.directives; + +public class Instruction extends Directive { + +} diff --git a/src/nl/grauw/glass/directives/Irp.java b/src/nl/grauw/glass/directives/Irp.java new file mode 100644 index 0000000..8c687ba --- /dev/null +++ b/src/nl/grauw/glass/directives/Irp.java @@ -0,0 +1,21 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; + +public class Irp extends Directive { + + private final Source source; + + public Irp(Source source) { + this.source = source; + } + + @Override + public void register(Scope scope, Line line) { + line.setInstruction(new nl.grauw.glass.instructions.Irp(source)); + super.register(scope, line); + } + +} diff --git a/src/nl/grauw/glass/directives/Macro.java b/src/nl/grauw/glass/directives/Macro.java new file mode 100644 index 0000000..bd7da78 --- /dev/null +++ b/src/nl/grauw/glass/directives/Macro.java @@ -0,0 +1,31 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.AssemblyException; +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Instruction; +import nl.grauw.glass.instructions.MacroInstruction; + +public class Macro extends Directive { + + private final Source source; + + public Macro(Source source) { + this.source = source; + } + + @Override + public void register(Scope scope, Line line) { + if (line.getLabel() == null) + throw new AssemblyException("Macro without label."); + scope.addSymbol(line.getLabel(), + new Instruction( + new MacroInstruction(line.getArguments(), source), + source.getScope() + ) + ); + line.setInstruction(new nl.grauw.glass.instructions.Macro(source)); + } + +} diff --git a/src/nl/grauw/glass/directives/Proc.java b/src/nl/grauw/glass/directives/Proc.java new file mode 100644 index 0000000..0791f2a --- /dev/null +++ b/src/nl/grauw/glass/directives/Proc.java @@ -0,0 +1,21 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; + +public class Proc extends Directive { + + private final Source source; + + public Proc(Source source) { + this.source = source; + } + + @Override + public void register(Scope scope, Line line) { + line.setInstruction(new nl.grauw.glass.instructions.Proc(source)); + super.register(scope, line); + } + +} diff --git a/src/nl/grauw/glass/directives/Rept.java b/src/nl/grauw/glass/directives/Rept.java new file mode 100644 index 0000000..1ff521d --- /dev/null +++ b/src/nl/grauw/glass/directives/Rept.java @@ -0,0 +1,21 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; + +public class Rept extends Directive { + + private final Source source; + + public Rept(Source source) { + this.source = source; + } + + @Override + public void register(Scope scope, Line line) { + line.setInstruction(new nl.grauw.glass.instructions.Rept(source)); + super.register(scope, line); + } + +} diff --git a/src/nl/grauw/glass/directives/Section.java b/src/nl/grauw/glass/directives/Section.java new file mode 100644 index 0000000..a2b9501 --- /dev/null +++ b/src/nl/grauw/glass/directives/Section.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.directives; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; + +public class Section extends Directive { + + private final Source source; + + public Section(Source source) { + this.source = source; + } + + @Override + public void register(Scope scope, Line line) { + nl.grauw.glass.instructions.Section section = new nl.grauw.glass.instructions.Section(source); + line.setInstruction(section); + + source.register(); + super.register(scope, line); + } + +} diff --git a/src/nl/grauw/glass/expressions/Add.java b/src/nl/grauw/glass/expressions/Add.java new file mode 100644 index 0000000..825e102 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Add.java @@ -0,0 +1,51 @@ +package nl.grauw.glass.expressions; + +public class Add extends BinaryOperator { + + public Add(Expression augend, Expression addend) { + super(augend, addend); + } + + @Override + public Add copy(Context context) { + return new Add(term1.copy(context), term2.copy(context)); + } + + public Expression getAugend() { + return term1; + } + + public Expression getAddend() { + return term2; + } + + @Override + public int getInteger() { + return term1.getInteger() + term2.getInteger(); + } + + @Override + public boolean isRegister() { + if (term1.isRegister()) { + Register register = term1.getRegister(); + return register.isIndex() && register.isPair(); + } + return false; + } + + @Override + public Register getRegister() { + if (term1.isRegister()) { + Register register = term1.getRegister(); + if (register.isIndex() && register.isPair()) + return new Register(register, new Add(register.getIndexOffset(), term2)); + } + throw new EvaluationException("Not a register."); + } + + @Override + public String getLexeme() { + return "+"; + } + +} diff --git a/src/nl/grauw/glass/expressions/And.java b/src/nl/grauw/glass/expressions/And.java new file mode 100644 index 0000000..92d64ec --- /dev/null +++ b/src/nl/grauw/glass/expressions/And.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class And extends BinaryOperator { + + public And(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public And copy(Context context) { + return new And(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() & term2.getInteger(); + } + + @Override + public String getLexeme() { + return "&"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Annotation.java b/src/nl/grauw/glass/expressions/Annotation.java new file mode 100644 index 0000000..c394b68 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Annotation.java @@ -0,0 +1,34 @@ +package nl.grauw.glass.expressions; + +public class Annotation extends Expression { + + private final Identifier annotation; + private final Expression annotee; + + public Annotation(Identifier annotation, Expression annotee) { + this.annotation = annotation; + this.annotee = annotee; + } + + @Override + public Annotation copy(Context context) { + return new Annotation(annotation.copy(context), annotee.copy(context)); + } + + public Identifier getAnnotation() { + return annotation; + } + + public Expression getAnnotee() { + return annotee; + } + + public String toString() { + return "" + annotation + " " + annotee; + } + + public String toDebugString() { + return "{" + annotation.toDebugString() + " " + annotee.toDebugString() + "}"; + } + +} diff --git a/src/nl/grauw/glass/expressions/BinaryOperator.java b/src/nl/grauw/glass/expressions/BinaryOperator.java new file mode 100644 index 0000000..66b67e3 --- /dev/null +++ b/src/nl/grauw/glass/expressions/BinaryOperator.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.expressions; + +public abstract class BinaryOperator extends Expression { + + protected final Expression term1; + protected final Expression term2; + + public abstract String getLexeme(); + + public BinaryOperator(Expression term1, Expression term2) { + this.term1 = term1; + this.term2 = term2; + } + + public Expression getTerm1() { + return term1; + } + + public Expression getTerm2() { + return term2; + } + + @Override + public boolean isInteger() { + return term1.isInteger() && term2.isInteger(); + } + + public String toString() { + return "" + term1 + " " + getLexeme() + " " + term2; + } + + public String toDebugString() { + return "{" + term1.toDebugString() + " " + getLexeme() + " " + term2.toDebugString() + "}"; + } + +} diff --git a/src/nl/grauw/glass/expressions/CharacterLiteral.java b/src/nl/grauw/glass/expressions/CharacterLiteral.java new file mode 100644 index 0000000..97afc2e --- /dev/null +++ b/src/nl/grauw/glass/expressions/CharacterLiteral.java @@ -0,0 +1,48 @@ +package nl.grauw.glass.expressions; + +public class CharacterLiteral extends Literal { + + private final char character; + + public CharacterLiteral(char character) { + this.character = character; + } + + @Override + public CharacterLiteral copy(Context context) { + return this; + } + + public char getCharacter() { + return character; + } + + @Override + public boolean isInteger() { + return true; + } + + @Override + public int getInteger() { + return character; + } + + public String toString() { + String escaped = Character.toString(character); + escaped = escaped.replace("\\", "\\\\"); + escaped = escaped.replace("\'", "\\\'"); + escaped = escaped.replace("\0", "\\0"); + escaped = escaped.replace("\7", "\\a"); + escaped = escaped.replace("\t", "\\t"); + escaped = escaped.replace("\n", "\\n"); + escaped = escaped.replace("\f", "\\f"); + escaped = escaped.replace("\r", "\\r"); + escaped = escaped.replace("\33", "\\e"); + return "'" + escaped + "'"; + } + + public String toDebugString() { + return toString(); + } + +} diff --git a/src/nl/grauw/glass/expressions/Complement.java b/src/nl/grauw/glass/expressions/Complement.java new file mode 100644 index 0000000..b5cdfa9 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Complement.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class Complement extends UnaryOperator { + + public Complement(Expression term) { + super(term); + } + + @Override + public Complement copy(Context context) { + return new Complement(term.copy(context)); + } + + @Override + public int getInteger() { + return ~term.getInteger(); + } + + @Override + public String getLexeme() { + return "~"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Context.java b/src/nl/grauw/glass/expressions/Context.java new file mode 100644 index 0000000..34b4cb7 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Context.java @@ -0,0 +1,11 @@ +package nl.grauw.glass.expressions; + +public interface Context { + + public Expression getSymbol(String name); + + public boolean hasSymbol(String name); + + public int getAddress(); + +} diff --git a/src/nl/grauw/glass/expressions/ContextLiteral.java b/src/nl/grauw/glass/expressions/ContextLiteral.java new file mode 100644 index 0000000..629c7f8 --- /dev/null +++ b/src/nl/grauw/glass/expressions/ContextLiteral.java @@ -0,0 +1,48 @@ +package nl.grauw.glass.expressions; + +public class ContextLiteral extends Literal { + + private final Context context; + + public ContextLiteral(Context context) { + this.context = context; + } + + @Override + public ContextLiteral copy(Context context) { + return new ContextLiteral(context); + } + + @Override + public boolean isContext() { + return true; + } + + @Override + public Context getContext() { + return context; + } + + @Override + public boolean isInteger() { + return true; + } + + @Override + public int getInteger() { + return context.getAddress(); + } + + public String toString() { + try { + return getHexValue(); + } catch (EvaluationException e) { + return "<" + e.getMessage() + ">"; + } + } + + public String toDebugString() { + return toString(); + } + +} diff --git a/src/nl/grauw/glass/expressions/Divide.java b/src/nl/grauw/glass/expressions/Divide.java new file mode 100644 index 0000000..a4e9bf4 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Divide.java @@ -0,0 +1,35 @@ +package nl.grauw.glass.expressions; + +public class Divide extends BinaryOperator { + + public Divide(Expression dividend, Expression divisor) { + super(dividend, divisor); + } + + @Override + public Divide copy(Context context) { + return new Divide(term1.copy(context), term2.copy(context)); + } + + public Expression getDividend() { + return term1; + } + + public Expression getDivisor() { + return term2; + } + + @Override + public int getInteger() { + int divisor = term2.getInteger(); + if (divisor == 0) + throw new EvaluationException("Division by zero."); + return term1.getInteger() / divisor; + } + + @Override + public String getLexeme() { + return "/"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Equals.java b/src/nl/grauw/glass/expressions/Equals.java new file mode 100644 index 0000000..973cbaa --- /dev/null +++ b/src/nl/grauw/glass/expressions/Equals.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class Equals extends BinaryOperator { + + public Equals(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public Equals copy(Context context) { + return new Equals(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() == term2.getInteger() ? -1 : 0; + } + + @Override + public String getLexeme() { + return "="; + } + +} diff --git a/src/nl/grauw/glass/expressions/EvaluationException.java b/src/nl/grauw/glass/expressions/EvaluationException.java new file mode 100644 index 0000000..ef59fe4 --- /dev/null +++ b/src/nl/grauw/glass/expressions/EvaluationException.java @@ -0,0 +1,16 @@ +package nl.grauw.glass.expressions; + +import nl.grauw.glass.AssemblyException; + +public class EvaluationException extends AssemblyException { + private static final long serialVersionUID = 1L; + + public EvaluationException() { + this(null); + } + + public EvaluationException(String message) { + super(message); + } + +} diff --git a/src/nl/grauw/glass/expressions/Expression.java b/src/nl/grauw/glass/expressions/Expression.java new file mode 100644 index 0000000..1e32d7b --- /dev/null +++ b/src/nl/grauw/glass/expressions/Expression.java @@ -0,0 +1,117 @@ +package nl.grauw.glass.expressions; + +import java.util.ArrayList; +import java.util.List; + +import nl.grauw.glass.instructions.InstructionFactory; + +public abstract class Expression { + + public abstract String toDebugString(); + + public abstract Expression copy(Context context); + + public Expression resolve() { + return this; + } + + public boolean isInteger() { + return false; + } + + public int getInteger() { + throw new EvaluationException("Not an integer."); + } + + public boolean isString() { + return false; + } + + public String getString() { + throw new EvaluationException("Not a string."); + } + + public boolean isRegister() { + return false; + } + + public Register getRegister() { + throw new EvaluationException("Not a register."); + } + + public boolean isFlag() { + return false; + } + + public Flag getFlag() { + throw new EvaluationException("Not a flag."); + } + + public boolean isGroup() { + return false; + } + + public Identifier getAnnotation() { + return null; + } + + public Expression getAnnotee() { + return this; + } + + public boolean isInstruction() { + return false; + } + + public InstructionFactory getInstruction() { + throw new EvaluationException("Not an instruction."); + } + + public boolean isContext() { + return false; + } + + public Context getContext() { + throw new EvaluationException("Not a context."); + } + + public boolean isSectionContext() { + return false; + } + + public SectionContext getSectionContext() { + throw new EvaluationException("Not a context."); + } + + public int getAddress() { + return getInteger(); + } + + public List getList() { + List list = new ArrayList<>(); + addToList(list); + return list; + } + + protected void addToList(List list) { + list.add(this); + } + + public Expression getElement() { + return getElement(0); + } + + public Expression getElement(int index) { + return index == 0 ? this : null; + } + + public Expression getNext() { + return null; + } + + public String getHexValue() { + String string = Integer.toHexString(getInteger()).toUpperCase(); + return (string.charAt(0) >= 'A' && string.charAt(0) <= 'F' ? "0" : "") + string + "H"; + } + +} diff --git a/src/nl/grauw/glass/expressions/ExpressionBuilder.java b/src/nl/grauw/glass/expressions/ExpressionBuilder.java new file mode 100644 index 0000000..b6e9535 --- /dev/null +++ b/src/nl/grauw/glass/expressions/ExpressionBuilder.java @@ -0,0 +1,390 @@ +package nl.grauw.glass.expressions; + +import java.util.ArrayDeque; +import java.util.Deque; + +import nl.grauw.glass.AssemblyException; + + +/** + * Constructs an AST from the given expression tokens. + * + * It uses a shunting yard algorithm. + */ +public class ExpressionBuilder { + + private Deque operands = new ArrayDeque(); + private Deque operators = new ArrayDeque(); + private int groupCount = 0; + + public ExpressionBuilder() { + operators.push(SENTINEL); + } + + public void addValueToken(Expression value) { + operands.push(value); + } + + public void addOperatorToken(Operator operator) { + evaluateNotYieldingTo(operator); + + if (operator == GROUP_OPEN || operator == INDEX_OPEN) { + groupCount++; + operators.push(operator); + operators.push(SENTINEL); + } else if (operator == GROUP_CLOSE || operator == INDEX_CLOSE) { + groupCount--; + if (operators.pop() != SENTINEL) + throw new AssemblyException("Sentinel expected."); + if (operator == GROUP_CLOSE && operators.peek() != GROUP_OPEN) + throw new ExpressionError("Group open expected."); + if (operator == INDEX_CLOSE && operators.peek() != INDEX_OPEN) + throw new ExpressionError("Index open expected."); + } else { + operators.push(operator); + } + } + + public Expression getExpression() { + if (operands.isEmpty() || operators.isEmpty()) + throw new AssemblyException("Operands / operators is empty: " + this); + + // process remainder + evaluateNotYieldingTo(SENTINEL); + + if (operators.size() > 1 && operators.peek() == SENTINEL) + throw new ExpressionError("Group close expected."); + if (operands.size() > 1 || operators.size() != 1) + throw new AssemblyException("Not all operands / operators were processed: " + this); + + return operands.pop(); + } + + private void evaluateNotYieldingTo(Operator operator) { + while (!operators.peek().yieldsTo(operator)) + operands.push(operators.pop().evaluate()); + } + + public boolean hasOpenGroup() + { + return groupCount > 0; + } + + public String toString() { + return "" + operands + " / " + operators; + } + + private abstract class Operator { + + private Precedence precedence; + private Associativity associativity; + + private Operator(Precedence precedence, Associativity associativity) { + this.precedence = precedence; + this.associativity = associativity; + } + + public boolean yieldsTo(Operator other) { + if (associativity == Associativity.LEFT_TO_RIGHT) + return precedence.ordinal() > other.precedence.ordinal(); + else + return precedence.ordinal() >= other.precedence.ordinal(); + } + + public abstract Expression evaluate(); + } + + public final Operator POSITIVE = new Operator(Precedence.UNARY, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + return new Positive(operands.pop()); + }; + }; + + public final Operator NEGATIVE = new Operator(Precedence.UNARY, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + return new Negative(operands.pop()); + }; + }; + + public final Operator COMPLEMENT = new Operator(Precedence.UNARY, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + return new Complement(operands.pop()); + }; + }; + + public final Operator NOT = new Operator(Precedence.UNARY, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + return new Not(operands.pop()); + }; + }; + + public final Operator MEMBER = new Operator(Precedence.MEMBER, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + if (!(operandRight instanceof Identifier)) + throw new ExpressionError("Member operator right hand side must be an identifier."); + return new Member(operands.pop(), (Identifier)operandRight); + }; + }; + + public final Operator MULTIPLY = new Operator(Precedence.MULTIPLICATION, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Multiply(operands.pop(), operandRight); + }; + }; + + public final Operator DIVIDE = new Operator(Precedence.MULTIPLICATION, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Divide(operands.pop(), operandRight); + }; + }; + + public final Operator MODULO = new Operator(Precedence.MULTIPLICATION, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Modulo(operands.pop(), operandRight); + }; + }; + + public final Operator ADD = new Operator(Precedence.ADDITION, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Add(operands.pop(), operandRight); + }; + }; + + public final Operator SUBTRACT = new Operator(Precedence.ADDITION, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Subtract(operands.pop(), operandRight); + }; + }; + + public final Operator SHIFT_LEFT = new Operator(Precedence.SHIFT, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new ShiftLeft(operands.pop(), operandRight); + }; + }; + + public final Operator SHIFT_RIGHT = new Operator(Precedence.SHIFT, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new ShiftRight(operands.pop(), operandRight); + }; + }; + + public final Operator LESS_THAN = new Operator(Precedence.COMPARISON, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new LessThan(operands.pop(), operandRight); + }; + }; + + public final Operator LESS_OR_EQUALS = new Operator(Precedence.COMPARISON, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new LessOrEquals(operands.pop(), operandRight); + }; + }; + + public final Operator GREATER_THAN = new Operator(Precedence.COMPARISON, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new GreaterThan(operands.pop(), operandRight); + }; + }; + + public final Operator GREATER_OR_EQUALS = new Operator(Precedence.COMPARISON, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new GreaterOrEquals(operands.pop(), operandRight); + }; + }; + + public final Operator EQUALS = new Operator(Precedence.EQUALITY, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Equals(operands.pop(), operandRight); + }; + }; + + public final Operator NOT_EQUALS = new Operator(Precedence.EQUALITY, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new NotEquals(operands.pop(), operandRight); + }; + }; + + public final Operator AND = new Operator(Precedence.AND, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new And(operands.pop(), operandRight); + }; + }; + + public final Operator XOR = new Operator(Precedence.XOR, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Xor(operands.pop(), operandRight); + }; + }; + + public final Operator OR = new Operator(Precedence.OR, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Or(operands.pop(), operandRight); + }; + }; + + public final Operator LOGICAL_AND = new Operator(Precedence.LOGICAL_AND, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new LogicalAnd(operands.pop(), operandRight); + }; + }; + + public final Operator LOGICAL_OR = new Operator(Precedence.LOGICAL_OR, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new LogicalOr(operands.pop(), operandRight); + }; + }; + + public final Operator ANNOTATION = new Operator(Precedence.ANNOTATION, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + Expression operandLeft = operands.pop(); + if (!(operandLeft instanceof Identifier)) + throw new ExpressionError("Annotation left hand side must be an identifier."); + return new Annotation((Identifier)operandLeft, operandRight); + }; + }; + + public final Operator SEQUENCE = new Operator(Precedence.SEQUENCE, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Sequence(operands.pop(), operandRight); + }; + }; + + public final Operator TERNARYIF = new Operator(Precedence.TERNARYIFELSE, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + throw new ExpressionError("Ternary if (?) without else (:)."); + }; + }; + + public final Operator TERNARYELSE = new Operator(Precedence.TERNARYIFELSE, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + while (operators.peek() == TERNARYELSE) { + operands.push(operators.pop().evaluate()); + } + if (operators.peek() == TERNARYIF) { + operators.pop(); + Expression operandMiddle = operands.pop(); + return new IfElse(operands.pop(), operandMiddle, operandRight); + } else { + throw new ExpressionError("Ternary else (:) without if (?)."); + } + }; + }; + + public final Operator GROUP_OPEN = new Operator(Precedence.GROUPING, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + return new Group(operands.pop()); + }; + }; + + public final Operator GROUP_CLOSE = new Operator(Precedence.NONE, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + throw new AssemblyException("Can not evaluate group close."); + }; + }; + + public final Operator INDEX_OPEN = new Operator(Precedence.MEMBER, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + Expression operandRight = operands.pop(); + return new Index(operands.pop(), operandRight); + }; + }; + + public final Operator INDEX_CLOSE = new Operator(Precedence.NONE, Associativity.LEFT_TO_RIGHT) { + @Override + public Expression evaluate() { + throw new AssemblyException("Can not evaluate group close."); + }; + }; + + public final Operator SENTINEL = new Operator(Precedence.NONE, Associativity.RIGHT_TO_LEFT) { + @Override + public Expression evaluate() { + throw new AssemblyException("Can not evaluate sentinel."); + }; + }; + + private enum Precedence { + GROUPING, + MEMBER, + UNARY, + MULTIPLICATION, + ADDITION, + SHIFT, + COMPARISON, + EQUALITY, + AND, + XOR, + OR, + LOGICAL_AND, + LOGICAL_OR, + TERNARYIFELSE, + ANNOTATION, + SEQUENCE, + NONE + } + + private enum Associativity { + LEFT_TO_RIGHT, + RIGHT_TO_LEFT + } + + public static class ExpressionError extends AssemblyException { + private static final long serialVersionUID = 1L; + public ExpressionError(String message) { + super("Expression error: " + message); + } + } + +} diff --git a/src/nl/grauw/glass/expressions/Flag.java b/src/nl/grauw/glass/expressions/Flag.java new file mode 100644 index 0000000..e13e53c --- /dev/null +++ b/src/nl/grauw/glass/expressions/Flag.java @@ -0,0 +1,81 @@ +package nl.grauw.glass.expressions; + +public class Flag extends Literal { + + public static Flag NZ = new Flag("nz", 0); + public static Flag Z = new Flag("z", 1); + public static Flag NC = new Flag("nc", 2); + public static Flag C = new Flag("c", 3); + public static Flag PO = new Flag("po", 4); + public static Flag PE = new Flag("pe", 5); + public static Flag P = new Flag("p", 6); + public static Flag M = new Flag("m", 7); + + private final String name; + private final int code; + + public Flag(String name, int code) { + this.name = name; + this.code = code; + } + + @Override + public Flag copy(Context context) { + return this; + } + + public int getCode() { + return code; + } + + @Override + public boolean isFlag() { + return true; + } + + @Override + public Flag getFlag() { + return this; + } + + @Override + public String toString() { + return name; + } + + @Override + public String toDebugString() { + return toString(); + } + + public static Flag getByName(String name) { + switch (name) { + case "nz": + case "NZ": + return Flag.NZ; + case "z": + case "Z": + return Flag.Z; + case "nc": + case "NC": + return Flag.NC; + case "c": + case "C": + return Flag.C; + case "po": + case "PO": + return Flag.PO; + case "pe": + case "PE": + return Flag.PE; + case "p": + case "P": + return Flag.P; + case "m": + case "M": + return Flag.M; + } + return null; + } + +} diff --git a/src/nl/grauw/glass/expressions/FlagOrRegister.java b/src/nl/grauw/glass/expressions/FlagOrRegister.java new file mode 100644 index 0000000..9101f06 --- /dev/null +++ b/src/nl/grauw/glass/expressions/FlagOrRegister.java @@ -0,0 +1,62 @@ +package nl.grauw.glass.expressions; + +public class FlagOrRegister extends Literal { + + public static FlagOrRegister C = new FlagOrRegister(Flag.C, Register.C); + + private final Flag flag; + private final Register register; + + public FlagOrRegister(Flag flag, Register register) { + this.flag = flag; + this.register = register; + } + + @Override + public FlagOrRegister copy(Context context) { + return this; + } + + @Override + public boolean isFlag() { + return true; + } + + @Override + public Flag getFlag() { + return flag; + } + + @Override + public boolean isRegister() { + return true; + } + + @Override + public Register getRegister() { + return register; + } + + @Override + public String toString() { + return flag.toString(); + } + + @Override + public String toDebugString() { + return flag.toString(); + } + + public static Literal getByName(String name) { + Flag flag = Flag.getByName(name); + Register register = Register.getByName(name); + if (flag != null && register == null) + return flag; + if (flag == null && register != null) + return register; + if (flag != null && register != null) + return new FlagOrRegister(flag, register); + return null; + } + +} diff --git a/src/nl/grauw/glass/expressions/GreaterOrEquals.java b/src/nl/grauw/glass/expressions/GreaterOrEquals.java new file mode 100644 index 0000000..11ae3db --- /dev/null +++ b/src/nl/grauw/glass/expressions/GreaterOrEquals.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class GreaterOrEquals extends BinaryOperator { + + public GreaterOrEquals(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public GreaterOrEquals copy(Context context) { + return new GreaterOrEquals(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() >= term2.getInteger() ? -1 : 0; + } + + @Override + public String getLexeme() { + return ">="; + } + +} diff --git a/src/nl/grauw/glass/expressions/GreaterThan.java b/src/nl/grauw/glass/expressions/GreaterThan.java new file mode 100644 index 0000000..606f24b --- /dev/null +++ b/src/nl/grauw/glass/expressions/GreaterThan.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class GreaterThan extends BinaryOperator { + + public GreaterThan(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public GreaterThan copy(Context context) { + return new GreaterThan(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() > term2.getInteger() ? -1 : 0; + } + + @Override + public String getLexeme() { + return ">"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Group.java b/src/nl/grauw/glass/expressions/Group.java new file mode 100644 index 0000000..78c5ae3 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Group.java @@ -0,0 +1,38 @@ +package nl.grauw.glass.expressions; + +public class Group extends Passthrough { + + private final Expression term; + + public Group(Expression term) { + this.term = term; + } + + @Override + public Group copy(Context context) { + return new Group(term.copy(context)); + } + + public Expression getTerm() { + return term; + } + + @Override + public Expression resolve() { + return term; + } + + @Override + public boolean isGroup() { + return true; + } + + public String toString() { + return "(" + term + ")"; + } + + public String toDebugString() { + return "(" + term.toDebugString() + ")"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Identifier.java b/src/nl/grauw/glass/expressions/Identifier.java new file mode 100644 index 0000000..3401a4e --- /dev/null +++ b/src/nl/grauw/glass/expressions/Identifier.java @@ -0,0 +1,55 @@ +package nl.grauw.glass.expressions; + +public class Identifier extends Passthrough { + + private final String name; + private final Context context; + + public Identifier(String name, Context context) { + this.name = name; + this.context = context; + } + + @Override + public Identifier copy(Context context) { + return new Identifier(name, context); + } + + public String getName() { + return name; + } + + public boolean exists() { + return FlagOrRegister.getByName(name) != null || context.hasSymbol(name); + } + + @Override + public Expression resolve() { + Literal flagOrRegister = FlagOrRegister.getByName(name); + return flagOrRegister != null ? flagOrRegister : context.getSymbol(name); + } + + @Override + public boolean isRegister() { + return exists() && super.isRegister(); + } + + @Override + public boolean isFlag() { + return exists() && super.isFlag(); + } + + @Override + public boolean isGroup() { + return exists() && super.isGroup(); + } + + public String toString() { + return name; + } + + public String toDebugString() { + return toString(); + } + +} diff --git a/src/nl/grauw/glass/expressions/IfElse.java b/src/nl/grauw/glass/expressions/IfElse.java new file mode 100644 index 0000000..a8199c4 --- /dev/null +++ b/src/nl/grauw/glass/expressions/IfElse.java @@ -0,0 +1,76 @@ +package nl.grauw.glass.expressions; + +public class IfElse extends Passthrough { + + private final Expression condition; + private final Expression trueTerm; + private final Expression falseTerm; + + public IfElse(Expression condition, Expression trueTerm, Expression falseTerm) { + this.condition = condition; + this.trueTerm = trueTerm; + this.falseTerm = falseTerm; + } + + @Override + public IfElse copy(Context context) { + return new IfElse(condition.copy(context), trueTerm.copy(context), falseTerm.copy(context)); + } + + public Expression getCondition() { + return condition; + } + + public Expression getTrueTerm() { + return trueTerm; + } + + public Expression getFalseTerm() { + return falseTerm; + } + + public boolean isTrue() { + return condition.getInteger() != 0; + } + + @Override + public Expression resolve() { + return isTrue() ? trueTerm : falseTerm; + } + + @Override + public boolean isInteger() { + return (trueTerm.isInteger() && falseTerm.isInteger()) || super.isInteger(); + } + + @Override + public boolean isString() { + return (trueTerm.isString() && falseTerm.isString()) || super.isString(); + } + + @Override + public boolean isRegister() { + return (trueTerm.isRegister() && falseTerm.isRegister()) || super.isRegister(); + } + + @Override + public boolean isFlag() { + return (trueTerm.isFlag() && falseTerm.isFlag()) || super.isFlag(); + } + + @Override + public boolean isContext() { + return (trueTerm.isContext() && falseTerm.isContext()) || super.isContext(); + } + + @Override + public String toString() { + return "" + condition + " ? " + trueTerm + " : " + falseTerm; + } + + @Override + public String toDebugString() { + return "{" + condition.toDebugString() + " ? " + trueTerm.toDebugString() + " : " + falseTerm.toDebugString() + "}"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Index.java b/src/nl/grauw/glass/expressions/Index.java new file mode 100644 index 0000000..e76afbb --- /dev/null +++ b/src/nl/grauw/glass/expressions/Index.java @@ -0,0 +1,42 @@ +package nl.grauw.glass.expressions; + +public class Index extends Passthrough { + + private final Expression sequence; + private final Expression index; + + public Index(Expression sequence, Expression index) { + this.sequence = sequence; + this.index = index; + } + + @Override + public Index copy(Context context) { + return new Index(sequence.copy(context), index.copy(context)); + } + + public Expression getSequence() { + return sequence; + } + + public Expression getIndex() { + return index; + } + + @Override + public Expression resolve() { + Expression element = sequence.resolve().getElement(index.getInteger()); + if (element == null) + throw new EvaluationException("Index out of bounds."); + return element; + } + + public String toString() { + return "" + sequence + "[" + index + "]"; + } + + public String toDebugString() { + return "{" + sequence.toDebugString() + "[" + index.toDebugString() + "]}"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Instruction.java b/src/nl/grauw/glass/expressions/Instruction.java new file mode 100644 index 0000000..3111cf7 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Instruction.java @@ -0,0 +1,54 @@ +package nl.grauw.glass.expressions; + +import nl.grauw.glass.instructions.InstructionFactory; + +public class Instruction extends Expression { + + private final InstructionFactory instruction; + private final Context context; + + public Instruction(InstructionFactory instruction) { + this(instruction, null); + } + + public Instruction(InstructionFactory instruction, Context context) { + this.instruction = instruction; + this.context = context; + } + + @Override + public Instruction copy(Context context) { + return new Instruction(instruction, context); + } + + @Override + public boolean isInstruction() { + return true; + } + + @Override + public InstructionFactory getInstruction() { + return instruction; + } + + @Override + public boolean isContext() { + return context != null; + } + + @Override + public Context getContext() { + if (!isContext()) + super.getContext(); + return context; + } + + public String toString() { + return "instruction"; + } + + public String toDebugString() { + return toString(); + } + +} diff --git a/src/nl/grauw/glass/expressions/IntegerLiteral.java b/src/nl/grauw/glass/expressions/IntegerLiteral.java new file mode 100644 index 0000000..7060faf --- /dev/null +++ b/src/nl/grauw/glass/expressions/IntegerLiteral.java @@ -0,0 +1,38 @@ +package nl.grauw.glass.expressions; + +public class IntegerLiteral extends Literal { + + public static final IntegerLiteral ZERO = new IntegerLiteral(0); + public static final IntegerLiteral ONE = new IntegerLiteral(1); + + private final int value; + + public IntegerLiteral(int value) { + this.value = value; + } + + @Override + public IntegerLiteral copy(Context context) { + return this; + } + + @Override + public boolean isInteger() { + return true; + } + + @Override + public int getInteger() { + return value; + } + + public String toString() { + String string = Integer.toHexString(value).toUpperCase(); + return (string.charAt(0) >= 'A' && string.charAt(0) <= 'F' ? "0" : "") + string + "H"; + } + + public String toDebugString() { + return toString(); + } + +} diff --git a/src/nl/grauw/glass/expressions/LessOrEquals.java b/src/nl/grauw/glass/expressions/LessOrEquals.java new file mode 100644 index 0000000..cc579fc --- /dev/null +++ b/src/nl/grauw/glass/expressions/LessOrEquals.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class LessOrEquals extends BinaryOperator { + + public LessOrEquals(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public LessOrEquals copy(Context context) { + return new LessOrEquals(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() <= term2.getInteger() ? -1 : 0; + } + + @Override + public String getLexeme() { + return "<="; + } + +} diff --git a/src/nl/grauw/glass/expressions/LessThan.java b/src/nl/grauw/glass/expressions/LessThan.java new file mode 100644 index 0000000..dc02fb2 --- /dev/null +++ b/src/nl/grauw/glass/expressions/LessThan.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class LessThan extends BinaryOperator { + + public LessThan(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public LessThan copy(Context context) { + return new LessThan(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() < term2.getInteger() ? -1 : 0; + } + + @Override + public String getLexeme() { + return "<"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Literal.java b/src/nl/grauw/glass/expressions/Literal.java new file mode 100644 index 0000000..e6ec014 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Literal.java @@ -0,0 +1,5 @@ +package nl.grauw.glass.expressions; + +public abstract class Literal extends Expression { + +} diff --git a/src/nl/grauw/glass/expressions/LogicalAnd.java b/src/nl/grauw/glass/expressions/LogicalAnd.java new file mode 100644 index 0000000..d096899 --- /dev/null +++ b/src/nl/grauw/glass/expressions/LogicalAnd.java @@ -0,0 +1,25 @@ +package nl.grauw.glass.expressions; + +public class LogicalAnd extends BinaryOperator { + + public LogicalAnd(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public LogicalAnd copy(Context context) { + return new LogicalAnd(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + int value1 = term1.getInteger(); + return value1 == 0 ? value1 : term2.getInteger(); + } + + @Override + public String getLexeme() { + return "&&"; + } + +} diff --git a/src/nl/grauw/glass/expressions/LogicalOr.java b/src/nl/grauw/glass/expressions/LogicalOr.java new file mode 100644 index 0000000..5e18128 --- /dev/null +++ b/src/nl/grauw/glass/expressions/LogicalOr.java @@ -0,0 +1,25 @@ +package nl.grauw.glass.expressions; + +public class LogicalOr extends BinaryOperator { + + public LogicalOr(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public LogicalOr copy(Context context) { + return new LogicalOr(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + int value1 = term1.getInteger(); + return value1 != 0 ? value1 : term2.getInteger(); + } + + @Override + public String getLexeme() { + return "||"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Member.java b/src/nl/grauw/glass/expressions/Member.java new file mode 100644 index 0000000..7ee918b --- /dev/null +++ b/src/nl/grauw/glass/expressions/Member.java @@ -0,0 +1,42 @@ +package nl.grauw.glass.expressions; + +public class Member extends Passthrough { + + private final Expression object; + private final Identifier subject; + + public Member(Expression object, Identifier subject) { + this.object = object; + this.subject = subject; + } + + @Override + public Member copy(Context context) { + return new Member(object.copy(context), subject.copy(context)); + } + + public Expression getObject() { + return object; + } + + public Expression getSubject() { + return subject; + } + + @Override + public Expression resolve() { + if (!object.isContext()) + throw new EvaluationException("Object not found."); + return object.getContext().getSymbol(subject.getName()); + } + + @Override + public String toString() { + return "" + object + "." + subject; + } + + public String toDebugString() { + return "{" + object.toDebugString() + "." + subject.toDebugString() + "}"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Modulo.java b/src/nl/grauw/glass/expressions/Modulo.java new file mode 100644 index 0000000..baaaa54 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Modulo.java @@ -0,0 +1,35 @@ +package nl.grauw.glass.expressions; + +public class Modulo extends BinaryOperator { + + public Modulo(Expression dividend, Expression divisor) { + super(dividend, divisor); + } + + @Override + public Modulo copy(Context context) { + return new Modulo(term1.copy(context), term2.copy(context)); + } + + public Expression getDividend() { + return term1; + } + + public Expression getDivisor() { + return term2; + } + + @Override + public int getInteger() { + int divisor = term2.getInteger(); + if (divisor == 0) + throw new EvaluationException("Division by zero."); + return term1.getInteger() % divisor; + } + + @Override + public String getLexeme() { + return "%"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Multiply.java b/src/nl/grauw/glass/expressions/Multiply.java new file mode 100644 index 0000000..2b6eef5 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Multiply.java @@ -0,0 +1,32 @@ +package nl.grauw.glass.expressions; + +public class Multiply extends BinaryOperator { + + public Multiply(Expression multiplicand, Expression multiplier) { + super(multiplicand, multiplier); + } + + @Override + public Multiply copy(Context context) { + return new Multiply(term1.copy(context), term2.copy(context)); + } + + public Expression getMultiplicand() { + return term1; + } + + public Expression getMultiplier() { + return term2; + } + + @Override + public int getInteger() { + return term1.getInteger() * term2.getInteger(); + } + + @Override + public String getLexeme() { + return "*"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Negative.java b/src/nl/grauw/glass/expressions/Negative.java new file mode 100644 index 0000000..80c77e1 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Negative.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class Negative extends UnaryOperator { + + public Negative(Expression term) { + super(term); + } + + @Override + public Negative copy(Context context) { + return new Negative(term.copy(context)); + } + + @Override + public int getInteger() { + return -term.getInteger(); + } + + @Override + public String getLexeme() { + return "-"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Not.java b/src/nl/grauw/glass/expressions/Not.java new file mode 100644 index 0000000..1ce315c --- /dev/null +++ b/src/nl/grauw/glass/expressions/Not.java @@ -0,0 +1,53 @@ +package nl.grauw.glass.expressions; + +import nl.grauw.glass.AssemblyException; + +public class Not extends UnaryOperator { + + public Not(Expression term) { + super(term); + } + + @Override + public Not copy(Context context) { + return new Not(term.copy(context)); + } + + @Override + public int getInteger() { + return term.getInteger() == 0 ? -1 : 0; + } + + @Override + public boolean isFlag() { + return term.isFlag(); + } + + @Override + public Flag getFlag() { + Flag flag = term.getFlag(); + if (flag == Flag.NZ) + return Flag.Z; + if (flag == Flag.Z) + return Flag.NZ; + if (flag == Flag.NC) + return Flag.C; + if (flag == Flag.C) + return Flag.NC; + if (flag == Flag.PO) + return Flag.PE; + if (flag == Flag.PE) + return Flag.PO; + if (flag == Flag.P) + return Flag.M; + if (flag == Flag.M) + return Flag.P; + throw new AssemblyException("Unrecognised flag."); + } + + @Override + public String getLexeme() { + return "!"; + } + +} diff --git a/src/nl/grauw/glass/expressions/NotEquals.java b/src/nl/grauw/glass/expressions/NotEquals.java new file mode 100644 index 0000000..bd9d71b --- /dev/null +++ b/src/nl/grauw/glass/expressions/NotEquals.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class NotEquals extends BinaryOperator { + + public NotEquals(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public NotEquals copy(Context context) { + return new NotEquals(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() != term2.getInteger() ? -1 : 0; + } + + @Override + public String getLexeme() { + return "!="; + } + +} diff --git a/src/nl/grauw/glass/expressions/Or.java b/src/nl/grauw/glass/expressions/Or.java new file mode 100644 index 0000000..4ecbf60 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Or.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class Or extends BinaryOperator { + + public Or(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public Or copy(Context context) { + return new Or(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() | term2.getInteger(); + } + + @Override + public String getLexeme() { + return "|"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Passthrough.java b/src/nl/grauw/glass/expressions/Passthrough.java new file mode 100644 index 0000000..9e86841 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Passthrough.java @@ -0,0 +1,82 @@ +package nl.grauw.glass.expressions; + +import nl.grauw.glass.instructions.InstructionFactory; + +public abstract class Passthrough extends Expression { + + @Override + public boolean isInteger() { + return resolve().isInteger(); + } + + @Override + public int getInteger() { + return resolve().getInteger(); + } + + @Override + public boolean isString() { + return resolve().isString(); + } + + @Override + public String getString() { + return resolve().getString(); + } + + @Override + public boolean isRegister() { + return resolve().isRegister(); + } + + @Override + public Register getRegister() { + return resolve().getRegister(); + } + + @Override + public boolean isFlag() { + return resolve().isFlag(); + } + + @Override + public Flag getFlag() { + return resolve().getFlag(); + } + + @Override + public boolean isGroup() { + return resolve().isGroup(); + } + + @Override + public boolean isInstruction() { + return resolve().isInstruction(); + } + + @Override + public InstructionFactory getInstruction() { + return resolve().getInstruction(); + } + + @Override + public boolean isContext() { + return resolve().isContext(); + } + + @Override + public Context getContext() { + return resolve().getContext(); + } + + @Override + public boolean isSectionContext() { + return resolve().isSectionContext(); + } + + @Override + public SectionContext getSectionContext() { + return resolve().getSectionContext(); + } + +} diff --git a/src/nl/grauw/glass/expressions/Positive.java b/src/nl/grauw/glass/expressions/Positive.java new file mode 100644 index 0000000..2e35961 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Positive.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class Positive extends UnaryOperator { + + public Positive(Expression term) { + super(term); + } + + @Override + public Positive copy(Context context) { + return new Positive(term.copy(context)); + } + + @Override + public int getInteger() { + return +term.getInteger(); + } + + @Override + public String getLexeme() { + return "+"; + } + +} diff --git a/src/nl/grauw/glass/expressions/Register.java b/src/nl/grauw/glass/expressions/Register.java new file mode 100644 index 0000000..8934915 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Register.java @@ -0,0 +1,186 @@ +package nl.grauw.glass.expressions; + +import nl.grauw.glass.AssemblyException; + +public class Register extends Literal { + + public static final int NONE = -1; + public static final int IX_CODE = 0xDD; + public static final int IY_CODE = 0xFD; + + public static Register B = new Register("b", false, 0, NONE, NONE); + public static Register C = new Register("c", false, 1, NONE, NONE); + public static Register D = new Register("d", false, 2, NONE, NONE); + public static Register E = new Register("e", false, 3, NONE, NONE); + public static Register H = new Register("h", false, 4, NONE, NONE); + public static Register L = new Register("l", false, 5, NONE, NONE); + public static Register A = new Register("a", false, 7, NONE, NONE); + public static Register IXH = new Register("ixh", false, 4, NONE, IX_CODE); + public static Register IXL = new Register("ixl", false, 5, NONE, IX_CODE); + public static Register IYH = new Register("iyh", false, 4, NONE, IY_CODE); + public static Register IYL = new Register("iyl", false, 5, NONE, IY_CODE); + public static Register BC = new Register("bc", true, NONE, 0, NONE); + public static Register DE = new Register("de", true, NONE, 1, NONE); + public static Register HL = new Register("hl", true, 6, 2, NONE); + public static Register SP = new Register("sp", true, NONE, 3, NONE); + public static Register AF = new Register("af", true, NONE, 3, NONE); + public static Register AF_ = new Register("af'", true, NONE, NONE, NONE); + public static Register IX = new Register("ix", true, 6, 2, IX_CODE, IntegerLiteral.ZERO); + public static Register IY = new Register("iy", true, 6, 2, IY_CODE, IntegerLiteral.ZERO); + public static Register I = new Register("i", false, NONE, NONE, NONE); + public static Register R = new Register("r", false, NONE, NONE, NONE); + + private final String name; + private final boolean pair; + private final int code8; + private final int code16; + private final int indexCode; + private final Expression indexOffset; + + public Register(String name, boolean pair, int code8, int code16, int indexCode) { + this(name, pair, code8, code16, indexCode, null); + } + + public Register(String name, boolean pair, int code8, int code16, int indexCode, Expression offset) { + if (offset != null && (!pair || indexCode == NONE)) + throw new AssemblyException("Can only specify offset for 16-bit index registers."); + + this.name = name; + this.pair = pair; + this.code8 = code8; + this.code16 = code16; + this.indexCode = indexCode; + this.indexOffset = offset; + } + + public Register(Register register, Expression offset) { + this(register.name, register.pair, register.code8, register.code16, register.indexCode, offset); + } + + @Override + public Register copy(Context context) { + return this; + } + + public boolean isPair() { + return pair; + } + + public int get8BitCode() { + if (code8 == NONE) + throw new EvaluationException("Register does not have an 8-bit code."); + return code8; + } + + public int get16BitCode() { + if (code16 == NONE) + throw new EvaluationException("Register does not have a 16-bit code."); + return code16; + } + + public boolean isIndex() { + return indexCode != NONE; + } + + public byte getIndexCode() { + if (indexCode == NONE) + throw new EvaluationException("Not an index register."); + return (byte)indexCode; + } + + public Expression getIndexOffset() { + if (indexCode == NONE || !pair) + throw new EvaluationException("Not an index register pair."); + return indexOffset; + } + + @Override + public boolean isRegister() { + return true; + } + + @Override + public Register getRegister() { + return this; + } + + @Override + public String toString() { + return name; + } + + @Override + public String toDebugString() { + return toString(); + } + + public static Register getByName(String name) { + switch (name) { + case "b": + case "B": + return Register.B; + case "c": + case "C": + return Register.C; + case "d": + case "D": + return Register.D; + case "e": + case "E": + return Register.E; + case "h": + case "H": + return Register.H; + case "l": + case "L": + return Register.L; + case "a": + case "A": + return Register.A; + case "ixh": + case "IXH": + return Register.IXH; + case "ixl": + case "IXL": + return Register.IXL; + case "iyh": + case "IYH": + return Register.IYH; + case "iyl": + case "IYL": + return Register.IYL; + case "bc": + case "BC": + return Register.BC; + case "de": + case "DE": + return Register.DE; + case "hl": + case "HL": + return Register.HL; + case "sp": + case "SP": + return Register.SP; + case "af": + case "AF": + return Register.AF; + case "af'": + case "AF'": + return Register.AF_; + case "ix": + case "IX": + return Register.IX; + case "iy": + case "IY": + return Register.IY; + case "i": + case "I": + return Register.I; + case "r": + case "R": + return Register.R; + } + return null; + } + +} diff --git a/src/nl/grauw/glass/expressions/Schema.java b/src/nl/grauw/glass/expressions/Schema.java new file mode 100644 index 0000000..46e7c69 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Schema.java @@ -0,0 +1,154 @@ +package nl.grauw.glass.expressions; + +public class Schema implements SchemaType { + + private SchemaType[] types; + + public Schema(SchemaType... types) { + this.types = types; + } + + public boolean check(Expression arguments) { + for (int i = 0; i < types.length; i++) { + if (arguments == null || !types[i].check(arguments.getElement())) + return false; + arguments = arguments.getNext(); + } + return arguments == null; + } + + public static SchemaType ANY = new IsAny(); + public static SchemaType DIRECT = new IsDirect(); + public static SchemaType INDIRECT = new IsIndirect(); + public static SchemaType INTEGER = new IsInteger(); + public static SchemaType STRING = new IsString(); + public static SchemaType IDENTIFIER = new IsIdentifier(); + public static SchemaType DIRECT_N = new And(DIRECT, INTEGER); + public static SchemaType DIRECT_R = new And(DIRECT, new IsRegister8Bit()); + public static SchemaType DIRECT_A = new And(DIRECT, new IsRegister(Register.A)); + public static SchemaType DIRECT_IR = new And(DIRECT, new IsRegister(Register.I, Register.R)); + public static SchemaType DIRECT_RR = new And(DIRECT, new IsRegister(Register.BC, Register.DE, Register.HL, Register.SP)); + public static SchemaType DIRECT_RR_INDEX = new And(DIRECT, new IsRegister(Register.BC, Register.DE, Register.HL, Register.SP, Register.IX, Register.IY)); + public static SchemaType DIRECT_RR_AF_INDEX = new And(DIRECT, new IsRegister(Register.BC, Register.DE, Register.HL, Register.AF, Register.IX, Register.IY)); + public static SchemaType DIRECT_DE = new And(DIRECT, new IsRegister(Register.DE)); + public static SchemaType DIRECT_HL = new And(DIRECT, new IsRegister(Register.HL)); + public static SchemaType DIRECT_HL_IX_IY = new And(DIRECT, new IsRegister(Register.HL, Register.IX, Register.IY)); + public static SchemaType DIRECT_SP = new And(DIRECT, new IsRegister(Register.SP)); + public static SchemaType DIRECT_AF = new And(DIRECT, new IsRegister(Register.AF)); + public static SchemaType DIRECT_AF_ = new And(DIRECT, new IsRegister(Register.AF_)); + public static SchemaType INDIRECT_N = new And(INDIRECT, INTEGER); + public static SchemaType INDIRECT_C = new And(INDIRECT, new IsRegister(Register.C)); + public static SchemaType INDIRECT_BC_DE = new And(INDIRECT, new IsRegister(Register.BC, Register.DE)); + public static SchemaType INDIRECT_HL_IX_IY = new And(INDIRECT, new IsRegister(Register.HL, Register.IX, Register.IY)); + public static SchemaType INDIRECT_SP = new And(INDIRECT, new IsRegister(Register.SP)); + public static SchemaType DIRECT_R_INDIRECT_HL_IX_IY = new IsDirectRIndirectHLIXIY(); + + public static class IsAny implements SchemaType { + public boolean check(Expression argument) { + return true; + } + } + + public static class And implements SchemaType { + private SchemaType[] types; + public And(SchemaType... types) { + this.types = types; + } + public boolean check(Expression argument) { + for (SchemaType type : types) + if (!type.check(argument)) + return false; + return true; + } + } + + public static class IsDirect implements SchemaType { + public boolean check(Expression argument) { + return !argument.isGroup(); + } + } + + public static class IsIndirect implements SchemaType { + public boolean check(Expression argument) { + return argument.isGroup(); + } + } + + public static class IsAnnotation implements SchemaType { + private final SchemaType rhsType; + public IsAnnotation(SchemaType rhsType) { + this.rhsType = rhsType; + } + public boolean check(Expression argument) { + return rhsType.check(argument.getAnnotee()); + } + } + + public static class IsInteger implements SchemaType { + public boolean check(Expression argument) { + return argument.isInteger(); + } + } + + public static class IsString implements SchemaType { + public boolean check(Expression argument) { + return argument.isString(); + } + } + + public static class IsIdentifier implements SchemaType { + public boolean check(Expression argument) { + return argument instanceof Identifier; + } + } + + public static class IsRegister implements SchemaType { + private Register[] registers; + public IsRegister(Register... registers) { + this.registers = registers; + } + public boolean check(Expression argument) { + if (argument.isRegister()) { + Register register = argument.getRegister(); + for (Register expected : registers) + if (register == expected) + return true; + } + return false; + } + } + + public static class IsRegister8Bit implements SchemaType { + public boolean check(Expression argument) { + if (argument.isRegister()) { + Register register = argument.getRegister(); + return !register.isPair() && register != Register.I && register != Register.R; + } + return false; + } + } + + public static class IsDirectRIndirectHLIXIY implements SchemaType { + public boolean check(Expression argument) { + if (argument.isRegister()) { + Register register = argument.getRegister(); + return DIRECT.check(argument) && !register.isPair() && register != Register.I && register != Register.R || + INDIRECT.check(argument) && (register == Register.HL || register.isIndex()); + } + return false; + } + } + + public static class IsFlag implements SchemaType { + public boolean check(Expression argument) { + return argument.isFlag(); + } + } + + public static class IsFlagZC implements SchemaType { + public boolean check(Expression argument) { + return argument.isFlag() && argument.getFlag().getCode() < 4; + } + } + +} diff --git a/src/nl/grauw/glass/expressions/SchemaType.java b/src/nl/grauw/glass/expressions/SchemaType.java new file mode 100644 index 0000000..821f34f --- /dev/null +++ b/src/nl/grauw/glass/expressions/SchemaType.java @@ -0,0 +1,5 @@ +package nl.grauw.glass.expressions; + +public interface SchemaType { + public boolean check(Expression argument); +} diff --git a/src/nl/grauw/glass/expressions/SectionContext.java b/src/nl/grauw/glass/expressions/SectionContext.java new file mode 100644 index 0000000..2241e72 --- /dev/null +++ b/src/nl/grauw/glass/expressions/SectionContext.java @@ -0,0 +1,9 @@ +package nl.grauw.glass.expressions; + +import nl.grauw.glass.instructions.Section; + +public interface SectionContext { + + public void addSection(Section section); + +} diff --git a/src/nl/grauw/glass/expressions/SectionContextLiteral.java b/src/nl/grauw/glass/expressions/SectionContextLiteral.java new file mode 100644 index 0000000..18ad7f8 --- /dev/null +++ b/src/nl/grauw/glass/expressions/SectionContextLiteral.java @@ -0,0 +1,22 @@ +package nl.grauw.glass.expressions; + +public class SectionContextLiteral extends ContextLiteral { + + private final SectionContext sectionContext; + + public SectionContextLiteral(Context context, SectionContext sectionContext) { + super(context); + this.sectionContext = sectionContext; + } + + @Override + public boolean isSectionContext() { + return true; + } + + @Override + public SectionContext getSectionContext() { + return sectionContext; + } + +} diff --git a/src/nl/grauw/glass/expressions/Sequence.java b/src/nl/grauw/glass/expressions/Sequence.java new file mode 100644 index 0000000..cd8b66e --- /dev/null +++ b/src/nl/grauw/glass/expressions/Sequence.java @@ -0,0 +1,57 @@ +package nl.grauw.glass.expressions; + +import java.util.List; + +public class Sequence extends BinaryOperator { + + public Sequence(Expression value, Expression tail) { + super(value, tail); + } + + @Override + public Sequence copy(Context context) { + return new Sequence(term1.copy(context), term2.copy(context)); + } + + public Expression getValue() { + return term1; + } + + public Expression getTail() { + return term2; + } + + @Override + protected void addToList(List list) { + term1.addToList(list); + Expression tail = term2; + while (tail != null) { + tail.getElement().addToList(list); + tail = tail.getNext(); + } + } + + @Override + public Expression getElement(int index) { + return index == 0 ? term1 : term2.getElement(index - 1); + } + + @Override + public Expression getNext() { + return term2; + } + + @Override + public String getLexeme() { + return ","; + } + + public String toString() { + return "" + term1 + ", " + term2; + } + + public String toDebugString() { + return "{" + term1.toDebugString() + ", " + term2.toDebugString() + "}"; + } + +} diff --git a/src/nl/grauw/glass/expressions/ShiftLeft.java b/src/nl/grauw/glass/expressions/ShiftLeft.java new file mode 100644 index 0000000..ccfe89f --- /dev/null +++ b/src/nl/grauw/glass/expressions/ShiftLeft.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class ShiftLeft extends BinaryOperator { + + public ShiftLeft(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public ShiftLeft copy(Context context) { + return new ShiftLeft(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() << term2.getInteger(); + } + + @Override + public String getLexeme() { + return "<<"; + } + +} diff --git a/src/nl/grauw/glass/expressions/ShiftRight.java b/src/nl/grauw/glass/expressions/ShiftRight.java new file mode 100644 index 0000000..88505b7 --- /dev/null +++ b/src/nl/grauw/glass/expressions/ShiftRight.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class ShiftRight extends BinaryOperator { + + public ShiftRight(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public ShiftRight copy(Context context) { + return new ShiftRight(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() >> term2.getInteger(); + } + + @Override + public String getLexeme() { + return ">>"; + } + +} diff --git a/src/nl/grauw/glass/expressions/StringLiteral.java b/src/nl/grauw/glass/expressions/StringLiteral.java new file mode 100644 index 0000000..8f5547c --- /dev/null +++ b/src/nl/grauw/glass/expressions/StringLiteral.java @@ -0,0 +1,63 @@ +package nl.grauw.glass.expressions; + +import java.util.List; + +public class StringLiteral extends Literal { + + private final String string; + + public StringLiteral(String string) { + this.string = string; + } + + @Override + public StringLiteral copy(Context context) { + return this; + } + + @Override + public boolean isInteger() { + return string.length() == 1; + } + + @Override + public int getInteger() { + if (string.length() != 1) + throw new EvaluationException("Can not evaluate strings of more than 1 character to integer."); + return string.codePointAt(0); + } + + @Override + public boolean isString() { + return true; + } + + public String getString() { + return string; + } + + @Override + protected void addToList(List list) { + for (int i = 0, length = string.length(); i < length; i++) + list.add(new CharacterLiteral(string.charAt(i))); + } + + public String toString() { + String escaped = string; + escaped = escaped.replace("\\", "\\\\"); + escaped = escaped.replace("\"", "\\\""); + escaped = escaped.replace("\0", "\\0"); + escaped = escaped.replace("\7", "\\a"); + escaped = escaped.replace("\t", "\\t"); + escaped = escaped.replace("\n", "\\n"); + escaped = escaped.replace("\f", "\\f"); + escaped = escaped.replace("\r", "\\r"); + escaped = escaped.replace("\33", "\\e"); + return "\"" + escaped + "\""; + } + + public String toDebugString() { + return toString(); + } + +} diff --git a/src/nl/grauw/glass/expressions/Subtract.java b/src/nl/grauw/glass/expressions/Subtract.java new file mode 100644 index 0000000..e9fee72 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Subtract.java @@ -0,0 +1,51 @@ +package nl.grauw.glass.expressions; + +public class Subtract extends BinaryOperator { + + public Subtract(Expression minuend, Expression subtrahend) { + super(minuend, subtrahend); + } + + @Override + public Subtract copy(Context context) { + return new Subtract(term1.copy(context), term2.copy(context)); + } + + public Expression getMinuend() { + return term1; + } + + public Expression getSubtrahend() { + return term2; + } + + @Override + public int getInteger() { + return term1.getInteger() - term2.getInteger(); + } + + @Override + public boolean isRegister() { + if (term1.isRegister() && term2.isInteger()) { + Register register = term1.getRegister(); + return register.isIndex() && register.isPair(); + } + return false; + } + + @Override + public Register getRegister() { + if (term1.isRegister() && term2.isInteger()) { + Register register = term1.getRegister(); + if (register.isIndex() && register.isPair()) + return new Register(register, new Subtract(register.getIndexOffset(), term2)); + } + throw new EvaluationException("Not a register."); + } + + @Override + public String getLexeme() { + return "-"; + } + +} diff --git a/src/nl/grauw/glass/expressions/UnaryOperator.java b/src/nl/grauw/glass/expressions/UnaryOperator.java new file mode 100644 index 0000000..02c59df --- /dev/null +++ b/src/nl/grauw/glass/expressions/UnaryOperator.java @@ -0,0 +1,30 @@ +package nl.grauw.glass.expressions; + +public abstract class UnaryOperator extends Expression { + + protected final Expression term; + + public abstract String getLexeme(); + + public UnaryOperator(Expression term) { + this.term = term; + } + + public Expression getTerm() { + return term; + } + + @Override + public boolean isInteger() { + return term.isInteger(); + } + + public String toString() { + return getLexeme() + term; + } + + public String toDebugString() { + return getLexeme() + term.toDebugString(); + } + +} diff --git a/src/nl/grauw/glass/expressions/Xor.java b/src/nl/grauw/glass/expressions/Xor.java new file mode 100644 index 0000000..5558083 --- /dev/null +++ b/src/nl/grauw/glass/expressions/Xor.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.expressions; + +public class Xor extends BinaryOperator { + + public Xor(Expression term1, Expression term2) { + super(term1, term2); + } + + @Override + public Xor copy(Context context) { + return new Xor(term1.copy(context), term2.copy(context)); + } + + @Override + public int getInteger() { + return term1.getInteger() ^ term2.getInteger(); + } + + @Override + public String getLexeme() { + return "^"; + } + +} diff --git a/src/nl/grauw/glass/instructions/Adc.java b/src/nl/grauw/glass/instructions/Adc.java new file mode 100644 index 0000000..fd3d7ed --- /dev/null +++ b/src/nl/grauw/glass/instructions/Adc.java @@ -0,0 +1,91 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Adc extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Adc_A_R.ARGUMENTS.check(arguments)) + return new Adc_A_R(context, arguments.getElement(1)); + if (Adc_A_N.ARGUMENTS.check(arguments)) + return new Adc_A_N(context, arguments.getElement(1)); + if (Adc_HL_RR.ARGUMENTS.check(arguments)) + return new Adc_HL_RR(context, arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Adc_A_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Adc_A_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0x88 | register.get8BitCode())); + } + + } + + public static class Adc_A_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_N); + + private Expression argument; + + public Adc_A_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xCE, (byte)argument.getInteger() }; + } + + } + + public static class Adc_HL_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_HL, Schema.DIRECT_RR); + + private Expression argument; + + public Adc_HL_RR(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)(0x4A | argument.getRegister().get16BitCode() << 4) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Add.java b/src/nl/grauw/glass/instructions/Add.java new file mode 100644 index 0000000..4a13358 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Add.java @@ -0,0 +1,96 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Add extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Add_A_R.ARGUMENTS.check(arguments)) + return new Add_A_R(context, arguments.getElement(1)); + if (Add_A_N.ARGUMENTS.check(arguments)) + return new Add_A_N(context, arguments.getElement(1)); + if (Add_HL_RR.ARGUMENTS.check(arguments)) + return new Add_HL_RR(context, arguments.getElement(0).getRegister(), arguments.getElement(1).getRegister()); + throw new ArgumentException(); + } + + public static class Add_A_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Add_A_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0x80 | register.get8BitCode())); + } + + } + + public static class Add_A_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_N); + + private Expression argument; + + public Add_A_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xC6, (byte)argument.getInteger() }; + } + + } + + public static class Add_HL_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_HL_IX_IY, Schema.DIRECT_RR_INDEX); + + private Register register1; + private Register register2; + + public Add_HL_RR(Scope context, Register register1, Register register2) { + super(context); + this.register1 = register1; + this.register2 = register2; + + if (register1.get16BitCode() == register2.get16BitCode() && register1 != register2) + throw new ArgumentException(); + } + + @Override + public int getSize() { + return indexifyDirect(register1.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + return indexifyDirect(register1.getRegister(), (byte)(0x09 | register2.getRegister().get16BitCode() << 4)); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/And.java b/src/nl/grauw/glass/instructions/And.java new file mode 100644 index 0000000..610494d --- /dev/null +++ b/src/nl/grauw/glass/instructions/And.java @@ -0,0 +1,66 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class And extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (And_R.ARGUMENTS.check(arguments)) + return new And_R(context, arguments); + if (And_N.ARGUMENTS.check(arguments)) + return new And_N(context, arguments); + throw new ArgumentException(); + } + + public static class And_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public And_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0xA0 | register.get8BitCode())); + } + + } + + public static class And_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public And_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xE6, (byte)argument.getInteger() }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/ArgumentException.java b/src/nl/grauw/glass/instructions/ArgumentException.java new file mode 100644 index 0000000..72f5dd5 --- /dev/null +++ b/src/nl/grauw/glass/instructions/ArgumentException.java @@ -0,0 +1,16 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.AssemblyException; + +public class ArgumentException extends AssemblyException { + private static final long serialVersionUID = 1L; + + public ArgumentException() { + this("Invalid arguments."); + } + + public ArgumentException(String message) { + super(message); + } + +} diff --git a/src/nl/grauw/glass/instructions/Bit.java b/src/nl/grauw/glass/instructions/Bit.java new file mode 100644 index 0000000..61d5f1a --- /dev/null +++ b/src/nl/grauw/glass/instructions/Bit.java @@ -0,0 +1,46 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Bit extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Bit_N_R.ARGUMENTS.check(arguments)) + return new Bit_N_R(context, arguments.getElement(0), arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Bit_N_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N, Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument1; + private Expression argument2; + + public Bit_N_R(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return indexifyIndirect(argument2.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + int value = argument1.getInteger(); + if (value < 0 || value > 7) + throw new ArgumentException(); + Register register = argument2.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x40 | value << 3 | register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Call.java b/src/nl/grauw/glass/instructions/Call.java new file mode 100644 index 0000000..66578dd --- /dev/null +++ b/src/nl/grauw/glass/instructions/Call.java @@ -0,0 +1,68 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Call extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Call_F_N.ARGUMENTS.check(arguments)) + return new Call_F_N(context, arguments.getElement(0), arguments.getElement(1)); + if (Call_N.ARGUMENTS.check(arguments)) + return new Call_N(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Call_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Call_N(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 3; + } + + @Override + public byte[] getBytes() { + int address = argument.getAddress(); + return new byte[] { (byte)0xCD, (byte)address, (byte)(address >> 8) }; + } + + } + + public static class Call_F_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(new Schema.IsFlag(), Schema.DIRECT_N); + + private Expression argument1; + private Expression argument2; + + public Call_F_N(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return 3; + } + + @Override + public byte[] getBytes() { + int address = argument2.getAddress(); + return new byte[] { (byte)(0xC4 | argument1.getFlag().getCode() << 3), (byte)address, (byte)(address >> 8) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ccf.java b/src/nl/grauw/glass/instructions/Ccf.java new file mode 100644 index 0000000..25d68d5 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ccf.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ccf extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ccf_.ARGUMENTS.check(arguments)) + return new Ccf_(context); + throw new ArgumentException(); + } + + public static class Ccf_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ccf_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x3F }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Cp.java b/src/nl/grauw/glass/instructions/Cp.java new file mode 100644 index 0000000..029672d --- /dev/null +++ b/src/nl/grauw/glass/instructions/Cp.java @@ -0,0 +1,66 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Cp extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Cp_R.ARGUMENTS.check(arguments)) + return new Cp_R(context, arguments); + if (Cp_N.ARGUMENTS.check(arguments)) + return new Cp_N(context, arguments); + throw new ArgumentException(); + } + + public static class Cp_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Cp_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0xB8 | register.get8BitCode())); + } + + } + + public static class Cp_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Cp_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xFE, (byte)argument.getInteger() }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Cpd.java b/src/nl/grauw/glass/instructions/Cpd.java new file mode 100644 index 0000000..f4b7c54 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Cpd.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Cpd extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Cpd_.ARGUMENTS.check(arguments)) + return new Cpd_(context); + throw new ArgumentException(); + } + + public static class Cpd_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Cpd_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xA9 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Cpdr.java b/src/nl/grauw/glass/instructions/Cpdr.java new file mode 100644 index 0000000..784676c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Cpdr.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Cpdr extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Cpdr_.ARGUMENTS.check(arguments)) + return new Cpdr_(context); + throw new ArgumentException(); + } + + public static class Cpdr_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Cpdr_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xB9 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Cpi.java b/src/nl/grauw/glass/instructions/Cpi.java new file mode 100644 index 0000000..a91e072 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Cpi.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Cpi extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Cpi_.ARGUMENTS.check(arguments)) + return new Cpi_(context); + throw new ArgumentException(); + } + + public static class Cpi_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Cpi_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xA1 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Cpir.java b/src/nl/grauw/glass/instructions/Cpir.java new file mode 100644 index 0000000..de88118 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Cpir.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Cpir extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Cpir_.ARGUMENTS.check(arguments)) + return new Cpir_(context); + throw new ArgumentException(); + } + + public static class Cpir_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Cpir_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xB1 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Cpl.java b/src/nl/grauw/glass/instructions/Cpl.java new file mode 100644 index 0000000..cdc5fda --- /dev/null +++ b/src/nl/grauw/glass/instructions/Cpl.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Cpl extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Cpl_.ARGUMENTS.check(arguments)) + return new Cpl_(context); + throw new ArgumentException(); + } + + public static class Cpl_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Cpl_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x2F }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Daa.java b/src/nl/grauw/glass/instructions/Daa.java new file mode 100644 index 0000000..6fce034 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Daa.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Daa extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Daa_.ARGUMENTS.check(arguments)) + return new Daa_(context); + throw new ArgumentException(); + } + + public static class Daa_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Daa_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x27 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Db.java b/src/nl/grauw/glass/instructions/Db.java new file mode 100644 index 0000000..c7be5cc --- /dev/null +++ b/src/nl/grauw/glass/instructions/Db.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; + +public class Db extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (arguments != null) + return new Db_N(context, arguments.getList()); + throw new ArgumentException(); + } + + public static class Db_N extends InstructionObject { + + private List arguments; + + public Db_N(Scope context, List arguments) { + super(context); + this.arguments = arguments; + } + + @Override + public int getSize() { + return arguments.size(); + } + + @Override + public byte[] getBytes() { + byte[] bytes = new byte[arguments.size()]; + for (int i = 0, length = arguments.size(); i < length; i++) + bytes[i] = (byte)arguments.get(i).getInteger(); + return bytes; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Dd.java b/src/nl/grauw/glass/instructions/Dd.java new file mode 100644 index 0000000..dd0198a --- /dev/null +++ b/src/nl/grauw/glass/instructions/Dd.java @@ -0,0 +1,45 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; + +public class Dd extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (arguments != null) + return new Dd_N(context, arguments.getList()); + throw new ArgumentException(); + } + + public static class Dd_N extends InstructionObject { + + private List arguments; + + public Dd_N(Scope context, List arguments) { + super(context); + this.arguments = arguments; + } + + @Override + public int getSize() { + return arguments.size() * 4; + } + + @Override + public byte[] getBytes() { + byte[] bytes = new byte[arguments.size() * 4]; + for (int i = 0, length = arguments.size(); i < length; i++) { + bytes[i * 4] = (byte)arguments.get(i).getInteger(); + bytes[i * 4 + 1] = (byte)(arguments.get(i).getInteger() >> 8); + bytes[i * 4 + 2] = (byte)(arguments.get(i).getInteger() >> 16); + bytes[i * 4 + 3] = (byte)(arguments.get(i).getInteger() >> 24); + } + return bytes; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Dec.java b/src/nl/grauw/glass/instructions/Dec.java new file mode 100644 index 0000000..68b773b --- /dev/null +++ b/src/nl/grauw/glass/instructions/Dec.java @@ -0,0 +1,67 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Dec extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Dec_R.ARGUMENTS.check(arguments)) + return new Dec_R(context, arguments.getElement(0)); + if (Dec_RR.ARGUMENTS.check(arguments)) + return new Dec_RR(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Dec_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Dec_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0x05 | register.get8BitCode() << 3)); + } + + } + + public static class Dec_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_RR_INDEX); + + private Expression argument; + + public Dec_RR(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyDirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyDirect(register, (byte)(0x0B | register.get16BitCode() << 4)); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Di.java b/src/nl/grauw/glass/instructions/Di.java new file mode 100644 index 0000000..1549af8 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Di.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Di extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Di_.ARGUMENTS.check(arguments)) + return new Di_(context); + throw new ArgumentException(); + } + + public static class Di_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Di_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xF3 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Djnz.java b/src/nl/grauw/glass/instructions/Djnz.java new file mode 100644 index 0000000..428f6f6 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Djnz.java @@ -0,0 +1,42 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Djnz extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Djnz_N.ARGUMENTS.check(arguments)) + return new Djnz_N(context, arguments); + throw new ArgumentException(); + } + + public static class Djnz_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Djnz_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + int offset = argument.getAddress() - (context.getAddress() + 2); + if (offset < -128 || offset > 127) + throw new ArgumentException("Jump offset out of range: " + offset); + return new byte[] { (byte)0x10, (byte)offset }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ds.java b/src/nl/grauw/glass/instructions/Ds.java new file mode 100644 index 0000000..0b670a4 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ds.java @@ -0,0 +1,105 @@ +package nl.grauw.glass.instructions; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import nl.grauw.glass.AssemblyException; +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Identifier; +import nl.grauw.glass.expressions.IntegerLiteral; +import nl.grauw.glass.expressions.Schema; +import nl.grauw.glass.expressions.SectionContext; + +public class Ds extends InstructionFactory implements SectionContext { + + public static Schema ARGUMENTS_N = new Schema(new Schema.IsAnnotation(Schema.INTEGER)); + public static Schema ARGUMENTS_N_N = new Schema(Schema.INTEGER, Schema.INTEGER); + + private final List
sections = new ArrayList<>(); + + @Override + public void addSection(Section section) { + sections.add(section); + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS_N.check(arguments)) + return new Ds_N_N(context, arguments.getAnnotation(), + arguments.getAnnotee(), IntegerLiteral.ZERO); + if (ARGUMENTS_N_N.check(arguments)) + return new Ds_N_N(context, null, arguments.getElement(0), arguments.getElement(1)); + throw new ArgumentException(); + } + + public class Ds_N_N extends InstructionObject { + + private final boolean virtual; + private final Expression size; + private final Expression value; + + public Ds_N_N(Scope context, Identifier annotation, Expression size, Expression value) { + super(context); + this.virtual = annotation != null && ("virtual".equals(annotation.getName()) || "VIRTUAL".equals(annotation.getName())); + this.size = size; + this.value = value; + + if (annotation != null && !virtual) + throw new ArgumentException("Unsupported annotation: " + annotation.getName()); + } + + @Override + public int resolve(int address) { + int innerAddress = address; + for (Section section : sections) + innerAddress = section.getSource().resolve(innerAddress); + return super.resolve(address); + } + + @Override + public int getSize() { + return size.getInteger(); + } + + @Override + public void generateObjectCode(OutputStream output) throws IOException { + byte[] bytes = getSectionBytes(); + if (bytes.length > size.getInteger()) + throw new AssemblyException("Section size exceeds space (required: " + + bytes.length + " bytes, available: " + size.getInteger() + " bytes)."); + + if (virtual) + return; + + output.write(bytes); + + byte[] padding = new byte[size.getInteger() - bytes.length]; + Arrays.fill(padding, (byte)value.getInteger()); + + output.write(padding); + } + + public byte[] getSectionBytes() throws IOException { + ByteArrayOutputStream sourceByteStream = new ByteArrayOutputStream(size.getInteger()); + for (Section section : sections) + section.getSource().generateObjectCode(sourceByteStream); + return sourceByteStream.toByteArray(); + } + + @Override + public byte[] getBytes() { + if (virtual) + return new byte[] {}; + byte[] bytes = new byte[size.getInteger()]; + Arrays.fill(bytes, (byte)value.getInteger()); + return bytes; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Dw.java b/src/nl/grauw/glass/instructions/Dw.java new file mode 100644 index 0000000..07c64ab --- /dev/null +++ b/src/nl/grauw/glass/instructions/Dw.java @@ -0,0 +1,43 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; + +public class Dw extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (arguments != null) + return new Dw_N(context, arguments.getList()); + throw new ArgumentException(); + } + + public static class Dw_N extends InstructionObject { + + private List arguments; + + public Dw_N(Scope context, List arguments) { + super(context); + this.arguments = arguments; + } + + @Override + public int getSize() { + return arguments.size() * 2; + } + + @Override + public byte[] getBytes() { + byte[] bytes = new byte[arguments.size() * 2]; + for (int i = 0, length = arguments.size(); i < length; i++) { + bytes[i * 2] = (byte)arguments.get(i).getInteger(); + bytes[i * 2 + 1] = (byte)(arguments.get(i).getInteger() >> 8); + } + return bytes; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ei.java b/src/nl/grauw/glass/instructions/Ei.java new file mode 100644 index 0000000..cbe458c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ei.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ei extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ei_.ARGUMENTS.check(arguments)) + return new Ei_(context); + throw new ArgumentException(); + } + + public static class Ei_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ei_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xFB }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Else.java b/src/nl/grauw/glass/instructions/Else.java new file mode 100644 index 0000000..e47a698 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Else.java @@ -0,0 +1,18 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Else extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Empty.java b/src/nl/grauw/glass/instructions/Empty.java new file mode 100644 index 0000000..d08730b --- /dev/null +++ b/src/nl/grauw/glass/instructions/Empty.java @@ -0,0 +1,35 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; + +public class Empty extends InstructionFactory { + + public static final Empty INSTANCE = new Empty(); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + return new EmptyObject(context); + } + + public static class EmptyObject extends InstructionObject { + + private static final byte[] NO_BYTES = new byte[] {}; + + public EmptyObject(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 0; + } + + @Override + public byte[] getBytes() { + return NO_BYTES; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/End.java b/src/nl/grauw/glass/instructions/End.java new file mode 100644 index 0000000..6055e70 --- /dev/null +++ b/src/nl/grauw/glass/instructions/End.java @@ -0,0 +1,18 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class End extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Endif.java b/src/nl/grauw/glass/instructions/Endif.java new file mode 100644 index 0000000..736f241 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Endif.java @@ -0,0 +1,18 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Endif extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Endm.java b/src/nl/grauw/glass/instructions/Endm.java new file mode 100644 index 0000000..a7af6d2 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Endm.java @@ -0,0 +1,18 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Endm extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Endp.java b/src/nl/grauw/glass/instructions/Endp.java new file mode 100644 index 0000000..28f1628 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Endp.java @@ -0,0 +1,18 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Endp extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Ends.java b/src/nl/grauw/glass/instructions/Ends.java new file mode 100644 index 0000000..d213ac3 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ends.java @@ -0,0 +1,18 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ends extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Equ.java b/src/nl/grauw/glass/instructions/Equ.java new file mode 100644 index 0000000..dfc28f3 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Equ.java @@ -0,0 +1,18 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Equ extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(Schema.ANY); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Error.java b/src/nl/grauw/glass/instructions/Error.java new file mode 100644 index 0000000..49c0ae5 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Error.java @@ -0,0 +1,51 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.AssemblyException; +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Error extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + public static Schema ARGUMENTS_S = new Schema(Schema.STRING); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments) || ARGUMENTS_S.check(arguments)) + return new Error_(context, arguments); + throw new ArgumentException(); + } + + public static class Error_ extends Empty.EmptyObject { + + private final Expression argument; + + public Error_(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public byte[] getBytes() { + if (argument == null) + throw new ErrorDirectiveException(); + throw new ErrorDirectiveException(argument.getString()); + } + + } + + public static class ErrorDirectiveException extends AssemblyException { + private static final long serialVersionUID = 1L; + + public ErrorDirectiveException() { + this("Error directive was encountered."); + } + + public ErrorDirectiveException(String message) { + super(message); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ex.java b/src/nl/grauw/glass/instructions/Ex.java new file mode 100644 index 0000000..2e53c83 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ex.java @@ -0,0 +1,83 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ex extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ex_AF.ARGUMENTS.check(arguments)) + return new Ex_AF(context); + if (Ex_DE_HL.ARGUMENTS.check(arguments)) + return new Ex_DE_HL(context); + if (Ex_SP.ARGUMENTS.check(arguments)) + return new Ex_SP(context, arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Ex_AF extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_AF, Schema.DIRECT_AF_); + + public Ex_AF(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x08 }; + } + + } + + public static class Ex_DE_HL extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_DE, Schema.DIRECT_HL); + + public Ex_DE_HL(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xEB }; + } + + } + + public static class Ex_SP extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_SP, Schema.DIRECT_HL_IX_IY); + + private Expression argument; + + public Ex_SP(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyDirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + return indexifyDirect(argument.getRegister(), (byte)0xE3); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Exx.java b/src/nl/grauw/glass/instructions/Exx.java new file mode 100644 index 0000000..efb750c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Exx.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Exx extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Exx_.ARGUMENTS.check(arguments)) + return new Exx_(context); + throw new ArgumentException(); + } + + public static class Exx_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Exx_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xD9 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Halt.java b/src/nl/grauw/glass/instructions/Halt.java new file mode 100644 index 0000000..040e22b --- /dev/null +++ b/src/nl/grauw/glass/instructions/Halt.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Halt extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Halt_.ARGUMENTS.check(arguments)) + return new Halt_(context); + throw new ArgumentException(); + } + + public static class Halt_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Halt_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x76 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/If.java b/src/nl/grauw/glass/instructions/If.java new file mode 100644 index 0000000..ff5e2cd --- /dev/null +++ b/src/nl/grauw/glass/instructions/If.java @@ -0,0 +1,77 @@ +package nl.grauw.glass.instructions; + +import java.io.IOException; +import java.io.OutputStream; + +import nl.grauw.glass.AssemblyException; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class If extends InstructionFactory { + + private static Schema ARGUMENTS = new Schema(Schema.INTEGER); + + private final Source thenSource; + private final Source elseSource; + + public If(Source thenSource, Source elseSource) { + this.thenSource = thenSource; + this.elseSource = elseSource; + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new IfObject(context, arguments); + throw new ArgumentException(); + } + + public class IfObject extends InstructionObject { + + private final Expression argument; + + public IfObject(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int resolve(int address) { + context.setAddress(address); + if (argument.getInteger() != 0) { + thenSource.register(); + thenSource.expand(); + return thenSource.resolve(address); + } else if (elseSource != null) { + elseSource.register(); + elseSource.expand(); + return elseSource.resolve(address); + } else { + return address; + } + } + + @Override + public int getSize() { + throw new AssemblyException("Not implemented."); + } + + @Override + public void generateObjectCode(OutputStream output) throws IOException { + if (argument.getInteger() != 0) { + thenSource.generateObjectCode(output); + } else if (elseSource != null) { + elseSource.generateObjectCode(output); + } + } + + @Override + public byte[] getBytes() { + throw new AssemblyException("Not implemented."); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Im.java b/src/nl/grauw/glass/instructions/Im.java new file mode 100644 index 0000000..5bf6539 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Im.java @@ -0,0 +1,47 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Im extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Im_N.ARGUMENTS.check(arguments)) + return new Im_N(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Im_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Im_N(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + int value = argument.getInteger(); + if (value == 0) { + return new byte[] { (byte)0xED, (byte)0x46 }; + } else if (value == 1) { + return new byte[] { (byte)0xED, (byte)0x56 }; + } else if (value == 2) { + return new byte[] { (byte)0xED, (byte)0x5E }; + } + throw new ArgumentException(); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/In.java b/src/nl/grauw/glass/instructions/In.java new file mode 100644 index 0000000..1d1ea60 --- /dev/null +++ b/src/nl/grauw/glass/instructions/In.java @@ -0,0 +1,68 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class In extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (In_N_C.ARGUMENTS.check(arguments)) + return new In_N_C(context, arguments.getElement(0)); + if (In_N_C.ARGUMENTS_NO_R.check(arguments)) + return new In_N_C(context, Register.HL); + if (In_N_N.ARGUMENTS.check(arguments)) + return new In_N_N(context, arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class In_N_C extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R, Schema.INDIRECT_C); + public static Schema ARGUMENTS_NO_R = new Schema(Schema.INDIRECT_C); + + private Expression argument; + + public In_N_C(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)(0x40 | argument.getRegister().get8BitCode() << 3) }; + } + + } + + public static class In_N_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.INDIRECT_N); + + private Expression argument; + + public In_N_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xDB, (byte)argument.getInteger() }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Inc.java b/src/nl/grauw/glass/instructions/Inc.java new file mode 100644 index 0000000..20b78fb --- /dev/null +++ b/src/nl/grauw/glass/instructions/Inc.java @@ -0,0 +1,67 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Inc extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Inc_R.ARGUMENTS.check(arguments)) + return new Inc_R(context, arguments.getElement(0)); + if (Inc_RR.ARGUMENTS.check(arguments)) + return new Inc_RR(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Inc_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Inc_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0x04 | register.get8BitCode() << 3)); + } + + } + + public static class Inc_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_RR_INDEX); + + private Expression argument; + + public Inc_RR(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyDirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyDirect(register, (byte)(0x03 | register.get16BitCode() << 4)); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Incbin.java b/src/nl/grauw/glass/instructions/Incbin.java new file mode 100644 index 0000000..6ba667e --- /dev/null +++ b/src/nl/grauw/glass/instructions/Incbin.java @@ -0,0 +1,98 @@ +package nl.grauw.glass.instructions; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import nl.grauw.glass.AssemblyException; +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.IntegerLiteral; +import nl.grauw.glass.expressions.Schema; + +public class Incbin extends InstructionFactory { + + private final List basePaths = new ArrayList(); + + public Incbin(File basePath, List includePaths) { + this.basePaths.add(basePath); + this.basePaths.addAll(includePaths); + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Incbin_.ARGUMENTS_S.check(arguments)) + return new Incbin_(context, arguments.getElement(0), + IntegerLiteral.ZERO, null, basePaths); + if (Incbin_.ARGUMENTS_S_N.check(arguments)) + return new Incbin_(context, arguments.getElement(0), + arguments.getElement(1), null, basePaths); + if (Incbin_.ARGUMENTS_S_N_N.check(arguments)) + return new Incbin_(context, arguments.getElement(0), + arguments.getElement(1), arguments.getElement(2), basePaths); + throw new ArgumentException(); + } + + public static class Incbin_ extends InstructionObject { + + public static Schema ARGUMENTS_S = new Schema(Schema.STRING); + public static Schema ARGUMENTS_S_N = new Schema(Schema.STRING, Schema.INTEGER); + public static Schema ARGUMENTS_S_N_N = new Schema(Schema.STRING, Schema.INTEGER, Schema.INTEGER); + + private final Expression path; + private final Expression start; + private final Expression length; + private final List basePaths; + private byte[] bytes; + + public Incbin_(Scope context, Expression path, Expression start, Expression length, List basePaths) { + super(context); + this.path = path; + this.start = start; + this.length = length; + this.basePaths = basePaths; + } + + @Override + public int getSize() { + return length != null ? length.getInteger() : getBytes().length; + } + + @Override + public byte[] getBytes() { + if (bytes == null) { + byte[] allBytes = loadFile(); + + int from = this.start.getInteger(); + int to = this.length != null ? from + this.length.getInteger() : allBytes.length; + if (from < 0 || from > allBytes.length) + throw new AssemblyException("Incbin start exceeds file size."); + if (to < from || to > allBytes.length) + throw new AssemblyException("Incbin length exceeds file size."); + + bytes = Arrays.copyOfRange(allBytes, from, to); + } + return bytes; + } + + private byte[] loadFile() + { + for (File basePath : basePaths) { + File fullPath = new File(basePath.getParent(), path.getString()); + if (fullPath.exists()) { + try { + return Files.readAllBytes(fullPath.toPath()); + } catch (IOException e) { + throw new AssemblyException(e); + } + } + } + throw new AssemblyException("Incbin file not found: " + path.getString()); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Include.java b/src/nl/grauw/glass/instructions/Include.java new file mode 100644 index 0000000..62b5ee1 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Include.java @@ -0,0 +1,24 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Include extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(Schema.STRING); + public static Schema ARGUMENTS_ONCE = new Schema(new Schema.IsAnnotation(Schema.STRING)); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + if (ARGUMENTS_ONCE.check(arguments)) { + String annotation = arguments.getAnnotation().getName(); + if ("once".equals(annotation) || "ONCE".equals(annotation)) + return new Empty.EmptyObject(context); + } + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Ind.java b/src/nl/grauw/glass/instructions/Ind.java new file mode 100644 index 0000000..74fde4c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ind.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ind extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ind_.ARGUMENTS.check(arguments)) + return new Ind_(context); + throw new ArgumentException(); + } + + public static class Ind_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ind_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xAA }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Indr.java b/src/nl/grauw/glass/instructions/Indr.java new file mode 100644 index 0000000..779bdba --- /dev/null +++ b/src/nl/grauw/glass/instructions/Indr.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Indr extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Indr_.ARGUMENTS.check(arguments)) + return new Indr_(context); + throw new ArgumentException(); + } + + public static class Indr_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Indr_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xBA }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ini.java b/src/nl/grauw/glass/instructions/Ini.java new file mode 100644 index 0000000..f018ded --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ini.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ini extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ini_.ARGUMENTS.check(arguments)) + return new Ini_(context); + throw new ArgumentException(); + } + + public static class Ini_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ini_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xA2 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Inir.java b/src/nl/grauw/glass/instructions/Inir.java new file mode 100644 index 0000000..dc11813 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Inir.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Inir extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Inir_.ARGUMENTS.check(arguments)) + return new Inir_(context); + throw new ArgumentException(); + } + + public static class Inir_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Inir_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xB2 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/InstructionFactory.java b/src/nl/grauw/glass/instructions/InstructionFactory.java new file mode 100644 index 0000000..0c129e2 --- /dev/null +++ b/src/nl/grauw/glass/instructions/InstructionFactory.java @@ -0,0 +1,20 @@ +package nl.grauw.glass.instructions; + +import java.util.ArrayList; +import java.util.List; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; + +public abstract class InstructionFactory { + + public List expand(Line line) { + List lines = new ArrayList(); + lines.add(line); + return lines; + } + + public abstract InstructionObject createObject(Scope context, Expression arguments); + +} diff --git a/src/nl/grauw/glass/instructions/InstructionObject.java b/src/nl/grauw/glass/instructions/InstructionObject.java new file mode 100644 index 0000000..7b7c2eb --- /dev/null +++ b/src/nl/grauw/glass/instructions/InstructionObject.java @@ -0,0 +1,102 @@ +package nl.grauw.glass.instructions; + +import java.io.IOException; +import java.io.OutputStream; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Register; + +public abstract class InstructionObject { + + protected final Scope context; + + public InstructionObject(Scope context) { + this.context = context; + } + + public int resolve(int address) { + context.setAddress(address); + return address + getSize(); + } + + public abstract int getSize(); + + public void generateObjectCode(OutputStream output) throws IOException { + byte[] object = getBytes(); + output.write(object, 0, object.length); + } + + public abstract byte[] getBytes(); + + public int indexifyDirect(Register register, int size) { + return register.isIndex() ? size + 1 : size; + } + + public int indexifyIndirect(Register register, int size) { + return register.isIndex() ? register.isPair() ? size + 2 : size + 1 : size; + } + + /** + * Inserts index register prefix in the object code if needed. + */ + public byte[] indexifyDirect(Register register, byte byte1) { + if (!register.isIndex()) + return new byte[] { byte1 }; + if (register.isPair() && register.getIndexOffset().getInteger() != 0) + throw new ArgumentException("Can not have index offset for direct addressing."); + return new byte[] { register.getIndexCode(), byte1 }; + } + + public byte[] indexifyDirect(Register register, byte byte1, byte byte2) { + if (!register.isIndex()) + return new byte[] { byte1, byte2 }; + if (register.isPair() && register.getIndexOffset().getInteger() != 0) + throw new ArgumentException("Can not have index offset for direct addressing."); + return new byte[] { register.getIndexCode(), byte1, byte2 }; + } + + public byte[] indexifyDirect(Register register, byte byte1, byte byte2, byte byte3) { + if (!register.isIndex()) + return new byte[] { byte1, byte2, byte3 }; + if (register.isPair() && register.getIndexOffset().getInteger() != 0) + throw new ArgumentException("Can not have index offset for direct addressing."); + return new byte[] { register.getIndexCode(), byte1, byte2, byte3 }; + } + + /** + * Inserts index register prefix + offset in the object code if needed. + */ + public byte[] indexifyIndirect(Register register, byte byte1) { + if (!register.isIndex()) + return new byte[] { byte1 }; + if (!register.isPair()) + return indexifyDirect(register, byte1); + int offset = register.getIndexOffset().getInteger(); + if (offset < -128 || offset > 127) + throw new ArgumentException("Index offset out of range: " + offset); + return new byte[] { register.getIndexCode(), byte1, (byte)offset }; + } + + public byte[] indexifyIndirect(Register register, byte byte1, byte byte2) { + if (!register.isIndex()) + return new byte[] { byte1, byte2 }; + if (!register.isPair()) + return indexifyDirect(register, byte1, byte2); + int offset = register.getIndexOffset().getInteger(); + if (offset < -128 || offset > 127) + throw new ArgumentException("Index offset out of range: " + offset); + return new byte[] { register.getIndexCode(), byte1, (byte)offset, byte2 }; + } + + public byte[] indexifyOnlyIndirect(Register register, byte byte1, byte byte2) { + if (!register.isIndex()) + return new byte[] { byte1, byte2 }; + if (!register.isPair()) + throw new ArgumentException(); + int offset = register.getIndexOffset().getInteger(); + if (offset < -128 || offset > 127) + throw new ArgumentException("Index offset out of range: " + offset); + return new byte[] { register.getIndexCode(), byte1, (byte)offset, byte2 }; + } + +} diff --git a/src/nl/grauw/glass/instructions/Irp.java b/src/nl/grauw/glass/instructions/Irp.java new file mode 100644 index 0000000..5030da4 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Irp.java @@ -0,0 +1,51 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Line; +import nl.grauw.glass.ParameterScope; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Irp extends InstructionFactory { + + private final Source source; + + public Irp(Source source) { + this.source = source; + } + + public List expand(Line line) { + Expression arguments = line.getArguments(); + if (arguments == null || !Schema.IDENTIFIER.check(arguments.getElement())) + throw new ArgumentException(); + + List lines = super.expand(line); + Expression parameter = arguments.getElement(); + for (int i = 0; (arguments = arguments.getNext()) != null; i++) { + Scope parameterScope = new ParameterScope(line.getScope(), parameter, arguments.getElement()); + + // set up the number symbol + line.getScope().addSymbol(Integer.toString(i), parameterScope); + Line rept = new Line(parameterScope, line); + rept.setInstruction(Empty.INSTANCE); + lines.add(rept); // so that the parameterScope address gets initialised + + // copy & expand content + List lineCopies = source.getLineCopies(parameterScope); + for (Line lineCopy : lineCopies) + lineCopy.register(parameterScope); + for (Line lineCopy : lineCopies) + lines.addAll(lineCopy.expand()); + } + return lines; + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + return new Empty.EmptyObject(context); + } + +} diff --git a/src/nl/grauw/glass/instructions/Jp.java b/src/nl/grauw/glass/instructions/Jp.java new file mode 100644 index 0000000..2feab6c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Jp.java @@ -0,0 +1,94 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Jp extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Jp_F_N.ARGUMENTS.check(arguments)) + return new Jp_F_N(context, arguments.getElement(0), arguments.getElement(1)); + if (Jp_HL.ARGUMENTS.check(arguments) || Jp_HL.ARGUMENTS_ALT.check(arguments)) + return new Jp_HL(context, arguments.getElement(0)); + if (Jp_N.ARGUMENTS.check(arguments)) + return new Jp_N(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Jp_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Jp_N(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 3; + } + + @Override + public byte[] getBytes() { + int address = argument.getAddress(); + return new byte[] { (byte)0xC3, (byte)address, (byte)(address >> 8) }; + } + + } + + public static class Jp_F_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(new Schema.IsFlag(), Schema.DIRECT_N); + + private Expression argument1; + private Expression argument2; + + public Jp_F_N(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return 3; + } + + @Override + public byte[] getBytes() { + int address = argument2.getAddress(); + return new byte[] { (byte)(0xC2 | argument1.getFlag().getCode() << 3), (byte)address, (byte)(address >> 8) }; + } + + } + + public static class Jp_HL extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_HL_IX_IY); + public static Schema ARGUMENTS_ALT = new Schema(Schema.DIRECT_HL_IX_IY); + + private Expression argument; + + public Jp_HL(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyDirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + return indexifyDirect(argument.getRegister(), (byte)0xE9); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Jr.java b/src/nl/grauw/glass/instructions/Jr.java new file mode 100644 index 0000000..2c2eca0 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Jr.java @@ -0,0 +1,74 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Jr extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Jr_F_N.ARGUMENTS.check(arguments)) + return new Jr_F_N(context, arguments.getElement(0), arguments.getElement(1)); + if (Jr_N.ARGUMENTS.check(arguments)) + return new Jr_N(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Jr_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Jr_N(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + int offset = argument.getAddress() - (context.getAddress() + 2); + if (offset < -128 || offset > 127) + throw new ArgumentException("Jump offset out of range: " + offset); + return new byte[] { (byte)0x18, (byte)offset }; + } + + } + + public static class Jr_F_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(new Schema.IsFlagZC(), Schema.DIRECT_N); + + private final Scope context; + private Expression argument1; + private Expression argument2; + + public Jr_F_N(Scope context, Expression argument1, Expression argument2) { + super(context); + this.context = context; + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + int offset = argument2.getAddress() - (context.getAddress() + 2); + if (offset < -128 || offset > 127) + throw new ArgumentException("Jump offset out of range: " + offset); + return new byte[] { (byte)(0x20 | argument1.getFlag().getCode() << 3), (byte)offset }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ld.java b/src/nl/grauw/glass/instructions/Ld.java new file mode 100644 index 0000000..6d7e097 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ld.java @@ -0,0 +1,406 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Ld extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ld_R_R.ARGUMENTS.check(arguments)) + return new Ld_R_R(context, arguments.getElement(0).getRegister(), arguments.getElement(1).getRegister()); + if (Ld_A_BCDE.ARGUMENTS.check(arguments)) + return new Ld_A_BCDE(context, arguments.getElement(1)); + if (Ld_BCDE_A.ARGUMENTS.check(arguments)) + return new Ld_BCDE_A(context, arguments.getElement(0)); + if (Ld_SP_HL.ARGUMENTS.check(arguments)) + return new Ld_SP_HL(context, arguments.getElement(1)); + if (Ld_A_IR.ARGUMENTS.check(arguments)) + return new Ld_A_IR(context, arguments.getElement(1)); + if (Ld_IR_A.ARGUMENTS.check(arguments)) + return new Ld_IR_A(context, arguments.getElement(0)); + if (Ld_R_N.ARGUMENTS.check(arguments)) + return new Ld_R_N(context, arguments.getElement(0), arguments.getElement(1)); + if (Ld_RR_N.ARGUMENTS.check(arguments)) + return new Ld_RR_N(context, arguments.getElement(0), arguments.getElement(1)); + if (Ld_A_NN.ARGUMENTS.check(arguments)) + return new Ld_A_NN(context, arguments.getElement(0)); + if (Ld_HL_NN.ARGUMENTS.check(arguments)) + return new Ld_HL_NN(context, arguments.getElement(0), arguments.getElement(1)); + if (Ld_RR_NN.ARGUMENTS.check(arguments)) + return new Ld_RR_NN(context, arguments.getElement(0), arguments.getElement(1)); + if (Ld_NN_A.ARGUMENTS.check(arguments)) + return new Ld_NN_A(context, arguments.getElement(1)); + if (Ld_NN_HL.ARGUMENTS.check(arguments)) + return new Ld_NN_HL(context, arguments.getElement(0), arguments.getElement(1)); + if (Ld_NN_RR.ARGUMENTS.check(arguments)) + return new Ld_NN_RR(context, arguments.getElement(0), arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Ld_R_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY, Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Register register1; + private Register register2; + + public Ld_R_R(Scope context, Register register1, Register register2) { + super(context); + this.register1 = register1; + this.register2 = register2; + + if (register1.isPair() && register2.isPair()) + throw new ArgumentException(); // forbid (hl),(hl), (ix+0),(ix-0), etc. + if (register1.isIndex() && register2.isIndex() && register1.getIndexCode() != register2.getIndexCode()) + throw new ArgumentException(); // forbid ixh,iyl, etc. + if (register1.isIndex() && register2.isPair() || register1.isPair() && register2.isIndex()) + throw new ArgumentException(); // forbid (hl),ixh, ixh,(ix+0) + if (register1.isIndex() && !register1.isPair() && (register2 == Register.H || register2 == Register.L)) + throw new ArgumentException(); // forbid iyl,h + if (register2.isIndex() && !register2.isPair() && (register1 == Register.H || register1 == Register.L)) + throw new ArgumentException(); // forbid h,iyl + } + + @Override + public int getSize() { + return indexifyIndirect(register1.isIndex() ? register1 : register2, 1); + } + + @Override + public byte[] getBytes() { + return indexifyIndirect(register1.isIndex() ? register1 : register2, + (byte)(0x40 | register1.get8BitCode() << 3 | register2.get8BitCode())); + } + + } + + public static class Ld_A_BCDE extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.INDIRECT_BC_DE); + + private Expression argument; + + public Ld_A_BCDE(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)(0x0A | argument.getRegister().get16BitCode() << 4) }; + } + + } + + public static class Ld_BCDE_A extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_BC_DE, Schema.DIRECT_A); + + private Expression argument; + + public Ld_BCDE_A(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)(0x02 | argument.getRegister().get16BitCode() << 4) }; + } + + } + + public static class Ld_SP_HL extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_SP, Schema.DIRECT_HL_IX_IY); + + private Expression argument; + + public Ld_SP_HL(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyDirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + return indexifyDirect(argument.getRegister(), (byte)0xF9); + } + + } + + public static class Ld_A_IR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_IR); + + private Expression argument; + + public Ld_A_IR(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + if (argument.getRegister() == Register.I) + return new byte[] { (byte)0xED, (byte)0x57 }; + return new byte[] { (byte)0xED, (byte)0x5F }; + } + + } + + public static class Ld_IR_A extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_IR, Schema.DIRECT_A); + + private Expression argument; + + public Ld_IR_A(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + if (argument.getRegister() == Register.I) + return new byte[] { (byte)0xED, (byte)0x47 }; + return new byte[] { (byte)0xED, (byte)0x4F }; + } + + } + + public static class Ld_R_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY, Schema.DIRECT_N); + + private Expression argument1; + private Expression argument2; + + public Ld_R_N(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return indexifyIndirect(argument1.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument1.getRegister(); + return indexifyIndirect(register, (byte)(0x06 | register.get8BitCode() << 3), (byte)argument2.getInteger()); + } + + } + + public static class Ld_RR_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_RR_INDEX, Schema.DIRECT_N); + + private Expression argument1; + private Expression argument2; + + public Ld_RR_N(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return indexifyDirect(argument1.getRegister(), 3); + } + + @Override + public byte[] getBytes() { + Register register = argument1.getRegister(); + return indexifyDirect(register, (byte)(0x01 | register.get16BitCode() << 4), + (byte)argument2.getInteger(), (byte)(argument2.getInteger() >> 8)); + } + + } + + public static class Ld_A_NN extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_N, Schema.DIRECT_A); + + private Expression argument; + + public Ld_A_NN(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 3; + } + + @Override + public byte[] getBytes() { + int address = argument.getAddress(); + return new byte[] { (byte)0x32, (byte)address, (byte)(address >> 8) }; + } + + } + + public static class Ld_HL_NN extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_HL_IX_IY, Schema.INDIRECT_N); + + private Expression argument1; + private Expression argument2; + + public Ld_HL_NN(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return indexifyDirect(argument1.getRegister(), 3); + } + + @Override + public byte[] getBytes() { + int address = argument2.getAddress(); + return indexifyDirect(argument1.getRegister(), (byte)0x2A, (byte)address, (byte)(address >> 8)); + } + + } + + public static class Ld_RR_NN extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_RR, Schema.INDIRECT_N); + + private Expression argument1; + private Expression argument2; + + public Ld_RR_NN(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return 4; + } + + @Override + public byte[] getBytes() { + int address = argument2.getAddress(); + return new byte[] { (byte)0xED, (byte)(0x4B | argument1.getRegister().get16BitCode() << 4), + (byte)address, (byte)(address >> 8) }; + } + + } + + public static class Ld_NN_A extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.INDIRECT_N); + + private Expression argument; + + public Ld_NN_A(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 3; + } + + @Override + public byte[] getBytes() { + int address = argument.getAddress(); + return new byte[] { (byte)0x3A, (byte)address, (byte)(address >> 8) }; + } + + } + + public static class Ld_NN_HL extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_N, Schema.DIRECT_HL_IX_IY); + + private Expression argument1; + private Expression argument2; + + public Ld_NN_HL(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return indexifyDirect(argument2.getRegister(), 3); + } + + @Override + public byte[] getBytes() { + int address = argument1.getAddress(); + return indexifyDirect(argument2.getRegister(), (byte)0x22, (byte)address, (byte)(address >> 8)); + } + + } + + public static class Ld_NN_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_N, Schema.DIRECT_RR); + + private Expression argument1; + private Expression argument2; + + public Ld_NN_RR(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return 4; + } + + @Override + public byte[] getBytes() { + int address = argument1.getAddress(); + return new byte[] { (byte)0xED, (byte)(0x43 | argument2.getRegister().get16BitCode() << 4), + (byte)address, (byte)(address >> 8) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ldd.java b/src/nl/grauw/glass/instructions/Ldd.java new file mode 100644 index 0000000..c1198f6 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ldd.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ldd extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ldd_.ARGUMENTS.check(arguments)) + return new Ldd_(context); + throw new ArgumentException(); + } + + public static class Ldd_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ldd_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xA8 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Lddr.java b/src/nl/grauw/glass/instructions/Lddr.java new file mode 100644 index 0000000..f9c4c59 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Lddr.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Lddr extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Lddr_.ARGUMENTS.check(arguments)) + return new Lddr_(context); + throw new ArgumentException(); + } + + public static class Lddr_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Lddr_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xB8 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ldi.java b/src/nl/grauw/glass/instructions/Ldi.java new file mode 100644 index 0000000..149ce07 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ldi.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ldi extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ldi_.ARGUMENTS.check(arguments)) + return new Ldi_(context); + throw new ArgumentException(); + } + + public static class Ldi_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ldi_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xA0 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ldir.java b/src/nl/grauw/glass/instructions/Ldir.java new file mode 100644 index 0000000..d481e4b --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ldir.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ldir extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ldir_.ARGUMENTS.check(arguments)) + return new Ldir_(context); + throw new ArgumentException(); + } + + public static class Ldir_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ldir_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xB0 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Macro.java b/src/nl/grauw/glass/instructions/Macro.java new file mode 100644 index 0000000..b290c01 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Macro.java @@ -0,0 +1,75 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.AssemblyException; +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Equals; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Identifier; +import nl.grauw.glass.expressions.IntegerLiteral; + +public class Macro extends InstructionFactory { + + private final Source source; + private final Scope parameterScope; + + public Macro(Source source) { + this.source = new Source(source.getScope()); + this.parameterScope = new Scope(source.getScope()); + this.source.addLines(source.getLineCopies(parameterScope)); + this.source.register(); + } + + @Override + public List expand(Line line) { + Expression parameters = line.getArguments(); + while (parameters != null) { + Expression parameter = parameters.getElement(); + if (!(parameter instanceof Identifier) && + !(parameter instanceof Equals && ((Equals)parameter).getTerm1() instanceof Identifier)) + throw new ArgumentException("Parameter must be an identifier."); + + if (parameter instanceof Equals) { + Equals equals = (Equals)parameter; + parameterScope.addSymbol(((Identifier)equals.getTerm1()).getName(), equals.getTerm2()); + } else { + parameterScope.addSymbol(((Identifier)parameter).getName(), IntegerLiteral.ZERO); + } + parameters = parameters.getNext(); + } + + try { + source.expand(); + } catch (AssemblyException e) { + // ignore + } + return super.expand(line); + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + return new MacroObject(context); + } + + public class MacroObject extends Empty.EmptyObject { + + public MacroObject(Scope context) { + super(context); + } + + @Override + public int resolve(int address) { + try { + source.resolve(0); + } catch (AssemblyException e) { + // ignore + } + return super.resolve(address); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/MacroInstruction.java b/src/nl/grauw/glass/instructions/MacroInstruction.java new file mode 100644 index 0000000..bb3e344 --- /dev/null +++ b/src/nl/grauw/glass/instructions/MacroInstruction.java @@ -0,0 +1,50 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Line; +import nl.grauw.glass.ParameterScope; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Equals; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Identifier; + +public class MacroInstruction extends InstructionFactory { + + private final Expression parameters; + private final Source source; + + public MacroInstruction(Expression parameters, Source source) { + this.parameters = parameters; + this.source = source; + + Expression parameter = parameters != null ? parameters.getElement(0) : null; + for (int i = 0; parameter != null; i++) { + if (!(parameter instanceof Identifier) && + !(parameter instanceof Equals && ((Equals)parameter).getTerm1() instanceof Identifier)) + throw new ArgumentException("Parameter must be an identifier."); + parameter = parameters.getElement(i); + } + } + + @Override + public List expand(Line line) { + Scope parameterScope = new ParameterScope(source.getScope(), parameters, line.getArguments()); + List lines = super.expand(line); + List lineCopies = source.getLineCopies(parameterScope); + for (Line lineCopy : lineCopies) { + lineCopy.register(parameterScope); + lineCopy.register(line.getScope()); + } + for (Line lineCopy : lineCopies) + lines.addAll(lineCopy.expand()); + return lines; + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + return new Empty.EmptyObject(context); + } + +} diff --git a/src/nl/grauw/glass/instructions/Mulub.java b/src/nl/grauw/glass/instructions/Mulub.java new file mode 100644 index 0000000..6013f76 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Mulub.java @@ -0,0 +1,39 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Mulub extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Mulub_R_R.ARGUMENTS.check(arguments)) + return new Mulub_R_R(context, arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Mulub_R_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_R); + + private Expression argument; + + public Mulub_R_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)(0xC1 | argument.getRegister().get8BitCode() << 3) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Muluw.java b/src/nl/grauw/glass/instructions/Muluw.java new file mode 100644 index 0000000..43103c6 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Muluw.java @@ -0,0 +1,39 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Muluw extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Muluw_RR_RR.ARGUMENTS.check(arguments)) + return new Muluw_RR_RR(context, arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Muluw_RR_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_HL, Schema.DIRECT_RR); + + private Expression argument; + + public Muluw_RR_RR(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)(0xC3 | argument.getRegister().get16BitCode() << 4) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Neg.java b/src/nl/grauw/glass/instructions/Neg.java new file mode 100644 index 0000000..b2b3446 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Neg.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Neg extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Neg_.ARGUMENTS.check(arguments)) + return new Neg_(context); + throw new ArgumentException(); + } + + public static class Neg_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Neg_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0x44 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Nop.java b/src/nl/grauw/glass/instructions/Nop.java new file mode 100644 index 0000000..5732f06 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Nop.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Nop extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Nop_.ARGUMENTS.check(arguments)) + return new Nop_(context); + throw new ArgumentException(); + } + + public static class Nop_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Nop_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x00 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Or.java b/src/nl/grauw/glass/instructions/Or.java new file mode 100644 index 0000000..9f19f5f --- /dev/null +++ b/src/nl/grauw/glass/instructions/Or.java @@ -0,0 +1,66 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Or extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Or_R.ARGUMENTS.check(arguments)) + return new Or_R(context, arguments); + if (Or_N.ARGUMENTS.check(arguments)) + return new Or_N(context, arguments); + throw new ArgumentException(); + } + + public static class Or_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Or_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0xB0 | register.get8BitCode())); + } + + } + + public static class Or_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Or_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xF6, (byte)argument.getInteger() }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Org.java b/src/nl/grauw/glass/instructions/Org.java new file mode 100644 index 0000000..bdaa5d5 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Org.java @@ -0,0 +1,46 @@ +package nl.grauw.glass.instructions; + +import java.io.IOException; +import java.io.OutputStream; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Org extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Org_N.ARGUMENTS.check(arguments)) + return new Org_N(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Org_N extends Empty.EmptyObject { + + public static Schema ARGUMENTS = new Schema(Schema.INTEGER); + + private Expression argument; + + public Org_N(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + public int getAddress() { + return argument.getAddress(); + } + + @Override + public int resolve(int address) { + super.resolve(address); + return getAddress(); + } + + @Override + public void generateObjectCode(OutputStream output) throws IOException { + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Otdr.java b/src/nl/grauw/glass/instructions/Otdr.java new file mode 100644 index 0000000..ddeab1e --- /dev/null +++ b/src/nl/grauw/glass/instructions/Otdr.java @@ -0,0 +1,37 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Otdr extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Otdr_.ARGUMENTS.check(arguments)) + return new Otdr_(context); + throw new ArgumentException(); + } + + public static class Otdr_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Otdr_(Scope context) { + super(context); + // TODO Auto-generated constructor stub + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xBB }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Otir.java b/src/nl/grauw/glass/instructions/Otir.java new file mode 100644 index 0000000..dbb405d --- /dev/null +++ b/src/nl/grauw/glass/instructions/Otir.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Otir extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Otir_.ARGUMENTS.check(arguments)) + return new Otir_(context); + throw new ArgumentException(); + } + + public static class Otir_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Otir_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xB3 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Out.java b/src/nl/grauw/glass/instructions/Out.java new file mode 100644 index 0000000..dc88f0c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Out.java @@ -0,0 +1,64 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Out extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Out_C_N.ARGUMENTS.check(arguments)) + return new Out_C_N(context, arguments.getElement(1)); + if (Out_N_N.ARGUMENTS.check(arguments)) + return new Out_N_N(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Out_C_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_C, Schema.DIRECT_R); + + private Expression argument; + + public Out_C_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)(0x41 | argument.getRegister().get8BitCode() << 3) }; + } + + } + + public static class Out_N_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.INDIRECT_N, Schema.DIRECT_A); + + private Expression argument; + + public Out_N_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xD3, (byte)argument.getInteger() }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Outd.java b/src/nl/grauw/glass/instructions/Outd.java new file mode 100644 index 0000000..146cb3a --- /dev/null +++ b/src/nl/grauw/glass/instructions/Outd.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Outd extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Outd_.ARGUMENTS.check(arguments)) + return new Outd_(context); + throw new ArgumentException(); + } + + public static class Outd_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Outd_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xAB }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Outi.java b/src/nl/grauw/glass/instructions/Outi.java new file mode 100644 index 0000000..05e1dc7 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Outi.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Outi extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Outi_.ARGUMENTS.check(arguments)) + return new Outi_(context); + throw new ArgumentException(); + } + + public static class Outi_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Outi_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0xA3 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Pop.java b/src/nl/grauw/glass/instructions/Pop.java new file mode 100644 index 0000000..047bb43 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Pop.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Pop extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Pop_RR.ARGUMENTS.check(arguments)) + return new Pop_RR(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Pop_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_RR_AF_INDEX); + + Expression argument; + + public Pop_RR(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyDirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyDirect(register, (byte)(0xC1 | register.get16BitCode() << 4)); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Proc.java b/src/nl/grauw/glass/instructions/Proc.java new file mode 100644 index 0000000..2a746b5 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Proc.java @@ -0,0 +1,40 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Proc extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + + private final Source source; + + public Proc(Source source) { + this.source = source; + } + + public List expand(Line line) { + Expression arguments = line.getArguments(); + if (!ARGUMENTS.check(arguments)) + throw new ArgumentException(); + + List lines = super.expand(line); + List lineCopies = source.getLineCopies(line.getScope()); + for (Line lineCopy : lineCopies) + lineCopy.register(line.getScope()); + for (Line lineCopy : lineCopies) + lines.addAll(lineCopy.expand()); + return lines; + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + return new Empty.EmptyObject(context); + } + +} diff --git a/src/nl/grauw/glass/instructions/Push.java b/src/nl/grauw/glass/instructions/Push.java new file mode 100644 index 0000000..7286bbf --- /dev/null +++ b/src/nl/grauw/glass/instructions/Push.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Push extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Push_RR.ARGUMENTS.check(arguments)) + return new Push_RR(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Push_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_RR_AF_INDEX); + + Expression argument; + + public Push_RR(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyDirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyDirect(register, (byte)(0xC5 | register.get16BitCode() << 4)); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rept.java b/src/nl/grauw/glass/instructions/Rept.java new file mode 100644 index 0000000..43de359 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rept.java @@ -0,0 +1,71 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Line; +import nl.grauw.glass.ParameterScope; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.IntegerLiteral; +import nl.grauw.glass.expressions.Schema; + +public class Rept extends InstructionFactory { + + public static Schema ARGUMENTS_N = new Schema(Schema.INTEGER); + public static Schema ARGUMENTS_N_ID = new Schema(Schema.INTEGER, Schema.IDENTIFIER); + public static Schema ARGUMENTS_N_ID_N = new Schema(Schema.INTEGER, Schema.IDENTIFIER, Schema.INTEGER); + public static Schema ARGUMENTS_N_ID_N_N = new Schema(Schema.INTEGER, Schema.IDENTIFIER, Schema.INTEGER, Schema.INTEGER); + + private final Source source; + + public Rept(Source source) { + this.source = source; + } + + public List expand(Line line) { + Expression arguments = line.getArguments(); + if (ARGUMENTS_N.check(arguments)) + return expand(line, arguments.getElement(0).getInteger(), null, 0, 1); + if (ARGUMENTS_N_ID.check(arguments)) + return expand(line, arguments.getElement(0).getInteger(), arguments.getElement(1), 0, 1); + if (ARGUMENTS_N_ID_N.check(arguments)) + return expand(line, arguments.getElement(0).getInteger(), arguments.getElement(1), + arguments.getElement(2).getInteger(), 1); + if (ARGUMENTS_N_ID_N_N.check(arguments)) + return expand(line, arguments.getElement(0).getInteger(), arguments.getElement(1), + arguments.getElement(2).getInteger(), arguments.getElement(3).getInteger()); + throw new ArgumentException(); + } + + public List expand(Line line, int count, Expression parameter, int start, int step) { + if (count < 0) + throw new ArgumentException("Repetition count must be 0 or greater."); + + List lines = super.expand(line); + for (int i = 0, counter = start; i < count; i++, counter += step) { + Scope parameterScope = new ParameterScope(line.getScope(), parameter, + parameter != null ? new IntegerLiteral(counter) : null); + + // set up the number symbol + line.getScope().addSymbol(Integer.toString(i), parameterScope); + Line rept = new Line(parameterScope, line); + rept.setInstruction(Empty.INSTANCE); + lines.add(rept); // so that the parameterScope address gets initialised + + // copy & expand content + List lineCopies = source.getLineCopies(parameterScope); + for (Line lineCopy : lineCopies) + lineCopy.register(parameterScope); + for (Line lineCopy : lineCopies) + lines.addAll(lineCopy.expand()); + } + return lines; + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + return new Empty.EmptyObject(context); + } + +} diff --git a/src/nl/grauw/glass/instructions/Res.java b/src/nl/grauw/glass/instructions/Res.java new file mode 100644 index 0000000..0f05ce8 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Res.java @@ -0,0 +1,46 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Res extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Res_N_R.ARGUMENTS.check(arguments)) + return new Res_N_R(context, arguments.getElement(0), arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Res_N_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N, Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument1; + private Expression argument2; + + public Res_N_R(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return indexifyIndirect(argument2.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + int value = argument1.getInteger(); + if (value < 0 || value > 7) + throw new ArgumentException(); + Register register = argument2.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x80 | value << 3 | register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Ret.java b/src/nl/grauw/glass/instructions/Ret.java new file mode 100644 index 0000000..2509cf7 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Ret.java @@ -0,0 +1,61 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Ret extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Ret_.ARGUMENTS.check(arguments)) + return new Ret_(context); + if (Ret_F.ARGUMENTS.check(arguments)) + return new Ret_F(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Ret_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Ret_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xC9 }; + } + + } + + public static class Ret_F extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(new Schema.IsFlag()); + + private Expression argument; + + public Ret_F(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)(0xC0 | argument.getFlag().getCode() << 3) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Reti.java b/src/nl/grauw/glass/instructions/Reti.java new file mode 100644 index 0000000..614322e --- /dev/null +++ b/src/nl/grauw/glass/instructions/Reti.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Reti extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Reti_.ARGUMENTS.check(arguments)) + return new Reti_(context); + throw new ArgumentException(); + } + + public static class Reti_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Reti_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0x4D }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Retn.java b/src/nl/grauw/glass/instructions/Retn.java new file mode 100644 index 0000000..fa52864 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Retn.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Retn extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Retn_.ARGUMENTS.check(arguments)) + return new Retn_(context); + throw new ArgumentException(); + } + + public static class Retn_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Retn_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0x45 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rl.java b/src/nl/grauw/glass/instructions/Rl.java new file mode 100644 index 0000000..f3b002d --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rl.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Rl extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rl_R.ARGUMENTS.check(arguments)) + return new Rl_R(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Rl_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Rl_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x10 + register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rla.java b/src/nl/grauw/glass/instructions/Rla.java new file mode 100644 index 0000000..e987fea --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rla.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Rla extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rla_.ARGUMENTS.check(arguments)) + return new Rla_(context); + throw new ArgumentException(); + } + + public static class Rla_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Rla_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x17 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rlc.java b/src/nl/grauw/glass/instructions/Rlc.java new file mode 100644 index 0000000..56ffee6 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rlc.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Rlc extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rlc_R.ARGUMENTS.check(arguments)) + return new Rlc_R(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Rlc_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Rlc_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x00 + register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rlca.java b/src/nl/grauw/glass/instructions/Rlca.java new file mode 100644 index 0000000..c048294 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rlca.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Rlca extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rlca_.ARGUMENTS.check(arguments)) + return new Rlca_(context); + throw new ArgumentException(); + } + + public static class Rlca_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Rlca_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x07 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rld.java b/src/nl/grauw/glass/instructions/Rld.java new file mode 100644 index 0000000..d7e4ac1 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rld.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Rld extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rld_.ARGUMENTS.check(arguments)) + return new Rld_(context); + throw new ArgumentException(); + } + + public static class Rld_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Rld_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0x6F }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rr.java b/src/nl/grauw/glass/instructions/Rr.java new file mode 100644 index 0000000..e5f259e --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rr.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Rr extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rr_R.ARGUMENTS.check(arguments)) + return new Rr_R(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Rr_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Rr_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x18 + register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rra.java b/src/nl/grauw/glass/instructions/Rra.java new file mode 100644 index 0000000..2a4ab00 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rra.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Rra extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rra_.ARGUMENTS.check(arguments)) + return new Rra_(context); + throw new ArgumentException(); + } + + public static class Rra_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Rra_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x1F }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rrc.java b/src/nl/grauw/glass/instructions/Rrc.java new file mode 100644 index 0000000..6c010c9 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rrc.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Rrc extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rrc_R.ARGUMENTS.check(arguments)) + return new Rrc_R(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Rrc_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Rrc_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x08 + register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rrca.java b/src/nl/grauw/glass/instructions/Rrca.java new file mode 100644 index 0000000..4c48a8e --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rrca.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Rrca extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rrca_.ARGUMENTS.check(arguments)) + return new Rrca_(context); + throw new ArgumentException(); + } + + public static class Rrca_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Rrca_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x0F }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rrd.java b/src/nl/grauw/glass/instructions/Rrd.java new file mode 100644 index 0000000..8053e6b --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rrd.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Rrd extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rrd_.ARGUMENTS.check(arguments)) + return new Rrd_(context); + throw new ArgumentException(); + } + + public static class Rrd_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Rrd_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)0x67 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Rst.java b/src/nl/grauw/glass/instructions/Rst.java new file mode 100644 index 0000000..4f44e34 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Rst.java @@ -0,0 +1,42 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Rst extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Rst_N.ARGUMENTS.check(arguments)) + return new Rst_N(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Rst_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Rst_N(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + int value = argument.getInteger(); + if (value < 0 || value > 0x38 || (value & 7) != 0) + throw new ArgumentException(); + return new byte[] { (byte)(0xC7 + value) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Sbc.java b/src/nl/grauw/glass/instructions/Sbc.java new file mode 100644 index 0000000..452604c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Sbc.java @@ -0,0 +1,91 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Sbc extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Sbc_A_R.ARGUMENTS.check(arguments)) + return new Sbc_A_R(context, arguments.getElement(1)); + if (Sbc_A_N.ARGUMENTS.check(arguments)) + return new Sbc_A_N(context, arguments.getElement(1)); + if (Sbc_HL_RR.ARGUMENTS.check(arguments)) + return new Sbc_HL_RR(context, arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Sbc_A_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Sbc_A_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0x98 | register.get8BitCode())); + } + + } + + public static class Sbc_A_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_A, Schema.DIRECT_N); + + private Expression argument; + + public Sbc_A_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xDE, (byte)argument.getInteger() }; + } + + } + + public static class Sbc_HL_RR extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_HL, Schema.DIRECT_RR); + + private Expression argument; + + public Sbc_HL_RR(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xED, (byte)(0x42 | argument.getRegister().get16BitCode() << 4) }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Scf.java b/src/nl/grauw/glass/instructions/Scf.java new file mode 100644 index 0000000..b961404 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Scf.java @@ -0,0 +1,36 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Scf extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Scf_.ARGUMENTS.check(arguments)) + return new Scf_(context); + throw new ArgumentException(); + } + + public static class Scf_ extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(); + + public Scf_(Scope context) { + super(context); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0x37 }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Section.java b/src/nl/grauw/glass/instructions/Section.java new file mode 100644 index 0000000..bf5c6ab --- /dev/null +++ b/src/nl/grauw/glass/instructions/Section.java @@ -0,0 +1,46 @@ +package nl.grauw.glass.instructions; + +import java.util.List; + +import nl.grauw.glass.Line; +import nl.grauw.glass.Scope; +import nl.grauw.glass.Source; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Section extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(Schema.IDENTIFIER); + + private final Source source; + + public Section(Source source) { + this.source = source; + } + + public Source getSource() { + return source; + } + + @Override + public List expand(Line line) { + if (!ARGUMENTS.check(line.getArguments())) + throw new ArgumentException(); + + if (!line.getArguments().isSectionContext()) + throw new ArgumentException("Argument does not reference a section context."); + + line.getArguments().getSectionContext().addSection(this); + + source.expand(); + return super.expand(line); + } + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments)) + return new Empty.EmptyObject(context); + throw new ArgumentException(); + } + +} diff --git a/src/nl/grauw/glass/instructions/Set.java b/src/nl/grauw/glass/instructions/Set.java new file mode 100644 index 0000000..ce907e7 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Set.java @@ -0,0 +1,46 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Set extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Set_N_R.ARGUMENTS.check(arguments)) + return new Set_N_R(context, arguments.getElement(0), arguments.getElement(1)); + throw new ArgumentException(); + } + + public static class Set_N_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N, Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument1; + private Expression argument2; + + public Set_N_R(Scope context, Expression argument1, Expression argument2) { + super(context); + this.argument1 = argument1; + this.argument2 = argument2; + } + + @Override + public int getSize() { + return indexifyIndirect(argument2.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + int value = argument1.getInteger(); + if (value < 0 || value > 7) + throw new ArgumentException(); + Register register = argument2.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0xC0 | value << 3 | register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Sla.java b/src/nl/grauw/glass/instructions/Sla.java new file mode 100644 index 0000000..0a2821c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Sla.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Sla extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Sla_R.ARGUMENTS.check(arguments)) + return new Sla_R(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Sla_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Sla_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x20 + register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Sra.java b/src/nl/grauw/glass/instructions/Sra.java new file mode 100644 index 0000000..94a652c --- /dev/null +++ b/src/nl/grauw/glass/instructions/Sra.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Sra extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Sra_R.ARGUMENTS.check(arguments)) + return new Sra_R(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Sra_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Sra_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x28 + register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Srl.java b/src/nl/grauw/glass/instructions/Srl.java new file mode 100644 index 0000000..78abc3e --- /dev/null +++ b/src/nl/grauw/glass/instructions/Srl.java @@ -0,0 +1,41 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Srl extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Srl_R.ARGUMENTS.check(arguments)) + return new Srl_R(context, arguments.getElement(0)); + throw new ArgumentException(); + } + + public static class Srl_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Srl_R(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 2); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyOnlyIndirect(register, (byte)0xCB, (byte)(0x38 + register.get8BitCode())); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Sub.java b/src/nl/grauw/glass/instructions/Sub.java new file mode 100644 index 0000000..d1dc183 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Sub.java @@ -0,0 +1,66 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Sub extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Sub_R.ARGUMENTS.check(arguments)) + return new Sub_R(context, arguments); + if (Sub_N.ARGUMENTS.check(arguments)) + return new Sub_N(context, arguments); + throw new ArgumentException(); + } + + public static class Sub_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Sub_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0x90 | register.get8BitCode())); + } + + } + + public static class Sub_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Sub_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xD6, (byte)argument.getInteger() }; + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Warning.java b/src/nl/grauw/glass/instructions/Warning.java new file mode 100644 index 0000000..27a99fe --- /dev/null +++ b/src/nl/grauw/glass/instructions/Warning.java @@ -0,0 +1,39 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Schema; + +public class Warning extends InstructionFactory { + + public static Schema ARGUMENTS = new Schema(); + public static Schema ARGUMENTS_S = new Schema(Schema.STRING); + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (ARGUMENTS.check(arguments) || ARGUMENTS_S.check(arguments)) + return new Warning_(context, arguments); + throw new ArgumentException(); + } + + public static class Warning_ extends Empty.EmptyObject { + + private final Expression argument; + + public Warning_(Scope context, Expression argument) { + super(context); + this.argument = argument; + } + + @Override + public byte[] getBytes() { + if (argument == null) + System.out.println("Warning: A warning directive was encountered."); + else + System.out.println("Warning: " + argument.getString()); + return super.getBytes(); + } + + } + +} diff --git a/src/nl/grauw/glass/instructions/Xor.java b/src/nl/grauw/glass/instructions/Xor.java new file mode 100644 index 0000000..d3751c7 --- /dev/null +++ b/src/nl/grauw/glass/instructions/Xor.java @@ -0,0 +1,66 @@ +package nl.grauw.glass.instructions; + +import nl.grauw.glass.Scope; +import nl.grauw.glass.expressions.Expression; +import nl.grauw.glass.expressions.Register; +import nl.grauw.glass.expressions.Schema; + +public class Xor extends InstructionFactory { + + @Override + public InstructionObject createObject(Scope context, Expression arguments) { + if (Xor_R.ARGUMENTS.check(arguments)) + return new Xor_R(context, arguments); + if (Xor_N.ARGUMENTS.check(arguments)) + return new Xor_N(context, arguments); + throw new ArgumentException(); + } + + public static class Xor_R extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_R_INDIRECT_HL_IX_IY); + + private Expression argument; + + public Xor_R(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return indexifyIndirect(argument.getRegister(), 1); + } + + @Override + public byte[] getBytes() { + Register register = argument.getRegister(); + return indexifyIndirect(register, (byte)(0xA8 | register.get8BitCode())); + } + + } + + public static class Xor_N extends InstructionObject { + + public static Schema ARGUMENTS = new Schema(Schema.DIRECT_N); + + private Expression argument; + + public Xor_N(Scope context, Expression arguments) { + super(context); + this.argument = arguments; + } + + @Override + public int getSize() { + return 2; + } + + @Override + public byte[] getBytes() { + return new byte[] { (byte)0xEE, (byte)argument.getInteger() }; + } + + } + +}