diff src/Input/console/Spc_Cpu.cxx @ 0:13389e613d67 trunk

[svn] - initial import of audacious-plugins tree (lots to do)
author nenolod
date Mon, 18 Sep 2006 01:11:49 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Input/console/Spc_Cpu.cxx	Mon Sep 18 01:11:49 2006 -0700
@@ -0,0 +1,1066 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Spc_Cpu.h"
+
+#include <limits.h>
+
+#include "blargg_endian.h"
+#include "Snes_Spc.h"
+
+/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// Several instructions are commented out (or not even implemented). These aren't
+// used by the SPC files tested.
+
+// Optimize performance for the most common instructions, and size for the rest:
+//
+// 15%  0xF0    BEQ rel
+//  8%  0xE4    MOV A,dp
+//  4%  0xF5    MOV A,abs+X
+//  4%  0xD0    BNE rel
+//  4%  0x6F    RET
+//  4%  0x3F    CALL addr
+//  4%  0xF4    MOV A,dp+X
+//  3%  0xC4    MOV dp,A
+//  2%  0xEB    MOV Y,dp
+//  2%  0x3D    INC X
+//  2%  0xF6    MOV A,abs+Y
+// (1% and below not shown)
+
+Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e )
+{
+	remain_ = 0;
+	BOOST_STATIC_ASSERT( sizeof (int) >= 4 );
+}
+
+#define READ( addr )            (emu.read( addr ))
+#define WRITE( addr, value )    (emu.write( addr, value ))
+
+#define READ_DP( addr )         READ( (addr) + dp )
+#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value )
+
+#define READ_PROG( addr )       (ram [addr])
+#define READ_PROG16( addr )     GET_LE16( &READ_PROG( addr ) )
+
+int Spc_Cpu::read( spc_addr_t addr )
+{
+	return READ( addr );
+}
+
+void Spc_Cpu::write( spc_addr_t addr, int data )
+{
+	WRITE( addr, data );
+}
+
+// Cycle table derived from text copy of SPC-700 manual (using regular expressions)
+static const unsigned char cycle_table [0x100] = {
+//  0 1 2 3 4 5 6 7 8 9 A B C D E F
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4
+	2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7
+	2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9
+	3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B
+	3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C
+	2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D
+	2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E
+	2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3  // F
+};
+
+// The C,mem instructions are hardly used, so a non-inline function is used for
+// the common access code.
+unsigned Spc_Cpu::mem_bit( spc_addr_t pc )
+{
+	unsigned addr = READ_PROG16( pc );
+	unsigned t = READ( addr & 0x1fff ) >> (addr >> 13);
+	return (t << 8) & 0x100;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+spc_time_t Spc_Cpu::run( spc_time_t cycle_count )
+{
+	remain_ = cycle_count;
+	
+	uint8_t* const ram = this->ram; // cache
+	
+	// Stack pointer is kept one greater than usual SPC stack pointer to allow
+	// common pre-decrement and post-increment memory instructions that some
+	// processors have. Address wrap-around isn't supported.
+	#define PUSH( v )       (*--sp = (v))
+	#define PUSH16( v )     (sp -= 2, SET_LE16( sp, v ))
+	#define POP()           (*sp++)
+	#define SET_SP( v )     (sp = ram + 0x101 + (v))
+	#define GET_SP()        (sp - 0x101 - ram)
+
+	uint8_t* sp;
+	SET_SP( r.sp );
+	
+	// registers
+	unsigned pc = r.pc;
+	int a = r.a;
+	int x = r.x;
+	int y = r.y;
+	
+	// status flags
+	
+	const int st_n = 0x80;
+	const int st_v = 0x40;
+	const int st_p = 0x20;
+	const int st_b = 0x10;
+	const int st_h = 0x08;
+	const int st_i = 0x04;
+	const int st_z = 0x02;
+	const int st_c = 0x01;
+	
+	#define IS_NEG (nz & 0x880)
+	
+	#define CALC_STATUS( out ) do {                 \
+		out = status & ~(st_n | st_z | st_c);       \
+		out |= (c >> 8) & st_c;                     \
+		out |= (dp >> 3) & st_p;                    \
+		if ( IS_NEG ) out |= st_n;                  \
+		if ( !(nz & 0xFF) ) out |= st_z;            \
+	} while ( 0 )       
+
+	#define SET_STATUS( in ) do {                   \
+		status = in & ~(st_n | st_z | st_c | st_p); \
+		c = in << 8;                                \
+		nz = (in << 4) & 0x800;                     \
+		nz |= ~in & st_z;                           \
+		dp = (in << 3) & 0x100;                     \
+	} while ( 0 )
+	
+	uint8_t status;
+	int c;  // store C as 'c' & 0x100.
+	int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0
+	unsigned dp; // direct page base
+	{
+		int temp = r.status;
+		SET_STATUS( temp );
+	}
+
+	goto loop;
+	
+	unsigned data; // first operand of instruction and temporary across function calls
+	
+	// Common endings for instructions
+cbranch_taken_loop: // compare and branch
+	pc += (BOOST::int8_t) READ_PROG( pc );
+	remain_ -= 2;
+inc_pc_loop: // end of instruction with an operand
+	pc++;
+loop:
+	
+	check( (unsigned) pc < 0x10000 );
+	check( (unsigned) GET_SP() < 0x100 );
+	
+	assert( (unsigned) a < 0x100 );
+	assert( (unsigned) x < 0x100 );
+	assert( (unsigned) y < 0x100 );
+	
+	unsigned opcode = READ_PROG( pc );
+	pc++;
+	// to do: if pc is at end of memory, this will get wrong byte
+	data = READ_PROG( pc );
+	
+	if ( remain_ <= 0 )
+		goto stop;
+	
+	remain_ -= cycle_table [opcode];
+	
+	// Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise
+	// use a local temporary.
+	switch ( opcode )
+	{
+	
+	#define BRANCH( cond ) {        \
+		pc++;                       \
+		int offset = (BOOST::int8_t) data; \
+		if ( cond ) {               \
+			pc += offset;           \
+			remain_ -= 2;           \
+		}                           \
+		goto loop;                  \
+	}
+	
+// Most-Common
+
+	case 0xF0: // BEQ (most common)
+		BRANCH( !(uint8_t) nz )
+	
+	case 0xD0: // BNE
+		BRANCH( (uint8_t) nz )
+	
+	case 0x3F: // CALL
+		PUSH16( pc + 2 );
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+	case 0x6F: // RET
+		pc = POP();
+		pc += POP() * 0x100;
+		goto loop;
+
+#define CASE( n )   case n:
+
+// Define common address modes based on opcode for immediate mode. Execution
+// ends with data set to the address of the operand.
+#define ADDR_MODES( op )                \
+	CASE( op - 0x02 ) /* (X) */         \
+		data = x + dp;                  \
+		pc--;                           \
+		goto end_##op;                  \
+	CASE( op + 0x0F ) /* (dp)+Y */      \
+		data = READ_PROG16( data + dp ) + y;\
+		goto end_##op;                  \
+	CASE( op - 0x01 ) /* (dp+X) */      \
+		data = READ_PROG16( uint8_t (data + x) + dp );\
+		goto end_##op;                  \
+	CASE( op + 0x0E ) /* abs+Y */       \
+		data += y;                      \
+		goto abs_##op;                  \
+	CASE( op + 0x0D ) /* abs+X */       \
+		data += x;                      \
+	CASE( op - 0x03 ) /* abs */         \
+	abs_##op:                           \
+		pc++;                           \
+		data += 0x100 * READ_PROG( pc );\
+		goto end_##op;                  \
+	CASE( op + 0x0C ) /* dp+X */        \
+		data = uint8_t (data + x);      \
+	CASE( op - 0x04 ) /* dp */          \
+		data += dp;                     \
+	end_##op:
+
+// 1. 8-bit Data Transmission Commands. Group I
+
+	ADDR_MODES( 0xE8 ) // MOV A,addr
+	// case 0xE4: // MOV a,dp (most common)
+	mov_a_addr:
+		a = nz = READ( data );
+		goto inc_pc_loop;
+	case 0xBF: // MOV A,(X)+
+		data = x + dp;
+		x = uint8_t (x + 1);
+		pc--;
+		goto mov_a_addr;
+	
+	case 0xE8: // MOV A,imm
+		a = data;
+		nz = data;
+		goto inc_pc_loop;
+	
+	case 0xF9: // MOV X,dp+Y
+		data = uint8_t (data + y);
+	case 0xF8: // MOV X,dp
+		data += dp;
+		goto mov_x_addr;
+	case 0xE9: // MOV X,abs
+		data = READ_PROG16( pc );
+		pc++;
+	mov_x_addr:
+		data = READ( data );
+	case 0xCD: // MOV X,imm
+		x = data;
+		nz = data;
+		goto inc_pc_loop;
+	
+	case 0xFB: // MOV Y,dp+X
+		data = uint8_t (data + x);
+	case 0xEB: // MOV Y,dp
+		data += dp;
+		goto mov_y_addr;
+	case 0xEC: // MOV Y,abs
+		data = READ_PROG16( pc );
+		pc++;
+	mov_y_addr:
+		data = READ( data );
+	case 0x8D: // MOV Y,imm
+		y = data;
+		nz = data;
+		goto inc_pc_loop;
+
+// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2
+
+	ADDR_MODES( 0xC8 ) // MOV addr,A
+		WRITE( data, a );
+		goto inc_pc_loop;
+	
+	{
+		int temp;
+	case 0xCC: // MOV abs,Y
+		temp = y;
+		goto mov_abs_temp;
+	case 0xC9: // MOV abs,X
+		temp = x;
+	mov_abs_temp:
+		WRITE( READ_PROG16( pc ), temp );
+		pc += 2;
+		goto loop;
+	}
+	
+	case 0xD9: // MOV dp+Y,X
+		data = uint8_t (data + y);
+	case 0xD8: // MOV dp,X
+		WRITE( data + dp, x );
+		goto inc_pc_loop;
+	
+	case 0xDB: // MOV dp+X,Y
+		data = uint8_t (data + x);
+	case 0xCB: // MOV dp,Y
+		WRITE( data + dp, y );
+		goto inc_pc_loop;
+
+	case 0xFA: // MOV dp,dp
+		data = READ( data + dp );
+	case 0x8F: // MOV dp,#imm
+		pc++;
+		WRITE_DP( READ_PROG( pc ), data );
+		goto inc_pc_loop;
+	
+// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.
+	
+	case 0x7D: // MOV A,X
+		a = x;
+		nz = x;
+		goto loop;
+	
+	case 0xDD: // MOV A,Y
+		a = y;
+		nz = y;
+		goto loop;
+	
+	case 0x5D: // MOV X,A
+		x = a;
+		nz = a;
+		goto loop;
+	
+	case 0xFD: // MOV Y,A
+		y = a;
+		nz = a;
+		goto loop;
+	
+	case 0x9D: // MOV X,SP
+		x = nz = GET_SP();
+		goto loop;
+	
+	case 0xBD: // MOV SP,X
+		SET_SP( x );
+		goto loop;
+	
+	//case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)
+	
+	case 0xAF: // MOV (X)+,A
+		WRITE_DP( x, a );
+		x++;
+		goto loop;
+	
+// 5. 8-BIT LOGIC OPERATION COMMANDS
+	
+#define LOGICAL_OP( op, func )  \
+	ADDR_MODES( op ) /* addr */ \
+		data = READ( data );    \
+	case op: /* imm */          \
+		nz = a func##= data;    \
+		goto inc_pc_loop;       \
+	{   unsigned addr;          \
+	case op + 0x11: /* X,Y */   \
+		data = READ_DP( y );    \
+		addr = x + dp;          \
+		pc--;                   \
+		goto addr_##op;         \
+	case op + 0x01: /* dp,dp */ \
+		data = READ_DP( data ); \
+	case op + 0x10: /*dp,imm*/\
+		pc++;                   \
+		addr = READ_PROG( pc ) + dp;\
+	addr_##op:                  \
+		nz = data func READ( addr );\
+		WRITE( addr, nz );      \
+		goto inc_pc_loop;       \
+	}
+	
+	LOGICAL_OP( 0x28, & ); // AND
+	
+	LOGICAL_OP( 0x08, | ); // OR
+	
+	LOGICAL_OP( 0x48, ^ ); // EOR
+	
+// 4. 8-BIT ARITHMETIC OPERATION COMMANDS
+
+	ADDR_MODES( 0x68 ) // CMP addr
+		data = READ( data );
+	case 0x68: // CMP imm
+		nz = a - data;
+		c = ~nz;
+		nz &= 0xff;
+		goto inc_pc_loop;
+	
+	case 0x79: // CMP (X),(Y)
+		data = READ_DP( x );
+		nz = data - READ_DP( y );
+		c = ~nz;
+		nz &= 0xff;
+		goto loop;
+	
+	case 0x69: // CMP (dp),(dp)
+		data = READ_DP( data );
+	case 0x78: // CMP dp,imm
+		pc++;
+		nz = READ_DP( READ_PROG( pc ) ) - data;
+		c = ~nz;
+		nz &= 0xff;
+		goto inc_pc_loop;
+	
+	case 0x3E: // CMP X,dp
+		data += dp;
+		goto cmp_x_addr;
+	case 0x1E: // CMP X,abs
+		data = READ_PROG16( pc );
+		pc++;
+	cmp_x_addr:
+		data = READ( data );
+	case 0xC8: // CMP X,imm
+		nz = x - data;
+		c = ~nz;
+		nz &= 0xff;
+		goto inc_pc_loop;
+	
+	case 0x7E: // CMP Y,dp
+		data += dp;
+		goto cmp_y_addr;
+	case 0x5E: // CMP Y,abs
+		data = READ_PROG16( pc );
+		pc++;
+	cmp_y_addr:
+		data = READ( data );
+	case 0xAD: // CMP Y,imm
+		nz = y - data;
+		c = ~nz;
+		nz &= 0xff;
+		goto inc_pc_loop;
+	
+	{
+		int addr;
+	case 0xB9: // SBC (x),(y)
+	case 0x99: // ADC (x),(y)
+		pc--; // compensate for inc later
+		data = READ_DP( x );
+		addr = y + dp;
+		goto adc_addr;
+	case 0xA9: // SBC dp,dp
+	case 0x89: // ADC dp,dp
+		data = READ_DP( data );
+	case 0xB8: // SBC dp,imm
+	case 0x98: // ADC dp,imm
+		pc++;
+		addr = READ_PROG( pc ) + dp;
+	adc_addr:
+		nz = READ( addr );
+		goto adc_data;
+		
+// catch ADC and SBC together, then decode later based on operand
+#undef CASE
+#define CASE( n ) case n: case (n) + 0x20:
+	ADDR_MODES( 0x88 ) // ADC/SBC addr
+		data = READ( data );
+	case 0xA8: // SBC imm
+	case 0x88: // ADC imm
+		addr = -1; // A
+		nz = a;
+	adc_data: {
+		if ( opcode & 0x20 )
+			data ^= 0xff; // SBC
+		int carry = (c >> 8) & 1;
+		int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
+		int hc = (nz & 15) + carry;
+		c = nz += data + carry;
+		hc = (nz & 15) - hc;
+		status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h);
+		if ( addr < 0 ) {
+			a = (uint8_t) nz;
+			goto inc_pc_loop;
+		}
+		WRITE( addr, (uint8_t) nz );
+		goto inc_pc_loop;
+	}
+	
+	}
+	
+// 6. ADDITION & SUBTRACTION COMMANDS
+
+#define INC_DEC_REG( reg, n )   \
+		nz = reg + n;           \
+		reg = (uint8_t) nz;     \
+		goto loop;
+
+	case 0xBC: INC_DEC_REG( a, 1 )  // INC A
+	case 0x3D: INC_DEC_REG( x, 1 )  // INC X
+	case 0xFC: INC_DEC_REG( y, 1 )  // INC Y
+	
+	case 0x9C: INC_DEC_REG( a, -1 ) // DEC A
+	case 0x1D: INC_DEC_REG( x, -1 ) // DEC X
+	case 0xDC: INC_DEC_REG( y, -1 ) // DEC Y
+
+	case 0x9B: // DEC dp+X
+	case 0xBB: // INC dp+X
+		data = uint8_t (data + x);
+	case 0x8B: // DEC dp
+	case 0xAB: // INC dp
+		data += dp;
+		goto inc_abs;
+	case 0x8C: // DEC abs
+	case 0xAC: // INC abs
+		data = READ_PROG16( pc );
+		pc++;
+	inc_abs:
+		nz = ((opcode >> 4) & 2) - 1;
+		nz += READ( data );
+		WRITE( data, (uint8_t) nz );
+		goto inc_pc_loop;
+	
+// 7. SHIFT, ROTATION COMMANDS
+
+	case 0x5C: // LSR A
+		c = 0;
+	case 0x7C:{// ROR A
+		nz = ((c >> 1) & 0x80) | (a >> 1);
+		c = a << 8;
+		a = nz;
+		goto loop;
+	}
+	
+	case 0x1C: // ASL A
+		c = 0;
+	case 0x3C:{// ROL A
+		int temp = (c >> 8) & 1;
+		c = a << 1;
+		nz = c | temp;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+	case 0x0B: // ASL dp
+		c = 0;
+		data += dp;
+		goto rol_mem;
+	case 0x1B: // ASL dp+X
+		c = 0;
+	case 0x3B: // ROL dp+X
+		data = uint8_t (data + x);
+	case 0x2B: // ROL dp
+		data += dp;
+		goto rol_mem;
+	case 0x0C: // ASL abs
+		c = 0;
+	case 0x2C: // ROL abs
+		data = READ_PROG16( pc );
+		pc++;
+	rol_mem:
+		nz = (c >> 8) & 1;
+		nz |= (c = READ( data ) << 1);
+		WRITE( data, (uint8_t) nz );
+		goto inc_pc_loop;
+	
+	case 0x4B: // LSR dp
+		c = 0;
+		data += dp;
+		goto ror_mem;
+	case 0x5B: // LSR dp+X
+		c = 0;
+	case 0x7B: // ROR dp+X
+		data = uint8_t (data + x);
+	case 0x6B: // ROR dp
+		data += dp;
+		goto ror_mem;
+	case 0x4C: // LSR abs
+		c = 0;
+	case 0x6C: // ROR abs
+		data = READ_PROG16( pc );
+		pc++;
+	ror_mem: {
+		int temp = READ( data );
+		nz = ((c >> 1) & 0x80) | (temp >> 1);
+		c = temp << 8;
+		WRITE( data, nz );
+		goto inc_pc_loop;
+	}
+
+	case 0x9F: // XCN
+		nz = a = (a >> 4) | uint8_t (a << 4);
+		goto loop;
+
+// 8. 16-BIT TRANSMISION COMMANDS
+
+	case 0xBA: // MOVW YA,dp
+		a = READ_DP( data );
+		nz = (a & 0x7f) | (a >> 1);
+		y = READ_DP( uint8_t (data + 1) );
+		nz |= y;
+		goto inc_pc_loop;
+	
+	case 0xDA: // MOVW dp,YA
+		WRITE_DP( data, a );
+		WRITE_DP( uint8_t (data + 1), y );
+		goto inc_pc_loop;
+	
+// 9. 16-BIT OPERATION COMMANDS
+
+	case 0x3A: // INCW dp
+	case 0x1A:{// DECW dp
+		data += dp;
+		
+		// low byte
+		int temp = READ( data );
+		temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW
+		nz = ((temp >> 1) | temp) & 0x7f;
+		WRITE( data, (uint8_t) temp );
+		
+		// high byte
+		data = uint8_t (data + 1) + dp;
+		temp >>= 8;
+		temp = uint8_t (temp + READ( data ));
+		nz |= temp;
+		WRITE( data, temp );
+		
+		goto inc_pc_loop;
+	}
+		
+	case 0x9A: // SUBW YA,dp
+	case 0x7A: // ADDW YA,dp
+	{
+		// read 16-bit addend
+		int temp = READ_DP( data );
+		int sign = READ_DP( uint8_t (data + 1) );
+		temp += 0x100 * sign;
+		status &= ~(st_v | st_h);
+		
+		// to do: fix half-carry for SUBW (it's probably wrong)
+		
+		// for SUBW, negate and truncate to 16 bits
+		if ( opcode & 0x80 ) {
+			temp = (temp ^ 0xFFFF) + 1;
+			sign = temp >> 8;
+		}
+		
+		// add low byte (A)
+		temp += a;
+		a = (uint8_t) temp;
+		nz = (temp | (temp >> 1)) & 0x7f;
+		
+		// add high byte (Y)
+		temp >>= 8;
+		c = y + temp;
+		nz = (nz | c) & 0xff;
+		
+		// half-carry (temporary avoids CodeWarrior optimizer bug)
+		unsigned hc = (c & 15) - (y & 15);
+		status |= (hc >> 4) & st_h;
+		
+		// overflow if sign of YA changed when previous sign and addend sign were same
+		status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v;
+		
+		y = (uint8_t) c;
+		
+		goto inc_pc_loop;
+	}
+	
+	case 0x5A: { // CMPW YA,dp
+		int temp = a - READ_DP( data );
+		nz = ((temp >> 1) | temp) & 0x7f;
+		temp = y + (temp >> 8);
+		temp -= READ_DP( uint8_t (data + 1) );
+		nz |= temp;
+		c = ~temp;
+		nz &= 0xff;
+		goto inc_pc_loop;
+	}
+	
+// 10. MULTIPLICATION & DIVISON COMMANDS
+
+	case 0xCF: { // MUL YA
+		unsigned temp = y * a;
+		a = (uint8_t) temp;
+		nz = ((temp >> 1) | temp) & 0x7f;
+		y = temp >> 8;
+		nz |= y;
+		goto loop;
+	}
+	
+	case 0x9E: // DIV YA,X
+	{
+		// behavior based on SPC CPU tests
+		
+		status &= ~(st_h | st_v);
+		
+		if ( (y & 15) >= (x & 15) )
+			status |= st_h;
+		
+		if ( y >= x )
+			status |= st_v;
+		
+		unsigned ya = y * 0x100 + a;
+		if ( y < x * 2 )
+		{
+			a = ya / x;
+			y = ya - a * x;
+		}
+		else
+		{
+			a = 255 - (ya - x * 0x200) / (256 - x);
+			y = x   + (ya - x * 0x200) % (256 - x);
+		}
+		
+		nz = (uint8_t) a;
+		a = (uint8_t) a;
+		
+		goto loop;
+	}
+	
+// 11. DECIMAL COMPENSATION COMMANDS
+	
+	// seem unused
+	// case 0xDF: // DAA
+	// case 0xBE: // DAS
+	
+// 12. BRANCHING COMMANDS
+
+	case 0x2F: // BRA rel
+		pc += (BOOST::int8_t) data;
+		goto inc_pc_loop;
+	
+	case 0x30: // BMI
+		BRANCH( IS_NEG )
+	
+	case 0x10: // BPL
+		BRANCH( !IS_NEG )
+	
+	case 0xB0: // BCS
+		BRANCH( c & 0x100 )
+	
+	case 0x90: // BCC
+		BRANCH( !(c & 0x100) )
+	
+	case 0x70: // BVS
+		BRANCH( status & st_v )
+	
+	case 0x50: // BVC
+		BRANCH( !(status & st_v) )
+	
+	case 0x03: // BBS dp.bit,rel
+	case 0x23:
+	case 0x43:
+	case 0x63:
+	case 0x83:
+	case 0xA3:
+	case 0xC3:
+	case 0xE3:
+		pc++;
+		if ( (READ_DP( data ) >> (opcode >> 5)) & 1 )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	
+	case 0x13: // BBC dp.bit,rel
+	case 0x33:
+	case 0x53:
+	case 0x73:
+	case 0x93:
+	case 0xB3:
+	case 0xD3:
+	case 0xF3:
+		pc++;
+		if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	
+	case 0xDE: // CBNE dp+X,rel
+		data = uint8_t (data + x);
+		// fall through
+	case 0x2E: // CBNE dp,rel
+		pc++;
+		if ( READ_DP( data ) != a )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	
+	case 0xFE: // DBNZ Y,rel
+		y = uint8_t (y - 1);
+		BRANCH( y )
+	
+	case 0x6E: { // DBNZ dp,rel
+		pc++;
+		unsigned temp = READ_DP( data ) - 1;
+		WRITE_DP( (uint8_t) data, (uint8_t) temp );
+		if ( temp )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	}
+	
+	case 0x1F: // JMP (abs+X)
+		pc = READ_PROG16( pc ) + x;
+		// fall through
+	case 0x5F: // JMP abs
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+// 13. SUB-ROUTINE CALL RETURN COMMANDS
+	
+	case 0x0F: // BRK
+		check( false ); // untested
+		PUSH16( pc + 1 );
+		pc = READ_PROG16( 0xffde ); // vector address verified
+		int temp;
+		CALC_STATUS( temp );
+		PUSH( temp );
+		status = (status | st_b) & ~st_i;
+		goto loop;
+	
+	case 0x4F: // PCALL offset
+		pc++;
+		PUSH16( pc );
+		pc = 0xff00 + data;
+		goto loop;
+	
+	case 0x01: // TCALL n
+	case 0x11:
+	case 0x21:
+	case 0x31:
+	case 0x41:
+	case 0x51:
+	case 0x61:
+	case 0x71:
+	case 0x81:
+	case 0x91:
+	case 0xA1:
+	case 0xB1:
+	case 0xC1:
+	case 0xD1:
+	case 0xE1:
+	case 0xF1:
+		PUSH16( pc );
+		pc = READ_PROG16( 0xffde - (opcode >> 3) );
+		goto loop;
+	
+// 14. STACK OPERATION COMMANDS
+
+	{
+		int temp;
+	case 0x7F: // RET1
+		temp = POP();
+		pc = POP();
+		pc |= POP() << 8;
+		goto set_status;
+	case 0x8E: // POP PSW
+		temp = POP();
+	set_status:
+		SET_STATUS( temp );
+		goto loop;
+	}
+	
+	case 0x0D: { // PUSH PSW
+		int temp;
+		CALC_STATUS( temp );
+		PUSH( temp );
+		goto loop;
+	}
+
+	case 0x2D: // PUSH A
+		PUSH( a );
+		goto loop;
+	
+	case 0x4D: // PUSH X
+		PUSH( x );
+		goto loop;
+	
+	case 0x6D: // PUSH Y
+		PUSH( y );
+		goto loop;
+	
+	case 0xAE: // POP A
+		a = POP();
+		goto loop;
+	
+	case 0xCE: // POP X
+		x = POP();
+		goto loop;
+	
+	case 0xEE: // POP Y
+		y = POP();
+		goto loop;
+	
+// 15. BIT OPERATION COMMANDS
+
+	case 0x02: // SET1
+	case 0x22:
+	case 0x42:
+	case 0x62:
+	case 0x82:
+	case 0xA2:
+	case 0xC2:
+	case 0xE2:
+	case 0x12: // CLR1
+	case 0x32:
+	case 0x52:
+	case 0x72:
+	case 0x92:
+	case 0xB2:
+	case 0xD2:
+	case 0xF2: {
+		data += dp;
+		int bit = 1 << (opcode >> 5);
+		int mask = ~bit;
+		if ( opcode & 0x10 )
+			bit = 0;
+		WRITE( data, (READ( data ) & mask) | bit );
+		goto inc_pc_loop;
+	}
+		
+	case 0x0E: // TSET1 abs
+	case 0x4E:{// TCLR1 abs
+		data = READ_PROG16( pc );
+		pc += 2;
+		unsigned temp = READ( data );
+		nz = temp & a;
+		temp &= ~a;
+		if ( !(opcode & 0x40) )
+			temp |= a;
+		WRITE( data, temp );
+		goto loop;
+	}
+	
+	case 0x4A: // AND1 C,mem.bit
+		c &= mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x6A: // AND1 C,/mem.bit
+		check( false ); // untested
+		c &= ~mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x0A: // OR1 C,mem.bit
+		check( false ); // untested
+		c |= mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x2A: // OR1 C,/mem.bit
+		check( false ); // untested
+		c |= ~mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x8A: // EOR1 C,mem.bit
+		c ^= mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0xEA: { // NOT1 mem.bit
+		data = READ_PROG16( pc );
+		pc += 2;
+		unsigned temp = READ( data & 0x1fff );
+		temp ^= 1 << (data >> 13);
+		WRITE( data & 0x1fff, temp );
+		goto loop;
+	}
+	
+	case 0xCA: { // MOV1 mem.bit,C
+		data = READ_PROG16( pc );
+		pc += 2;
+		unsigned temp = READ( data & 0x1fff );
+		unsigned bit = data >> 13;
+		temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit);
+		WRITE( data & 0x1fff, temp );
+		goto loop;
+	}
+	
+	case 0xAA: // MOV1 C,mem.bit
+		c = mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+// 16. PROGRAM STATUS FLAG OPERATION COMMANDS
+
+	case 0x60: // CLRC
+		c = 0;
+		goto loop;
+		
+	case 0x80: // SETC
+		c = ~0;
+		goto loop;
+	
+	case 0xED: // NOTC
+		c ^= 0x100;
+		goto loop;
+		
+	case 0xE0: // CLRV
+		status &= ~(st_v | st_h);
+		goto loop;
+	
+	case 0x20: // CLRP
+		dp = 0;
+		goto loop;
+	
+	case 0x40: // SETP
+		dp = 0x100;
+		goto loop;
+	
+	case 0xA0: // EI
+		check( false ); // untested
+		status |= st_i;
+		goto loop;
+	
+	case 0xC0: // DI
+		check( false ); // untested
+		status &= ~st_i;
+		goto loop;
+	
+// 17. OTHER COMMANDS
+
+	case 0x00: // NOP
+		goto loop;
+	
+	//case 0xEF: // SLEEP
+	//case 0xFF: // STOP
+	
+	} // switch
+	
+	// unhandled instructions fall out of switch so emulator can catch them
+	
+stop:
+	pc--;
+	
+	{
+		int temp;
+		CALC_STATUS( temp );
+		r.status = temp;
+	}
+	
+	r.pc = pc;
+	r.sp = GET_SP();
+	r.a = a;
+	r.x = x;
+	r.y = y;
+	
+	return remain_;
+}
+