Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ displacementSP

directRelative
: label
| '(' ip '+' number ')'
;

directLoad
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;}
Expand Down
58 changes: 42 additions & 16 deletions bcomp-assembler/src/main/java/ru/ifmo/cs/bcomp/assembler/AsmNg.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String> 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();
}
}