package dcpu.tests; import static org.junit.Assert.*; import org.junit.Test; import dcpu.Dcpu; import dcpu.MemoryWatcher; import static dcpu.DcpuConstants.*; public class DCPUBasic { private static void assertRegval(Dcpu d, int reg, int val) { assertEquals((char)val, d.regs.gp[reg]); } private static void assertRegvalCyclecnt(Dcpu d, int reg, int val, int cyclecnt) { assertRegval(d, reg, val); assertEquals(cyclecnt, d.cyclecnt); } @Test public void sub() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, SHORT_LIT(9)), ASSEMBLE(SET, REG_B, SHORT_LIT(10)), ASSEMBLE(SUB, REG_A, REG_B) ); d.step(); assertRegvalCyclecnt(d, REG_A, 9, 1); d.step(); assertRegvalCyclecnt(d, REG_B, 10, 2); d.step(); assertRegvalCyclecnt(d, REG_A, 0xFFFF, 4); assertEquals((char)0xFFFF, d.regs.ex); } @Test public void add() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, SHORT_LIT(-1)), ASSEMBLE(SET, REG_B, SHORT_LIT(-1)), ASSEMBLE(ADD, REG_A, REG_B) ); d.step(); assertRegvalCyclecnt(d, REG_A, 0xFFFF, 1); d.step(); assertRegvalCyclecnt(d, REG_B, 0xFFFF, 2); d.step(); assertRegvalCyclecnt(d, REG_A, 0xFFFE, 4); assertEquals((char)0x1, d.regs.ex); } @Test public void mul() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, LONG_LIT), 0x1001, ASSEMBLE(SET, REG_B, LONG_LIT), 0x1000, ASSEMBLE(MUL, REG_A, REG_B) ); d.step(); assertRegvalCyclecnt(d, REG_A, 0x1001, 2); d.step(); assertRegvalCyclecnt(d, REG_B, 0x1000, 4); d.step(); assertRegvalCyclecnt(d, REG_A, 0x1000, 6); assertEquals((char)0x100, d.regs.ex); } @Test public void mli() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, LONG_LIT), 0xFFF0, ASSEMBLE(SET, REG_B, LONG_LIT), 0x10, ASSEMBLE(MLI, REG_A, REG_B) ); d.step(); assertRegvalCyclecnt(d, REG_A, 0xFFF0, 2); d.step(); assertRegvalCyclecnt(d, REG_B, 0x10, 4); d.step(); assertRegvalCyclecnt(d, REG_A, 0xFF00, 6); assertEquals((char)0xFFFF, d.regs.ex); } @Test public void div() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, LONG_LIT), 0xFFFF, ASSEMBLE(SET, REG_B, LONG_LIT), 0x10, ASSEMBLE(DIV, REG_A, REG_B) ); d.step(); assertRegvalCyclecnt(d, REG_A, 0xFFFF, 2); d.step(); assertRegvalCyclecnt(d, REG_B, 0x10, 4); d.step(); assertRegvalCyclecnt(d, REG_A, 0x0FFF, 7); assertEquals((char)0xF000, d.regs.ex); } @Test public void dvi() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, LONG_LIT), 0xFFFF, ASSEMBLE(SET, REG_B, LONG_LIT), 0x10, ASSEMBLE(DVI, REG_A, REG_B) ); d.step(); assertRegvalCyclecnt(d, REG_A, 0xFFFF, 2); d.step(); assertRegvalCyclecnt(d, REG_B, 0x10, 4); d.step(); assertRegvalCyclecnt(d, REG_A, 0x0000, 7); assertEquals((char)0xF000, d.regs.ex); } @Test public void subroutine() { Dcpu d = mkcpu( ASSEMBLE(ADV, JSR, SHORT_LIT(2)), ASSEMBLE(SUB, PC, SHORT_LIT(1)), ASSEMBLE(SET, REG_A, SHORT_LIT(2)), ASSEMBLE(SET, PC, POP) ); int[] cyclecnts = new int[] { 3, //jsr 1, //set a, 2 1, //set pc, pop 2, //sub pc, 1 }; for (int i=0; i<4; i++) { d.cyclecnt = 0; d.step(); assertEquals(d.cyclecnt, cyclecnts[i]); } assertRegval(d, REG_A, 2); } @Test public void stack_test() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_B, SHORT_LIT(3)), ASSEMBLE(SET, PUSH, REG_B), ASSEMBLE(SET, REG_B, SHORT_LIT(10)), ASSEMBLE(MOD, REG_B, POP) ); int[] cyclecnts = new int[] { 1, //set b, 3 1, //set push, b 1, //set b, 10 3, //mod b, pop }; for (int i=0; i<4; i++) { d.cyclecnt = 0; d.step(); assertEquals(d.cyclecnt, cyclecnts[i]); } assertRegval(d, REG_B, 1); } private void check_conditional(int conditional, int a, int b, boolean expect_true) { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, LONG_LIT), a, ASSEMBLE(SET, REG_B, LONG_LIT), b, ASSEMBLE(conditional, REG_A, REG_B), ASSEMBLE(SET, EX, LONG_LIT), 100, ASSEMBLE(SET, REG_C, SHORT_LIT(1)) ); d.step();//set a d.step();//set b d.step();//execute conditional assertEquals(!expect_true, d.ifFailed); d.cyclecnt = 0; d.step();//execute or step over set ex, 1 if (expect_true) { assertEquals(2, d.cyclecnt); } else { assertEquals(1, d.cyclecnt); } assertEquals(false, d.ifFailed); d.step();//always run set c, 1 assertRegval(d, REG_C, 1); } @Test public void branches() { check_conditional(IFB, 0x8000, 0xFFFF, true); check_conditional(IFB, 0x8000, 0x7FFF, false); check_conditional(IFC, 0x8000, 0xFFFF, false); check_conditional(IFC, 0x8000, 0x7FFF, true); check_conditional(IFE, 0x1, 0x1, true); check_conditional(IFE, 0x10, 0x20, false); check_conditional(IFN, 0x1, 0x1, false); check_conditional(IFN, 0x10, 0x20, true); check_conditional(IFG, 0x8000, 0x8000, false); check_conditional(IFG, 0x8000, 0x8001, false); check_conditional(IFG, 0x8000, 0x1, true); check_conditional(IFA, 0x8000, 0x8000, false); check_conditional(IFA, 0x8001, 0x8000, true); check_conditional(IFA, 0x8000, 0x1, false); check_conditional(IFL, 0x8000, 0x8000, false); check_conditional(IFL, 0x8000, 0x8001, true); check_conditional(IFL, 0x8000, 0x1, false); check_conditional(IFU, 0x8000, 0x8000, false); check_conditional(IFU, 0x8000, 0x8001, true); check_conditional(IFU, 0x8000, 0x1, true); } @Test public void conditional_fallthrough() { Dcpu d = mkcpu( ASSEMBLE(SET, REG_A, SHORT_LIT(4)), ASSEMBLE(SET, REG_B, SHORT_LIT(3)), ASSEMBLE(IFE, REG_A, REG_B), ASSEMBLE(IFG, REG_A, SHORT_LIT(0)), ASSEMBLE(ADV, JSR, LONG_LIT),0x100, ASSEMBLE(SET, REG_C, SHORT_LIT(10)) ); d.step();//set a, 4 d.step();//set b, 3 assertEquals(2, d.cyclecnt); d.step();//IFE, a, b assertEquals(true, d.ifFailed); assertEquals(4, d.cyclecnt); d.step();//skips over the IFG, taking one cycle assertEquals(5, d.cyclecnt); d.step();//skips over JSR, taking one cycle assertEquals(6, d.cyclecnt); d.step();//set c, 10 assertEquals(7, d.cyclecnt); assertEquals(10, d.regs.gp[REG_C]); } @Test public void noops() { Dcpu d = mkcpu( ASSEMBLE(ADV, ADVNOOP4, LONG_LIT), 100, ASSEMBLE(NOOP1, LONG_LIT, LONG_LIT), 100, 100, ASSEMBLE(NOOP2, REG_A, SHORT_LIT(10)), ASSEMBLE(NOOP1, LONG_LIT, REG_A),100, ASSEMBLE(SET, LONG_LIT, REG_C) ); d.memory.watchers.add(new MemoryWatcher() { public void memoryChanged(char start, char end) { fail("Noop changed memory"); } }); d.step(); d.step(); d.step(); d.step(); d.step(); } @Test public void operand_memory_access() { Dcpu d = mkcpu( ASSEMBLE(SET, SP, SHORT_LIT(1)),//0 ASSEMBLE(SET, REG_A, PICK), 11,//1 ASSEMBLE(SET, REG_B, INDIRECT(REG_A)),//3 ASSEMBLE(SET, REG_C, INDIRECT_OFFSET(REG_A)),2,//4 ASSEMBLE(SET, PICK, SHORT_LIT(1)), 11,//6 ASSEMBLE(SET, INDIRECT(REG_A), SHORT_LIT(2)),//8 ASSEMBLE(SET, INDIRECT_OFFSET(REG_A), SHORT_LIT(3)), 2,//10 10,//11 11,//12 12,//13 13 //14 ); d.step(); //set sp, 1 d.cyclecnt = 0; d.step(); //set a, pick 11 => set a, [1+11] => set a, [12] assertEquals(11, d.regs.gp[REG_A]); assertEquals(2, d.cyclecnt); d.cyclecnt = 0; d.step(); //set b, [a] => set b, [11] => set b, 10 assertEquals(10, d.regs.gp[REG_B]); assertEquals(1, d.cyclecnt); d.cyclecnt = 0; d.step(); //set c, [a+2] => set c, [11+2] => set c, 12 assertEquals(12, d.regs.gp[REG_C]); assertEquals(2, d.cyclecnt); d.cyclecnt = 0; d.step(); //set pick 11, 1 assertEquals((char)1, d.memory.get((char)(d.regs.sp + 11))); assertEquals(2, d.cyclecnt); d.cyclecnt = 0; d.step(); //set [a], 2 assertEquals((char)2, d.memory.get(d.regs.gp[REG_A])); assertEquals(1, d.cyclecnt); d.cyclecnt = 0; d.step(); //set [a+2], 3 assertEquals((char)3, d.memory.get((char)(d.regs.gp[REG_A]+2))); assertEquals(2, d.cyclecnt); } @Test public void literal_indirect() { Dcpu d = mkcpu( ASSEMBLE(ADD, LIT_IND, LIT_IND), 2, 1 ); d.step(); assertEquals(4, d.cyclecnt); assertEquals((char)3, d.memory.get((char)1)); } @Test public void peek() { Dcpu d = mkcpu( ASSEMBLE(SET, PUSH, SHORT_LIT(1)), ASSEMBLE(ADD, PEEK, PEEK) ); d.step(); assertEquals((char)1, d.memory.get(d.regs.sp)); assertEquals(1, d.cyclecnt); d.cyclecnt = 0; d.step(); assertEquals((char)2, d.memory.get(d.regs.sp)); assertEquals(2, d.cyclecnt); } private void math_test(int op, int ex_start, int a, int b, int a_result, int ex_end, int cycles) { Dcpu d = mkcpu( ASSEMBLE(op, REG_A, REG_B) ); d.regs.ex = (char)ex_start; d.regs.gp[REG_A] = (char)a; d.regs.gp[REG_B] = (char)b; d.step(); assertEquals((char)a_result, d.regs.gp[REG_A]); assertEquals((char)ex_end, d.regs.ex); assertEquals(cycles, d.cyclecnt); } @Test public void misc_math() { math_test(MOD, 0, 10, 0, 0, 0, 3); math_test(MDI, 0, -7, 16, -7, 0, 3); math_test(MDI, 0, -10, 0, 0, 0, 3); math_test(AND, 1, 0x5, 0xc, 0x4, 1, 1); math_test(BOR, 1, 0x5, 0xc, 0xD, 1, 1); math_test(XOR, 1, 0x5, 0xc, 0x9, 1, 1); math_test(SHR, 0, 0x1001, 4, 0x100, 0x1000, 1); math_test(SHL, 0, 0x1001, 4, 0x10, 0x1, 1); math_test(ASR, 0, 0x8000, 1, 0xC000, 0, 1); math_test(ASR, 0, 0x4001, 1, 0x2000, 0x8000, 1); math_test(ADX, 1, 0xFFFF, 1, 1, 1, 3); math_test(SBX, 1, 3, 5, 0xFFFF, 0xFFFF, 3); math_test(SBX, 1, 0xFFFF, 0, 0, 1, 3); } @Test public void sti_std() { Dcpu d = mkcpu( ASSEMBLE(STI, INDIRECT(REG_I), INDIRECT(REG_J)), ASSEMBLE(STD, INDIRECT(REG_I), INDIRECT(REG_J)) ); d.regs.gp[REG_I] = 0x100; d.regs.gp[REG_J] = 0x200; d.memory.set(d.regs.gp[REG_J], 'x'); d.memory.set((char)(d.regs.gp[REG_J]+1), 'y'); d.step(); assertEquals('x',d.memory.get((char)0x100)); assertEquals((char)0x101, d.regs.gp[REG_I]); assertEquals((char)0x201, d.regs.gp[REG_J]); assertEquals(2, d.cyclecnt); d.step(); assertEquals('y',d.memory.get((char)0x101)); assertEquals((char)0x100, d.regs.gp[REG_I]); assertEquals((char)0x200, d.regs.gp[REG_J]); assertEquals(4, d.cyclecnt); } @Test public void ind_add() { Dcpu d = mkcpu( ASSEMBLE(ADD, LIT_IND, LONG_LIT), 0xa120, 0x5 ); for (int i=0; i<3; i++) { System.out.printf("%04x ", (int)d.memory.get((char)i)); } d.memory.set((char)0x5, (char)0xa120); d.step(); assertEquals((char)(0xa120*2), d.memory.get((char)0x5)); } }