diff --git a/.gitattributes b/.gitattributes index 9effe104a83..4bec9c46305 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/src/emu/cpu/cpu.mak b/src/emu/cpu/cpu.mak index fba66648b41..6bdf57c966d 100644 --- a/src/emu/cpu/cpu.mak +++ b/src/emu/cpu/cpu.mak @@ -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 #------------------------------------------------- diff --git a/src/emu/cpu/ssem/ssem.c b/src/emu/cpu/ssem/ssem.c new file mode 100644 index 00000000000..20f283994b2 --- /dev/null +++ b/src/emu/cpu/ssem/ssem.c @@ -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; + } +} diff --git a/src/emu/cpu/ssem/ssem.h b/src/emu/cpu/ssem/ssem.h new file mode 100644 index 00000000000..1478e54dc67 --- /dev/null +++ b/src/emu/cpu/ssem/ssem.h @@ -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__ */ diff --git a/src/emu/cpu/ssem/ssemdasm.c b/src/emu/cpu/ssem/ssemdasm.c new file mode 100644 index 00000000000..5fdac1d2456 --- /dev/null +++ b/src/emu/cpu/ssem/ssemdasm.c @@ -0,0 +1,75 @@ +/* + Manchester Small-Scale Experimental Machine (SSEM) disassembler + + Written by MooglyGuy +*/ + +#include "cpuintrf.h" +#include + +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; +} diff --git a/src/mame/mame.mak b/src/mame/mame.mak index 31ba6cccabc..766673b1f4f 100644 --- a/src/mame/mame.mak +++ b/src/mame/mame.mak @@ -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 #-------------------------------------------------