public NTPUCPU(uint ram_size, ushort[] rom) { _memory = new ushort[ram_size]; _rom = rom; _registers = new ushort[8]; // PC stored seperately. _pc = 0x0000; _interrupt_enable = false; _pt_enable = false; _sys_curr_mode = NTPUMode.System; Halted = false; _active_interrupt = 0; _flag_neg = false; _flag_zero = false; _flag_carry = false; _flag_overflow = false; _last_reset_addr = 0; _interrupt_stack = 0; }
public void ExecuteInstruction() { var instr = ReadMemoryPaged(_pc); if (instr == 0x0000) { Halted = true; return; } byte opcode = (byte)(instr & 0xFF); byte upbyte = (byte)((instr & 0xFF00) >> 8); Console.WriteLine("Executing instr {0:X4}", instr); byte src = (byte)((instr & 0x700) >> 8); byte ctrl = (byte)((instr & 0x1800) >> 11); byte r7offs = (byte)((instr & 0xF000) >> 12); byte dest = (byte)((instr & 0xE000) >> 13); bool standard_encode_1 = false; bool standard_encode_2 = false; bool calc_flags = true; bool do_pc_increment = true; Console.WriteLine("Dest: {0:x1}, ctrl: {1:x1}, r7offs: {2:x1}, src: {3:x1}", dest, ctrl, r7offs, src); // Ew. handles if an instr should use the normal load/store setup. switch (opcode) { case byte n when(n <= 0x20 && ((n & 0x01) == 0)): standard_encode_1 = true; break; case byte n when(n <= 0x20 && ((n & 0x01) == 1)): standard_encode_2 = true; break; case 0xFC: // Jcc Rn case 0xFD: // Jcc imm case 0xFE: // rst vector case 0xFF: // hlt code // neither of the normal encodes. break; } ushort src_val; ushort dest_val; ushort dest_write = 0; ushort imm = 0; // Yes, globalizing this is nasty. But it removes // prevents a spurious memory read that shouldn't // occur if (standard_encode_1) { dest_val = _registers[dest]; if (ctrl == 0) { src_val = _registers[src]; } else if (ctrl == 2) { var addr = _registers[src]; src_val = ReadMemoryPaged(addr); } else { var addr = (ushort)(_registers[7] + (ushort)SextR7Offs(r7offs)); src_val = ReadMemoryPaged(addr); } } else if (standard_encode_2) { if (ctrl == 0) { // Immediate modes imm = ReadMemoryPaged((ushort)(_pc + 1)); _pc += 1; switch (src) { case 0: dest_val = _registers[dest]; src_val = imm; break; case 1: src_val = ReadMemoryPaged(imm); dest_val = _registers[dest]; break; case 2: src_val = _registers[dest]; // swap mode. dest_val = ReadMemoryPaged(imm); break; default: _active_interrupt = 1; // Invalid instr return; } } else { src_val = _registers[src]; if (ctrl == 1) { dest_val = ReadMemoryPaged(_registers[dest]); } else { var addr = (ushort)(_registers[7] + (ushort)SextR7Offs(r7offs)); dest_val = ReadMemoryPaged(addr); } } } else { src_val = 0; dest_val = 0; } switch (opcode) { case 0x00: case 0x01: calc_flags = false; dest_write = src_val; break; case 0x02: case 0x03: InstrAdd(src_val, dest_val, out dest_write); break; case 0x04: case 0x05: InstrSub(src_val, dest_val, out dest_write); break; case 0x06: case 0x07: InstrAnd(src_val, dest_val, out dest_write); break; case 0x08: case 0x09: InstrOr(src_val, dest_val, out dest_write); break; case 0x0A: case 0x0B: InstrXor(src_val, dest_val, out dest_write); break; case 0x0C: case 0x0D: InstrShl(src_val, dest_val, out dest_write); break; case 0x0E: case 0x0F: InstrShr(src_val, dest_val, out dest_write); break; case 0x14: case 0x15: InstrCmp(src_val, dest_val, out dest_write); break; case 0x16: case 0x17: InstrTst(src_val, dest_val, out dest_write); break; case 0xF8: do_pc_increment = false; calc_flags = false; InstrCall(dest); break; case 0xF9: do_pc_increment = false; calc_flags = false; InstrRet(dest); break; case 0xFA: case 0xFB: InstrLdpr(instr); calc_flags = false; break; case 0xFC: case 0xFD: do_pc_increment = false; InstrJCC(opcode, upbyte, src); calc_flags = false; break; case 0xFF: if (_sys_curr_mode == NTPUMode.User) { goto case 0xFE; //would be fallthrough, but C#. } calc_flags = false; Halted = true; do_pc_increment = false; _last_reset_addr = CalculateFullAddress(_pc, (_sys_curr_mode == NTPUMode.System) ? _sys_page_table_r : _user_page_table_r); _last_reset_addr_virt = _pc; break; case 0xFE: calc_flags = false; do_pc_increment = false; _last_reset_addr = CalculateFullAddress(_pc, (_sys_curr_mode == NTPUMode.System) ? _sys_page_table_r : _user_page_table_r); _last_reset_addr_virt = _pc; if (_sys_curr_mode == NTPUMode.User) { _sys_curr_mode = NTPUMode.System; _pc = _syscall_entry; // Go to wherever the system says to. } else { _pc = 0; _pt_enable = false; _interrupt_enable = false; } break; default: break; } if (standard_encode_1) { _registers[dest] = dest_write; } else if (standard_encode_2) { if (ctrl == 0) { switch (src) { case 0: // LD_1 case 1: _registers[dest] = dest_write; break; case 2: WriteMemoryPaged(imm, dest_write); break; } } else if (ctrl == 1) { WriteMemoryPaged(_registers[dest], dest_write); } else { var addr = (ushort)(_registers[7] + (ushort)SextR7Offs(r7offs)); WriteMemoryPaged(addr, dest_write); } } if (calc_flags) { _flag_zero |= dest_write == 0; _flag_neg |= dest_write >= 0x7FFF; } if (do_pc_increment) { _pc += 1; } if (_pc > 513) { Halted = true; } }