summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/ssem/ssem.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/ssem/ssem.c')
-rw-r--r--trunk/src/emu/cpu/ssem/ssem.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/ssem/ssem.c b/trunk/src/emu/cpu/ssem/ssem.c
new file mode 100644
index 00000000000..22f18c12983
--- /dev/null
+++ b/trunk/src/emu/cpu/ssem/ssem.c
@@ -0,0 +1,309 @@
+/*
+ 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<legacy_cpu_device *>(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);