/* Manchester Small-Scale Experimental Machine (SSEM) emulator Written by MooglyGuy */ #include "emu.h" #include "debugger.h" #include "ssem.h" CPU_DISASSEMBLE( ssem ); #define SSEM_DISASM_ON_UNIMPL 0 #define SSEM_DUMP_MEM_ON_UNIMPL 0 typedef struct _ssem_state ssem_state; struct _ssem_state { UINT32 pc; UINT32 a; UINT32 halt; legacy_cpu_device *device; address_space *program; int icount; }; INLINE ssem_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == SSEM); return (ssem_state *)downcast(device)->token(); } #define INSTR ((op >> 13) & 7) #define ADDR (op & 0x1f) /*****************************************************************************/ // The SSEM stores its data, visually, with the leftmost bit corresponding to the least significant bit. // The de facto snapshot format for other SSEM simulators stores the data physically in that format as well. // Therefore, in MESS, every 32-bit word has its bits reversed, too, and as a result the values must be // un-reversed before being used. INLINE UINT32 reverse(UINT32 v) { // Taken from http://www-graphics.stanford.edu/~seander/bithacks.html#ReverseParallel // swap odd and even bits v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); // swap consecutive pairs v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); // swap nibbles ... v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); // swap bytes v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); // swap 2-byte long pairs v = ( v >> 16 ) | ( v << 16); return v; } INLINE UINT32 READ32(ssem_state *cpustate, UINT32 address) { UINT32 v = 0; // The MAME core does not have a good way of specifying a minimum datum size that is more than // 8 bits in width. The minimum datum width on the SSEM is 32 bits, so we need to quadruple // the address value to get the appropriate byte index. address <<= 2; v |= cpustate->program->read_byte(address + 0) << 24; v |= cpustate->program->read_byte(address + 1) << 16; v |= cpustate->program->read_byte(address + 2) << 8; v |= cpustate->program->read_byte(address + 3) << 0; return reverse(v); } INLINE void WRITE32(ssem_state *cpustate, UINT32 address, UINT32 data) { UINT32 v = reverse(data); // The MAME core does not have a good way of specifying a minimum datum size that is more than // 8 bits in width. The minimum datum width on the SSEM is 32 bits, so we need to quadruple // the address value to get the appropriate byte index. address <<= 2; cpustate->program->write_byte(address + 0, (v >> 24) & 0x000000ff); cpustate->program->write_byte(address + 1, (v >> 16) & 0x000000ff); cpustate->program->write_byte(address + 2, (v >> 8) & 0x000000ff); cpustate->program->write_byte(address + 3, (v >> 0) & 0x000000ff); return; } /*****************************************************************************/ static void unimplemented_opcode(ssem_state *cpustate, UINT32 op) { if((cpustate->device->machine().debug_flags & DEBUG_FLAG_ENABLED) != 0) { char string[200]; ssem_dasm_one(string, cpustate->pc-1, op); mame_printf_debug("%08X: %s\n", cpustate->pc-1, string); } #if SSEM_DISASM_ON_UNIMPL { char string[200] = { 0 }; UINT32 i = 0; FILE *disasm = fopen("ssemdasm.txt", "wt"); if(disasm) { for(i = 0; i < 0x20; i++) { UINT32 opcode = reverse(READ32(cpustate, i)); ssem_dasm_one(string, i, opcode); fprintf(disasm, "%02X: %08X %s\n", i, opcode, string); } fclose(disasm); } } #endif #if SSEM_DUMP_MEM_ON_UNIMPL { UINT32 i = 0; FILE *store = fopen("ssemmem.bin", "wb"); if(store) { for( i = 0; i < 0x80; i++ ) { fputc(cpustate->program->read_byte(i), store); } fclose(store); } } #endif fatalerror("SSEM: unknown opcode %d (%08X) at %d\n", reverse(op) & 7, reverse(op), cpustate->pc); } /*****************************************************************************/ static CPU_INIT( ssem ) { ssem_state *cpustate = get_safe_token(device); cpustate->pc = 1; cpustate->a = 0; cpustate->halt = 0; cpustate->device = device; cpustate->program = device->space(AS_PROGRAM); } static CPU_EXIT( ssem ) { } static CPU_RESET( ssem ) { ssem_state *cpustate = get_safe_token(device); cpustate->pc = 1; cpustate->a = 0; cpustate->halt = 0; } static CPU_EXECUTE( ssem ) { ssem_state *cpustate = get_safe_token(device); UINT32 op; cpustate->pc &= 0x1f; while (cpustate->icount > 0) { debugger_instruction_hook(device, cpustate->pc); op = READ32(cpustate, cpustate->pc); if( !cpustate->halt ) { cpustate->pc++; } else { op = 0x0000e000; } switch (INSTR) { case 0: // JMP: Move the value at the specified address into the Program Counter. cpustate->pc = READ32(cpustate, ADDR) + 1; break; case 1: // JRP: Add the value at the specified address to the Program Counter. cpustate->pc += (INT32)READ32(cpustate, ADDR); break; case 2: // LDN: Load the accumulator with the two's-complement negation of the value at the specified address. cpustate->a = (UINT32)(0 - (INT32)READ32(cpustate, ADDR)); break; case 3: // STO: Store the value in the accumulator at the specified address. WRITE32(cpustate, ADDR, cpustate->a); break; case 4: case 5: // SUB: Subtract the value at the specified address from the accumulator. cpustate->a -= READ32(cpustate, ADDR); break; case 6: // CMP: If the accumulator is less than zero, skip the next opcode. if((INT32)(cpustate->a) < 0) { cpustate->pc++; } break; case 7: // STP: Halt the computer. cpustate->halt = 1; break; default: // This is impossible, but it's better to be safe than sorry. unimplemented_opcode(cpustate, op); } --cpustate->icount; } } /*****************************************************************************/ static CPU_SET_INFO( ssem ) { ssem_state *cpustate = get_safe_token(device); switch (state) { /* --- the following bits of info are set as 64-bit signed integers --- */ case CPUINFO_INT_PC: case CPUINFO_INT_REGISTER + SSEM_PC: cpustate->pc = info->i; break; case CPUINFO_INT_REGISTER + SSEM_A: cpustate->a = info->i; break; case CPUINFO_INT_REGISTER + SSEM_HALT: cpustate->halt = info->i; break; } } CPU_GET_INFO( ssem ) { ssem_state *cpustate = (device != NULL && device->token() != NULL) ? get_safe_token(device) : NULL; switch(state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case CPUINFO_INT_CONTEXT_SIZE: info->i = sizeof(ssem_state); break; case CPUINFO_INT_INPUT_LINES: info->i = 0; break; case CPUINFO_INT_DEFAULT_IRQ_VECTOR: info->i = 0; break; case DEVINFO_INT_ENDIANNESS: info->i = ENDIANNESS_LITTLE; break; case CPUINFO_INT_CLOCK_MULTIPLIER: info->i = 1; break; case CPUINFO_INT_CLOCK_DIVIDER: info->i = 1; break; case CPUINFO_INT_MIN_INSTRUCTION_BYTES: info->i = 4; break; case CPUINFO_INT_MAX_INSTRUCTION_BYTES: info->i = 4; break; case CPUINFO_INT_MIN_CYCLES: info->i = 1; break; case CPUINFO_INT_MAX_CYCLES: info->i = 1; break; case DEVINFO_INT_DATABUS_WIDTH + AS_PROGRAM: info->i = 8; break; case DEVINFO_INT_ADDRBUS_WIDTH + AS_PROGRAM: info->i = 16; break; case DEVINFO_INT_ADDRBUS_SHIFT + AS_PROGRAM: info->i = 0; break; case DEVINFO_INT_DATABUS_WIDTH + AS_DATA: info->i = 0; break; case DEVINFO_INT_ADDRBUS_WIDTH + AS_DATA: info->i = 0; break; case DEVINFO_INT_ADDRBUS_SHIFT + AS_DATA: info->i = 0; break; case DEVINFO_INT_DATABUS_WIDTH + AS_IO: info->i = 0; break; case DEVINFO_INT_ADDRBUS_WIDTH + AS_IO: info->i = 0; break; case DEVINFO_INT_ADDRBUS_SHIFT + AS_IO: info->i = 0; break; case CPUINFO_INT_PC: /* intentional fallthrough */ case CPUINFO_INT_REGISTER + SSEM_PC: info->i = cpustate->pc << 2; break; case CPUINFO_INT_REGISTER + SSEM_A: info->i = cpustate->a; break; case CPUINFO_INT_REGISTER + SSEM_HALT: info->i = cpustate->halt; break; /* --- the following bits of info are returned as pointers to data or functions --- */ case CPUINFO_FCT_SET_INFO: info->setinfo = CPU_SET_INFO_NAME(ssem); break; case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(ssem); break; case CPUINFO_FCT_RESET: info->reset = CPU_RESET_NAME(ssem); break; case CPUINFO_FCT_EXIT: info->exit = CPU_EXIT_NAME(ssem); break; case CPUINFO_FCT_EXECUTE: info->execute = CPU_EXECUTE_NAME(ssem); break; case CPUINFO_FCT_BURN: info->burn = NULL; break; case CPUINFO_FCT_DISASSEMBLE: info->disassemble = CPU_DISASSEMBLE_NAME(ssem); break; case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &cpustate->icount; break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "SSEM"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "SSEM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; case CPUINFO_STR_FLAGS: strcpy(info->s, " "); break; case CPUINFO_STR_REGISTER + SSEM_PC: sprintf(info->s, "PC: %08X", cpustate->pc); break; case CPUINFO_STR_REGISTER + SSEM_A: sprintf(info->s, "A: %08X", cpustate->a); break; case CPUINFO_STR_REGISTER + SSEM_HALT: sprintf(info->s, "HALT: %d", cpustate->halt); break; } } DEFINE_LEGACY_CPU_DEVICE(SSEM, ssem);