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);
span class="w"> machine(),offset,MAKE_RGB(r,g,b) ); } /***************************************************************************/ WRITE8_MEMBER(thief_state::thief_color_plane_w){ /* --xx---- selects bitplane to read from (0..3) ----xxxx selects bitplane(s) to write to (0x0 = none, 0xf = all) */ m_write_mask = data&0xf; m_read_mask = (data>>4)&3; } READ8_MEMBER(thief_state::thief_videoram_r){ UINT8 *videoram = m_videoram; UINT8 *source = &videoram[offset]; if( m_video_control&0x02 ) source+=0x2000*4; /* foreground/background */ return source[m_read_mask*0x2000]; } WRITE8_MEMBER(thief_state::thief_videoram_w){ UINT8 *videoram = m_videoram; UINT8 *dest = &videoram[offset]; if( m_video_control&0x02 ) dest+=0x2000*4; /* foreground/background */ if( m_write_mask&0x1 ) dest[0x2000*0] = data; if( m_write_mask&0x2 ) dest[0x2000*1] = data; if( m_write_mask&0x4 ) dest[0x2000*2] = data; if( m_write_mask&0x8 ) dest[0x2000*3] = data; } /***************************************************************************/ VIDEO_START( thief ){ thief_state *state = machine.driver_data<thief_state>(); memset( &state->m_coprocessor, 0x00, sizeof(state->m_coprocessor) ); state->m_videoram = auto_alloc_array_clear(machine, UINT8, 0x2000*4*2 ); state->m_coprocessor.image_ram = auto_alloc_array(machine, UINT8, 0x2000 ); state->m_coprocessor.context_ram = auto_alloc_array(machine, UINT8, 0x400 ); } SCREEN_UPDATE_IND16( thief ){ thief_state *state = screen.machine().driver_data<thief_state>(); UINT8 *videoram = state->m_videoram; UINT32 offs; int flipscreen = state->m_video_control&1; const UINT8 *source = videoram; if (tms9927_screen_reset(screen.machine().device("tms"))) { bitmap.fill(get_black_pen(screen.machine()), cliprect); return 0; } if( state->m_video_control&4 ) /* visible page */ source += 0x2000*4; for( offs=0; offs<0x2000; offs++ ){ int ypos = offs/32; int xpos = (offs%32)*8; int plane0 = source[0x2000*0+offs]; int plane1 = source[0x2000*1+offs]; int plane2 = source[0x2000*2+offs]; int plane3 = source[0x2000*3+offs]; int bit; if( flipscreen ){ for( bit=0; bit<8; bit++ ){ bitmap.pix16(0xff - ypos, 0xff - (xpos+bit)) = (((plane0<<bit)&0x80)>>7) | (((plane1<<bit)&0x80)>>6) | (((plane2<<bit)&0x80)>>5) | (((plane3<<bit)&0x80)>>4); } } else { for( bit=0; bit<8; bit++ ){ bitmap.pix16(ypos, xpos+bit) = (((plane0<<bit)&0x80)>>7) | (((plane1<<bit)&0x80)>>6) | (((plane2<<bit)&0x80)>>5) | (((plane3<<bit)&0x80)>>4); } } } return 0; } /***************************************************************************/ static UINT16 fetch_image_addr( coprocessor_t &thief_coprocessor ){ int addr = thief_coprocessor.param[IMAGE_ADDR_LO]+256*thief_coprocessor.param[IMAGE_ADDR_HI]; /* auto-increment */ thief_coprocessor.param[IMAGE_ADDR_LO]++; if( thief_coprocessor.param[IMAGE_ADDR_LO]==0x00 ){ thief_coprocessor.param[IMAGE_ADDR_HI]++; } return addr; } WRITE8_MEMBER(thief_state::thief_blit_w){ coprocessor_t &thief_coprocessor = m_coprocessor; int i, offs, xoffset, dy; UINT8 *gfx_rom = memregion( "gfx1" )->base(); UINT8 x = thief_coprocessor.param[SCREEN_XPOS]; UINT8 y = thief_coprocessor.param[SCREEN_YPOS]; UINT8 width = thief_coprocessor.param[BLIT_WIDTH]; UINT8 height = thief_coprocessor.param[BLIT_HEIGHT]; UINT8 attributes = thief_coprocessor.param[BLIT_ATTRIBUTES]; UINT8 old_data; int xor_blit = data; /* making the xor behavior selectable fixes score display, but causes minor glitches on the playfield */ x -= width*8; xoffset = x&7; if( attributes&0x10 ){ y += 7-height; dy = 1; } else { dy = -1; } height++; while( height-- ){ for( i=0; i<=width; i++ ){ int addr = fetch_image_addr(thief_coprocessor); if( addr<0x2000 ){ data = thief_coprocessor.image_ram[addr]; } else { addr -= 0x2000; if( addr<0x2000*3 ) data = gfx_rom[addr]; } offs = (y*32+x/8+i)&0x1fff; old_data = thief_videoram_r(space,offs ); if( xor_blit ){ thief_videoram_w(space,offs, old_data^(data>>xoffset) ); } else { thief_videoram_w(space,offs, (old_data&(0xff00>>xoffset)) | (data>>xoffset) ); } offs = (offs+1)&0x1fff; old_data = thief_videoram_r(space,offs ); if( xor_blit ){ thief_videoram_w(space,offs, old_data^((data<<(8-xoffset))&0xff) ); } else { thief_videoram_w(space,offs, (old_data&(0xff>>xoffset)) | ((data<<(8-xoffset))&0xff) ); } } y+=dy; } } READ8_MEMBER(thief_state::thief_coprocessor_r){ coprocessor_t &thief_coprocessor = m_coprocessor; switch( offset ){ case SCREEN_XPOS: /* xpos */ case SCREEN_YPOS: /* ypos */ { /* XLAT: given (x,y) coordinate, return byte address in videoram */ int addr = thief_coprocessor.param[SCREEN_XPOS]+256*thief_coprocessor.param[SCREEN_YPOS]; int result = 0xc000 | (addr>>3); return (offset==0x03)?(result>>8):(result&0xff); } break; case GFX_PORT: { int addr = fetch_image_addr(thief_coprocessor); if( addr<0x2000 ){ return thief_coprocessor.image_ram[addr]; } else { UINT8 *gfx_rom = memregion( "gfx1" )->base(); addr -= 0x2000; if( addr<0x6000 ) return gfx_rom[addr]; } } break; case BARL_PORT: { /* return bitmask for addressed pixel */ int dx = thief_coprocessor.param[SCREEN_XPOS]&0x7; if( thief_coprocessor.param[BLIT_ATTRIBUTES]&0x01 ){ return 0x01<<dx; // flipx } else { return 0x80>>dx; // no flip } } break; } return thief_coprocessor.param[offset]; } WRITE8_MEMBER(thief_state::thief_coprocessor_w){ coprocessor_t &thief_coprocessor = m_coprocessor; switch( offset ){ case GFX_PORT: { int addr = fetch_image_addr(thief_coprocessor); if( addr<0x2000 ){ thief_coprocessor.image_ram[addr] = data; } } break; default: thief_coprocessor.param[offset] = data; break; } }