mirror of
https://github.com/holub/mame
synced 2025-05-25 15:25:33 +03:00
Added CPU core for the Manchester Small-Scale Experimental Machine (SSEM) from 1948 [MooglyGuy]
This commit is contained in:
parent
871aa59c7c
commit
7ef80f9393
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -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
|
||||
|
@ -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
320
src/emu/cpu/ssem/ssem.c
Normal 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
24
src/emu/cpu/ssem/ssem.h
Normal 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__ */
|
75
src/emu/cpu/ssem/ssemdasm.c
Normal file
75
src/emu/cpu/ssem/ssemdasm.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
#-------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user