kopia lustrzana https://github.com/maccasoft/z80-tools
Added Glass Z80 compiler
rodzic
4404375e85
commit
c2fe8d0527
|
@ -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.
|
|
@ -0,0 +1,5 @@
|
||||||
|
Glass Z80 assembler
|
||||||
|
Copyright 2013 Laurens Holst
|
||||||
|
|
||||||
|
This product includes software developed by
|
||||||
|
Laurens Holst <http://www.grauw.nl/>.
|
|
@ -224,6 +224,8 @@
|
||||||
|
|
||||||
<copy todir="${work}/${folder}">
|
<copy todir="${work}/${folder}">
|
||||||
<fileset file="LICENSE"/>
|
<fileset file="LICENSE"/>
|
||||||
|
<fileset file="LICENSE.glass"/>
|
||||||
|
<fileset file="NOTICE.glass"/>
|
||||||
</copy>
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<File>());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<File> includePaths = new ArrayList<File>();
|
||||||
|
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<File> 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 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Context> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Line> 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 : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String> sourceLines = new ArrayList<String>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, Expression> 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<String, Expression> sortedMap = new TreeMap<>(symbols);
|
||||||
|
for (Map.Entry<String, Expression> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Line> lines = new ArrayList<Line>();
|
||||||
|
|
||||||
|
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<Line> getLines() {
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Line getLastLine() {
|
||||||
|
return lines.size() > 0 ? lines.get(lines.size() - 1) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Line> getLineCopies(Scope newParent) {
|
||||||
|
List<Line> 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<Line> addLines(List<Line> 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<Line> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String> END_TERMINATORS = Arrays.asList(new String[] { "end", "END" });
|
||||||
|
public static final List<String> ENDM_TERMINATORS = Arrays.asList(new String[] { "endm", "ENDM" });
|
||||||
|
public static final List<String> ENDP_TERMINATORS = Arrays.asList(new String[] { "endp", "ENDP" });
|
||||||
|
public static final List<String> ENDS_TERMINATORS = Arrays.asList(new String[] { "ends", "ENDS" });
|
||||||
|
public static final List<String> ELSE_TERMINATORS = Arrays.asList(new String[] { "else", "ELSE", "endif", "ENDIF" });
|
||||||
|
public static final List<String> ENDIF_TERMINATORS = Arrays.asList(new String[] { "endif", "ENDIF" });
|
||||||
|
|
||||||
|
private final Source source;
|
||||||
|
private final List<String> terminators;
|
||||||
|
private final List<File> includePaths;
|
||||||
|
private final Parser parser = new Parser();
|
||||||
|
|
||||||
|
private static final List<File> sourceFiles = new ArrayList<File>();
|
||||||
|
|
||||||
|
public SourceBuilder(List<File> includePaths) {
|
||||||
|
this.source = new Source();
|
||||||
|
this.terminators = END_TERMINATORS;
|
||||||
|
this.includePaths = includePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceBuilder(Source source, List<String> terminators, List<File> 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<String> terminators, LineNumberReader reader, File sourceFile) {
|
||||||
|
return new SourceBuilder(new Source(scope), terminators, includePaths).parse(reader, sourceFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<File> includePaths;
|
||||||
|
|
||||||
|
public Incbin(File sourceFile, List<File> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package nl.grauw.glass.directives;
|
||||||
|
|
||||||
|
public class Include extends Directive {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package nl.grauw.glass.directives;
|
||||||
|
|
||||||
|
public class Instruction extends Directive {
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "+";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "&";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "~";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "=";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Expression> getList() {
|
||||||
|
List<Expression> list = new ArrayList<>();
|
||||||
|
addToList(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addToList(List<Expression> 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Expression> operands = new ArrayDeque<Expression>();
|
||||||
|
private Deque<Operator> operators = new ArrayDeque<Operator>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 ">=";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() + "]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "<=";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "<";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package nl.grauw.glass.expressions;
|
||||||
|
|
||||||
|
public abstract class Literal extends Expression {
|
||||||
|
|
||||||
|
}
|
|
@ -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 "&&";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "||";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "*";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "!";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "!=";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "|";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "+";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package nl.grauw.glass.expressions;
|
||||||
|
|
||||||
|
public interface SchemaType {
|
||||||
|
public boolean check(Expression argument);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package nl.grauw.glass.expressions;
|
||||||
|
|
||||||
|
import nl.grauw.glass.instructions.Section;
|
||||||
|
|
||||||
|
public interface SectionContext {
|
||||||
|
|
||||||
|
public void addSection(Section section);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Expression> 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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "<<";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 ">>";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Expression> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 "^";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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) };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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) };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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() };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Expression> arguments;
|
||||||
|
|
||||||
|
public Db_N(Scope context, List<Expression> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Expression> arguments;
|
||||||
|
|
||||||
|
public Dd_N(Scope context, List<Expression> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Section> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Expression> arguments;
|
||||||
|
|
||||||
|
public Dw_N(Scope context, List<Expression> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue