/*
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;
running_device *device;
const address_space *program;
int icount;
};
INLINE ssem_state *get_safe_token(running_device *device)
{
assert(device != NULL);
assert(device->token != NULL);
assert(device->type == CPU);
assert(cpu_get_type(device) == CPU_SSEM);
return (ssem_state *)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 |= memory_read_byte(cpustate->program, address + 0) << 24;
v |= memory_read_byte(cpustate->program, address + 1) << 16;
v |= memory_read_byte(cpustate->program, address + 2) << 8;
v |= memory_read_byte(cpustate->program, 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;
memory_write_byte(cpustate->program, address + 0, (v >> 24) & 0x000000ff);
memory_write_byte(cpustate->program, address + 1, (v >> 16) & 0x000000ff);
memory_write_byte(cpustate->program, address + 2, (v >> 8) & 0x000000ff);
memory_write_byte(cpustate->program, 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(memory_read_byte_32be(cpustate->program, 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->icount = cycles;
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;
}
return cycles - 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)
{