2acc0ac542
later. We still need to hunt down snippets used that are not. 1324 modified files... http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
1050 lines
25 KiB
C
1050 lines
25 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006-2007 Adam Gashlin (hcs)
|
|
* Copyright (C) 2004-2007 Shay Green (blargg)
|
|
* Copyright (C) 2002 Brad Martin
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* The CPU portion (shock!) */
|
|
#include "codec.h"
|
|
#include "codecs.h"
|
|
#include "spc_codec.h"
|
|
#include "spc_profiler.h"
|
|
|
|
#undef check
|
|
#define check assert
|
|
|
|
#define READ( addr ) (SPC_read( this, addr, spc_time_ ))
|
|
#define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ ))
|
|
|
|
#define READ_DP( addr ) READ( (addr) + dp )
|
|
#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value )
|
|
|
|
#define READ_PROG16( addr ) GET_LE16( RAM + (addr) )
|
|
|
|
#define READ_PC( pc ) (*(pc))
|
|
#define READ_PC16( pc ) GET_LE16( pc )
|
|
|
|
#define SET_PC( n ) (pc = RAM + (n))
|
|
#define GET_PC() (pc - RAM)
|
|
|
|
static unsigned char const cycle_table [0x100] = {
|
|
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 */
|
|
};
|
|
|
|
#define MEM_BIT() CPU_mem_bit( this, pc, spc_time_ )
|
|
|
|
static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
|
|
ICODE_ATTR;
|
|
|
|
static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
|
|
{
|
|
unsigned addr = READ_PC16( pc );
|
|
unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13);
|
|
return (t << 8) & 0x100;
|
|
}
|
|
|
|
/* status flags */
|
|
enum { st_n = 0x80 };
|
|
enum { st_v = 0x40 };
|
|
enum { st_p = 0x20 };
|
|
enum { st_b = 0x10 };
|
|
enum { st_h = 0x08 };
|
|
enum { st_i = 0x04 };
|
|
enum { st_z = 0x02 };
|
|
enum { st_c = 0x01 };
|
|
|
|
#define IS_NEG (nz & 0x880)
|
|
|
|
#define CALC_STATUS( out )\
|
|
{\
|
|
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;\
|
|
}
|
|
|
|
#define SET_STATUS( in )\
|
|
{\
|
|
status = in & ~(st_n | st_z | st_c | st_p);\
|
|
c = in << 8;\
|
|
nz = ((in << 4) & 0x800) | (~in & st_z);\
|
|
dp = (in << 3) & 0x100;\
|
|
}
|
|
|
|
|
|
/* stack */
|
|
#define PUSH( v ) (*--sp = (uint8_t) (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)
|
|
|
|
long CPU_run( THIS, long start_time )
|
|
{
|
|
#if 0
|
|
ENTER_TIMER(cpu);
|
|
#endif
|
|
|
|
register long spc_time_ = start_time;
|
|
|
|
#ifdef CPU_ARM
|
|
uint8_t* const ram_ = ram.ram;
|
|
#undef RAM
|
|
#define RAM ram_
|
|
#endif
|
|
|
|
int a = this->r.a;
|
|
int x = this->r.x;
|
|
int y = this->r.y;
|
|
|
|
uint8_t const* pc;
|
|
SET_PC( this->r.pc );
|
|
|
|
uint8_t* sp;
|
|
SET_SP( this->r.sp );
|
|
|
|
int status;
|
|
int c;
|
|
int nz;
|
|
unsigned dp;
|
|
{
|
|
int temp = this->r.status;
|
|
SET_STATUS( temp );
|
|
}
|
|
|
|
goto loop;
|
|
|
|
/* main loop */
|
|
cbranch_taken_loop:
|
|
pc += *(int8_t const*) pc;
|
|
spc_time_ += 2;
|
|
inc_pc_loop:
|
|
pc++;
|
|
loop:
|
|
check( (unsigned) GET_PC() < 0x10000 );
|
|
check( (unsigned) GET_SP() < 0x100 );
|
|
check( (unsigned) a < 0x100 );
|
|
check( (unsigned) x < 0x100 );
|
|
check( (unsigned) y < 0x100 );
|
|
|
|
unsigned opcode = *pc;
|
|
int cycles = this->cycle_table [opcode];
|
|
unsigned data = *++pc;
|
|
if ( (spc_time_ += cycles) > 0 )
|
|
goto out_of_time;
|
|
switch ( opcode )
|
|
{
|
|
|
|
/* Common instructions */
|
|
|
|
#define BRANCH( cond )\
|
|
{\
|
|
pc++;\
|
|
int offset = (int8_t) data;\
|
|
if ( cond ) {\
|
|
pc += offset;\
|
|
spc_time_ += 2;\
|
|
}\
|
|
goto loop;\
|
|
}
|
|
|
|
case 0xF0: /* BEQ (most common) */
|
|
BRANCH( !(uint8_t) nz )
|
|
|
|
case 0xD0: /* BNE */
|
|
BRANCH( (uint8_t) nz )
|
|
|
|
case 0x3F: /* CALL */
|
|
PUSH16( GET_PC() + 2 );
|
|
SET_PC( READ_PC16( pc ) );
|
|
goto loop;
|
|
|
|
case 0x6F: /* RET */
|
|
SET_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:\
|
|
data += 0x100 * READ_PC( ++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_PC16( 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_PC16( 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_PC16( 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 */
|
|
WRITE_DP( READ_PC( ++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*/\
|
|
addr = READ_PC( ++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 */
|
|
nz = READ_DP( READ_PC( ++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_PC16( 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_PC16( 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 */
|
|
addr = READ_PC( ++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 + (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_PC16( 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_PC16( 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_PC16( 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 += (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) */
|
|
SET_PC( READ_PC16( pc ) + x );
|
|
/* fall through */
|
|
case 0x5F: /* JMP abs */
|
|
SET_PC( READ_PC16( pc ) );
|
|
goto loop;
|
|
|
|
/* 13. SUB-ROUTINE CALL RETURN COMMANDS */
|
|
|
|
case 0x0F:{/* BRK */
|
|
check( 0 ); /* untested */
|
|
PUSH16( GET_PC() + 1 );
|
|
SET_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 */
|
|
PUSH16( GET_PC() + 1 );
|
|
SET_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( GET_PC() );
|
|
SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
|
|
goto loop;
|
|
|
|
/* 14. STACK OPERATION COMMANDS */
|
|
|
|
{
|
|
int temp;
|
|
case 0x7F: /* RET1 */
|
|
temp = POP();
|
|
SET_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_PC16( 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 += 2;
|
|
goto loop;
|
|
|
|
case 0x6A: /* AND1 C,/mem.bit */
|
|
check( 0 ); /* untested */
|
|
c &= ~MEM_BIT();
|
|
pc += 2;
|
|
goto loop;
|
|
|
|
case 0x0A: /* OR1 C,mem.bit */
|
|
check( 0 ); /* untested */
|
|
c |= MEM_BIT();
|
|
pc += 2;
|
|
goto loop;
|
|
|
|
case 0x2A: /* OR1 C,/mem.bit */
|
|
check( 0 ); /* untested */
|
|
c |= ~MEM_BIT();
|
|
pc += 2;
|
|
goto loop;
|
|
|
|
case 0x8A: /* EOR1 C,mem.bit */
|
|
c ^= MEM_BIT();
|
|
pc += 2;
|
|
goto loop;
|
|
|
|
case 0xEA: { /* NOT1 mem.bit */
|
|
data = READ_PC16( 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_PC16( 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 += 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( 0 ); /* untested */
|
|
status |= st_i;
|
|
goto loop;
|
|
|
|
case 0xC0: /* DI */
|
|
check( 0 ); /* untested */
|
|
status &= ~st_i;
|
|
goto loop;
|
|
|
|
/* 17. OTHER COMMANDS */
|
|
|
|
case 0x00: /* NOP */
|
|
goto loop;
|
|
|
|
/*case 0xEF:*/ /* SLEEP */
|
|
/*case 0xFF:*/ /* STOP */
|
|
case 0xFF:
|
|
c |= 1; /* force switch table to have 256 entries,
|
|
hopefully helping optimizer */
|
|
} /* switch */
|
|
|
|
/* unhandled instructions fall out of switch so emulator can catch them */
|
|
|
|
out_of_time:
|
|
/* undo partial execution of opcode */
|
|
spc_time_ -= this->cycle_table [*--pc];
|
|
{
|
|
int temp;
|
|
CALC_STATUS( temp );
|
|
this->r.status = (uint8_t) temp;
|
|
}
|
|
|
|
this->r.pc = GET_PC();
|
|
this->r.sp = (uint8_t) GET_SP();
|
|
this->r.a = (uint8_t) a;
|
|
this->r.x = (uint8_t) x;
|
|
this->r.y = (uint8_t) y;
|
|
|
|
#if 0
|
|
EXIT_TIMER(cpu);
|
|
#endif
|
|
return spc_time_;
|
|
}
|
|
|
|
void CPU_Init( THIS )
|
|
{
|
|
ci->memcpy( this->cycle_table, cycle_table, sizeof cycle_table );
|
|
}
|
|
|