Added CPU core for the Manchester Small-Scale Experimental Machine (SSEM) from 1948 [MooglyGuy]

This commit is contained in:
Ryan Holtz 2009-06-02 01:11:11 +00:00
parent 871aa59c7c
commit 7ef80f9393
6 changed files with 440 additions and 2 deletions

3
.gitattributes vendored
View File

@ -370,6 +370,9 @@ src/emu/cpu/spc700/spc700.c svneol=native#text/plain
src/emu/cpu/spc700/spc700.h svneol=native#text/plain
src/emu/cpu/spc700/spc700ds.c svneol=native#text/plain
src/emu/cpu/spc700/spc700ds.h svneol=native#text/plain
src/emu/cpu/ssem/ssem.c svneol=native#text/plain
src/emu/cpu/ssem/ssem.h svneol=native#text/plain
src/emu/cpu/ssem/ssemdasm.c svneol=native#text/plain
src/emu/cpu/ssp1601/ssp1601.c svneol=native#text/plain
src/emu/cpu/ssp1601/ssp1601.h svneol=native#text/plain
src/emu/cpu/ssp1601/ssp1601d.c svneol=native#text/plain

View File

@ -688,6 +688,21 @@ $(CPUOBJ)/lh5801/lh5801.o: $(CPUSRC)/lh5801/lh5801.c \
#-------------------------------------------------
# Manchester Small-Scale Experimental Machine
#-------------------------------------------------
ifneq ($(filter SSEM,$(CPUS)),)
OBJDIRS += $(CPUOBJ)/ssem
CPUOBJS += $(CPUOBJ)/ssem/ssem.o
DBGOBJS += $(CPUOBJ)/ssem/ssemdasm.o
endif
$(CPUOBJ)/ssem/ssem.o: $(CPUSRC)/ssem/ssem.c \
$(CPUSRC)/ssem/ssem.h
#-------------------------------------------------
# Fujitsu MB88xx
#-------------------------------------------------

320
src/emu/cpu/ssem/ssem.c Normal file
View File

@ -0,0 +1,320 @@
/*
Manchester Small-Scale Experimental Machine (SSEM) emulator
Written by MooglyGuy
*/
#include "cpuintrf.h"
#include "debugger.h"
#include "ssem.h"
#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;
const device_config *device;
const address_space *program;
int icount;
};
INLINE ssem_state *get_safe_token(const device_config *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 & 7)
#define ADDR ((op >> 3) & 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;
}
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(i));
ssem_dasm_one(string, i, opcode);
fprintf(dasm, "%02X: %08X %s\n", i, opcode, string);
}
fclose(dasm);
}
}
#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);
}
/*****************************************************************************/
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 CPU_INIT( ssem )
{
ssem_state *cpustate = get_safe_token(device);
cpustate->pc = 0;
cpustate->a = 0;
cpustate->halt = 0;
cpustate->device = device;
cpustate->program = memory_find_address_space(device, ADDRESS_SPACE_PROGRAM);
}
static CPU_EXIT( ssem )
{
}
static CPU_RESET( ssem )
{
ssem_state *cpustate = get_safe_token(device);
cpustate->pc = 0;
cpustate->a = 0;
cpustate->halt = 0;
}
static CPU_EXECUTE( ssem )
{
ssem_state *cpustate = get_safe_token(device);
UINT32 op;
cpustate->icount = cycles;
cpustate->pc &= 0x1fff;
while (cpustate->icount > 0)
{
debugger_instruction_hook(device, cpustate->pc);
op = READ32(cpustate, cpustate->pc);
if( !cpustate->halt )
{
cpustate->pc++;
}
else
{
op = 7;
}
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_DISASSEMBLE( ssem )
{
UINT32 op = (*(UINT8 *)(opram + 0) << 24) |
(*(UINT8 *)(opram + 1) << 16) |
(*(UINT8 *)(opram + 2) << 8) |
(*(UINT8 *)(opram + 3) << 0);
return ssem_dasm_one(buffer, pc, op);
}
/*****************************************************************************/
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 CPUINFO_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 CPUINFO_INT_DATABUS_WIDTH_PROGRAM: info->i = 8; break;
case CPUINFO_INT_ADDRBUS_WIDTH_PROGRAM: info->i = 16; break;
case CPUINFO_INT_ADDRBUS_SHIFT_PROGRAM: info->i = 0; break;
case CPUINFO_INT_DATABUS_WIDTH_DATA: info->i = 0; break;
case CPUINFO_INT_ADDRBUS_WIDTH_DATA: info->i = 0; break;
case CPUINFO_INT_ADDRBUS_SHIFT_DATA: info->i = 0; break;
case CPUINFO_INT_DATABUS_WIDTH_IO: info->i = 0; break;
case CPUINFO_INT_ADDRBUS_WIDTH_IO: info->i = 0; break;
case CPUINFO_INT_ADDRBUS_SHIFT_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 CPUINFO_STR_NAME: strcpy(info->s, "SSEM"); break;
case CPUINFO_STR_CORE_FAMILY: strcpy(info->s, "SSEM"); break;
case CPUINFO_STR_CORE_VERSION: strcpy(info->s, "1.0"); break;
case CPUINFO_STR_CORE_FILE: strcpy(info->s, __FILE__); break;
case CPUINFO_STR_CORE_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;
}
}

24
src/emu/cpu/ssem/ssem.h Normal file
View File

@ -0,0 +1,24 @@
/*
Manchester Small-Scale Experimental Machine (SSEM) emulator
Written by MooglyGuy
*/
#pragma once
#ifndef __SSEM_H__
#define __SSEM_H__
enum
{
SSEM_PC = 1,
SSEM_A,
SSEM_HALT,
};
CPU_GET_INFO( ssem );
#define CPU_SSEM CPU_GET_INFO_NAME( ssem )
extern offs_t ssem_dasm_one(char *buffer, offs_t pc, UINT32 op);
#endif /* __SSEM_H__ */

View File

@ -0,0 +1,75 @@
/*
Manchester Small-Scale Experimental Machine (SSEM) disassembler
Written by MooglyGuy
*/
#include "cpuintrf.h"
#include <stdarg.h>
static char *output;
static void ATTR_PRINTF(1,2) print(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
output += vsprintf(output, fmt, vl);
va_end(vl);
}
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;
}
offs_t ssem_dasm_one(char *buffer, offs_t pc, UINT32 op)
{
UINT8 instr = reverse(op) & 7;
UINT8 addr = (reverse(op) >> 3) & 0x1f;
output = buffer;
switch (instr)
{
case 0: // JMP S
print("JMP %d", addr);
break;
case 1: // JRP S
print("JRP %d", addr);
break;
case 2: // LDN S
print("LDN %d", addr);
break;
case 3: // STO S
print("STO %d", addr);
break;
case 4: // SUB S
case 5:
print("SUB %d", addr);
break;
case 6: // CMP
print("CMP");
break;
case 7: // STP
print("STP");
break;
default:
print("???");
break;
}
return 4 | DASMFLAG_SUPPORTED;
}

View File

@ -30,7 +30,8 @@ OBJDIRS += \
#-------------------------------------------------
# specify available CPU cores; some of these are
# only for MESS and so aren't included
# only for MESS, but are included so that they get
# updated with any MAME core changes
#-------------------------------------------------
CPUS += Z80
@ -109,7 +110,7 @@ CPUS += LR35902
CPUS += TMS7000
CPUS += SM8500
CPUS += MINX
CPUS += SSEM
#-------------------------------------------------