From 93c7706ad75ee299bb8cad3c2ed9f217aecf9fbe Mon Sep 17 00:00:00 2001 From: Moleus Date: Wed, 22 Jun 2022 17:30:39 +0300 Subject: [PATCH 1/2] Add new assembly mnemonics for direct-relative addressing mode. --- .../ru.ifmo.cs.bcomp.grammar/BCompNG.g4 | 1 + .../cs/bcomp/assembler/AddressingMode.java | 1 + .../ru/ifmo/cs/bcomp/assembler/AsmNg.java | 58 ++++++++++++++----- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/bcomp-assembler/src/main/antlr4/ru.ifmo.cs.bcomp.grammar/BCompNG.g4 b/bcomp-assembler/src/main/antlr4/ru.ifmo.cs.bcomp.grammar/BCompNG.g4 index a932e9a3..1bf98ac4 100644 --- a/bcomp-assembler/src/main/antlr4/ru.ifmo.cs.bcomp.grammar/BCompNG.g4 +++ b/bcomp-assembler/src/main/antlr4/ru.ifmo.cs.bcomp.grammar/BCompNG.g4 @@ -101,6 +101,7 @@ displacementSP directRelative : label + | '(' ip '+' number ')' ; directLoad diff --git a/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AddressingMode.java b/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AddressingMode.java index 2b0386be..cc6f960e 100644 --- a/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AddressingMode.java +++ b/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AddressingMode.java @@ -41,6 +41,7 @@ public String toString() { s="&undef"; break; case DIRECT_RELATIVE: if (reference != null ) {s = reference; break;} + if (number != MemoryWord.UNDEFINED ) {s = "(ip+" + number + ")"; break;} s="undef"; break; case DIRECT_LOAD: if (number != MemoryWord.UNDEFINED ) {s = "#"+number; break;} diff --git a/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AsmNg.java b/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AsmNg.java index 562d64c2..de3e7591 100644 --- a/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AsmNg.java +++ b/bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AsmNg.java @@ -598,7 +598,7 @@ private AddressingMode addressingModeByParserContext(OperandContext octx) { break; case BCompNGParser.RULE_directRelative: am.addressation = AddressingMode.AddressingType.DIRECT_RELATIVE; - am.reference = referenceByLabelContext(octx.directRelative().label()); + setNumberOrReferenceTo(am, octx.directRelative().number(), octx.directRelative().label()); break; case BCompNGParser.RULE_directLoad: am.addressation = AddressingMode.AddressingType.DIRECT_LOAD; @@ -611,6 +611,15 @@ private AddressingMode addressingModeByParserContext(OperandContext octx) { return am; } + private void setNumberOrReferenceTo(AddressingMode am, NumberContext number, LabelContext label) { + if (number != null) { + am.number = parseIntFromNumberContext(number, parser); + } + if (label != null) { + am.reference = referenceByLabelContext(label); + } + } + private String referenceByLabelContext(LabelContext lctx) { if (lctx == null) { AssemblerException ae = new AssemblerException("Internal error: LabelContex cant be null here",parser); @@ -633,7 +642,7 @@ private void compileOperand(InstructionWord iw) { if (iw.operand.reference != null) { Label l = labels.get(iw.operand.reference); if (l == null) { - reportError(new AssemblerException("Second pass: label refference "+iw.operand.reference+" not found",parser)); + reportError(new AssemblerException("Second pass: label reference "+iw.operand.reference+" not found", parser)); } else { num = l.address; @@ -655,25 +664,16 @@ private void compileOperand(InstructionWord iw) { iw.value = iw.instruction.opcode | 0x0B00 | convertReferenceToDisplacement(iw); break; case DISPLACEMENT_SP: - if (iw.operand.number != MemoryWord.UNDEFINED) { - num = iw.operand.number; - } else { - reportError(new AssemblerException("Second pass: number shoud present in command",parser)); - } - if (num > 127 || num < -128) { - reportError(new AssemblerException("Second pass: number exceed limits [-127..128]",parser)); - } + num = getOperandNumber(iw); + checkLimits(num, -128, 127); iw.value = iw.instruction.opcode | 0x0C00 | (num & 0xFF); break; case DIRECT_RELATIVE: - iw.value = iw.instruction.opcode | 0x0E00 | convertReferenceToDisplacement(iw); + num = getDisplacement(iw); + iw.value = iw.instruction.opcode | 0x0E00 | (num & 0xFF); break; case DIRECT_LOAD: - if (iw.operand.number != MemoryWord.UNDEFINED) { - num = iw.operand.number; - } else { - reportError(new AssemblerException("Second pass: number shoud present in command",parser)); - } + num = getOperandNumber(iw); if (num > 255 || num < -128) { //TODO error number exceed limit values throw new AssemblerException(parser); @@ -686,6 +686,32 @@ private void compileOperand(InstructionWord iw) { } } + /** + * Instruction's operand can be either a reference or a numeric displacement value. + */ + private int getDisplacement(InstructionWord iw) { + if (iw.operand.reference != null) { + return convertReferenceToDisplacement(iw); + } + int number = getOperandNumber(iw); + checkLimits(number, -128, 127); + return number; + } + + private void checkLimits(int number, int lowerBound, int upperBound) { + if (number > upperBound || number < lowerBound) { + reportError(new AssemblerException("Second pass: number exceed limits [-128..127]", parser)); + } + } + + private int getOperandNumber(InstructionWord iw) { + int value = iw.operand.number; + if (value == MemoryWord.UNDEFINED) { + reportError(new AssemblerException("Second pass: number should present in command", parser)); + } + return value; + } + private int convertReferenceToDisplacement(InstructionWord iw) { int num = MemoryWord.UNDEFINED; String reference = null; From fe0e7aaf2685271939f362f51ae8ed9185e6479a Mon Sep 17 00:00:00 2001 From: Moleus Date: Wed, 22 Jun 2022 17:30:59 +0300 Subject: [PATCH 2/2] Cover with tests conversions of assembly to instructions for all addressing modes. --- .../ru/ifmo/cs/bcomp/assembler/AsmNgTest.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 bcomp-assembler/src/test/java/ru/ifmo/cs/bcomp/assembler/AsmNgTest.java diff --git a/bcomp-assembler/src/test/java/ru/ifmo/cs/bcomp/assembler/AsmNgTest.java b/bcomp-assembler/src/test/java/ru/ifmo/cs/bcomp/assembler/AsmNgTest.java new file mode 100644 index 00000000..e9b67232 --- /dev/null +++ b/bcomp-assembler/src/test/java/ru/ifmo/cs/bcomp/assembler/AsmNgTest.java @@ -0,0 +1,108 @@ +package ru.ifmo.cs.bcomp.assembler; + +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +public class AsmNgTest { + @Test + public void directAbsoluteAddressingMode() { + validateConversion("JUMP 0x5", "C005"); + validateConversion("SAME: ADD $SAME", "4000"); + + assertHasErrors("LD -10"); + assertHasErrors("LD 0x800"); + assertHasErrors("LD $UNDEFINED"); + } + + @Test + public void indirectAddressingMode() { + validateConversion("SAME: ADD (SAME)", "48FF"); + + assertHasErrors("LD (10)"); + assertHasErrors("LD $UNDEFINED"); + } + + @Test + public void postIncrementAddressingMode() { + validateConversion("SAME: ADD (SAME)+", "4AFF"); + + assertHasErrors("LD (10)+"); + assertHasErrors("LD (UNDEFINED)+"); + } + + @Test + public void preDecrementAddressingMode() { + validateConversion("SAME: ADD -(SAME)", "4BFF"); + + assertHasErrors("LD -(10)"); + assertHasErrors("LD -(UNDEFINED)"); + } + + @Test + public void displacementSPAddressingMode() { + validateConversion("LD (Sp+-0x5)", "ACFB"); + validateConversion("LD &0xF", "AC0F"); + + assertHasErrors("LD (sp+128)"); + assertHasErrors("LD &UNDEFINED"); + } + + @Test + public void directRelativeAddressingMode() { + validateConversion("JUMP (ip+5)", "CE05"); + validateConversion("SAME: ADD SAME", "4EFF"); + validateConversion("LD (ip+-2)", "AEFE"); + + assertHasErrors("LD (ip+-129)"); + assertHasErrors("LD (ip+128)"); + assertHasErrors("LD UNDEFINED"); + } + + @Test + public void directLoadAddressingMode() { + validateConversion("LD #0x10", "AF10"); + validateConversion("LD #0xFF", "AFFF"); + + assertHasErrors("LD #256"); + assertHasErrors("LD #-129"); + assertHasErrors("LD #UNDEFINED"); + } + + private static void validateConversion(String assembly, String instruction) { + validateProgram(Collections.singletonMap(assembly, instruction)); + } + + private static void validateProgram(Map assemblyToInstruction) { + AsmNg sourceCode = getAssemblySource(assemblyToInstruction.keySet()); + Program program = sourceCode.compile(); + assertNotNull(program); + assertEquals("Errors occurred while compiling [" + assemblyToInstruction.keySet() + "]", Collections.EMPTY_LIST, sourceCode.getErrors()); + String[] correctInstructions = assemblyToInstruction.values().toArray(new String[]{}); + + for (int i = 0; i < program.binary.size(); i++) { + String actual = getHexInstruction(program.binary.get(i)); + String expected = correctInstructions[i]; + assertEquals(expected, actual); + } + } + + private static void assertHasErrors(String assembly) { + AsmNg sourceCode = getAssemblySource(Collections.singleton(assembly)); + sourceCode.compile(); + assertNotEquals("No errors occurred while compiling [" + assembly + "]", Collections.EMPTY_LIST, sourceCode.getErrors()); + } + + private static AsmNg getAssemblySource(Collection assembly) { + return new AsmNg(assembly.stream().collect(Collectors.joining("\n", "ORG 0\nSTART: \n", ""))); + } + + private static String getHexInstruction(int value) { + return Integer.toHexString(value).toUpperCase(); + } +} \ No newline at end of file