Added Glass Z80 compiler

master
Marco Maccaferri 2018-12-18 04:48:29 +01:00
rodzic 4404375e85
commit c2fe8d0527
175 zmienionych plików z 11673 dodań i 0 usunięć

22
LICENSE.glass 100644
Wyświetl plik

@ -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.

5
NOTICE.glass 100644
Wyświetl plik

@ -0,0 +1,5 @@
Glass Z80 assembler
Copyright 2013 Laurens Holst
This product includes software developed by
Laurens Holst <http://www.grauw.nl/>.

Wyświetl plik

@ -224,6 +224,8 @@
<copy todir="${work}/${folder}">
<fileset file="LICENSE"/>
<fileset file="LICENSE.glass"/>
<fileset file="NOTICE.glass"/>
</copy>
</target>

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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 {}
}
}

Wyświetl plik

@ -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;
}
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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 : "");
}
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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.");
}
}

Wyświetl plik

@ -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);
}
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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());
}
}

Wyświetl plik

@ -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));
}
}

Wyświetl plik

@ -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());
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -0,0 +1,5 @@
package nl.grauw.glass.directives;
public class Include extends Directive {
}

Wyświetl plik

@ -0,0 +1,5 @@
package nl.grauw.glass.directives;
public class Instruction extends Directive {
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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));
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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 "+";
}
}

Wyświetl plik

@ -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 "&";
}
}

Wyświetl plik

@ -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() + "}";
}
}

Wyświetl plik

@ -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() + "}";
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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 "~";
}
}

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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 "/";
}
}

Wyświetl plik

@ -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 "=";
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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";
}
}

Wyświetl plik

@ -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);
}
}
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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 ">=";
}
}

Wyświetl plik

@ -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 ">";
}
}

Wyświetl plik

@ -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() + ")";
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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() + "}";
}
}

Wyświetl plik

@ -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() + "]}";
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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 "<=";
}
}

Wyświetl plik

@ -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 "<";
}
}

Wyświetl plik

@ -0,0 +1,5 @@
package nl.grauw.glass.expressions;
public abstract class Literal extends Expression {
}

Wyświetl plik

@ -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 "&&";
}
}

Wyświetl plik

@ -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 "||";
}
}

Wyświetl plik

@ -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() + "}";
}
}

Wyświetl plik

@ -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 "%";
}
}

Wyświetl plik

@ -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 "*";
}
}

Wyświetl plik

@ -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 "-";
}
}

Wyświetl plik

@ -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 "!";
}
}

Wyświetl plik

@ -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 "!=";
}
}

Wyświetl plik

@ -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 "|";
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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 "+";
}
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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;
}
}
}

Wyświetl plik

@ -0,0 +1,5 @@
package nl.grauw.glass.expressions;
public interface SchemaType {
public boolean check(Expression argument);
}

Wyświetl plik

@ -0,0 +1,9 @@
package nl.grauw.glass.expressions;
import nl.grauw.glass.instructions.Section;
public interface SectionContext {
public void addSection(Section section);
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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() + "}";
}
}

Wyświetl plik

@ -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 "<<";
}
}

Wyświetl plik

@ -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 ">>";
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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 "-";
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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 "^";
}
}

Wyświetl plik

@ -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) };
}
}
}

Wyświetl plik

@ -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));
}
}
}

Wyświetl plik

@ -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() };
}
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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()));
}
}
}

Wyświetl plik

@ -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) };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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() };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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;
}
}
}

Wyświetl plik

@ -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;
}
}
}

Wyświetl plik

@ -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));
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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 };
}
}
}

Wyświetl plik

@ -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;
}
}
}

Wyświetl plik

@ -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