libx48ng/src/hp48_emulate.c
Gwenhael Le Moine 37fbf78e2a
sort includes
2023-05-10 16:44:03 +02:00

2436 lines
100 KiB
C

#include <stdio.h>
#include <unistd.h>
#include "hp48.h"
#include "hp48emu.h"
#include "timer.h"
#include "x48.h"
#include "x48_debugger.h"
extern int throttle;
static long jumpaddr;
unsigned long instructions = 0;
unsigned long old_instr = 0;
int rece_instr = 0;
int device_check = 0;
int adj_time_pending = 0;
int set_t1;
long schedule_event = 0;
#define SrvcIoStart 0x3c0
#define SrvcIoEnd 0x5ec
#define SCHED_INSTR_ROLLOVER 0x3fffffff
#define SCHED_RECEIVE 0x7ff
#define SCHED_ADJTIME 0x1ffe
#define SCHED_TIMER1 0x1e00
#define SCHED_TIMER2 0xf
#define SCHED_STATISTICS 0x7ffff
#define SCHED_NEVER 0x7fffffff
#define NR_SAMPLES 10
long sched_instr_rollover = SCHED_INSTR_ROLLOVER;
long sched_receive = SCHED_RECEIVE;
long sched_adjtime = SCHED_ADJTIME;
long sched_timer1 = SCHED_TIMER1;
long sched_timer2 = SCHED_TIMER2;
long sched_statistics = SCHED_STATISTICS;
long sched_display = SCHED_NEVER;
unsigned long t1_i_per_tick;
unsigned long t2_i_per_tick;
unsigned long s_1;
unsigned long s_16;
unsigned long old_s_1;
unsigned long old_s_16;
unsigned long delta_t_1;
unsigned long delta_t_16;
unsigned long delta_i;
word_64 run;
static word_20 jumpmasks[] = { 0xffffffff, 0xfffffff0, 0xffffff00, 0xfffff000,
0xffff0000, 0xfff00000, 0xff000000, 0xf0000000 };
int decode_group_80( void ) {
int t, op3, op4, op5, op6;
unsigned char* REG;
long addr;
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* OUT=CS */
saturn.PC += 3;
copy_register( saturn.OUT, saturn.C, OUTS_FIELD );
/* check_out_register(); */
return 0;
case 1: /* OUT=C */
saturn.PC += 3;
copy_register( saturn.OUT, saturn.C, OUT_FIELD );
/* check_out_register(); */
return 0;
case 2: /* A=IN */
saturn.PC += 3;
do_in();
copy_register( saturn.A, saturn.IN, IN_FIELD );
return 0;
case 3: /* C=IN */
saturn.PC += 3;
do_in();
copy_register( saturn.C, saturn.IN, IN_FIELD );
return 0;
case 4: /* UNCNFG */
saturn.PC += 3;
do_unconfigure();
return 0;
case 5: /* CONFIG */
saturn.PC += 3;
do_configure();
return 0;
case 6: /* C=ID */
saturn.PC += 3;
return get_identification();
case 7: /* SHUTDN */
saturn.PC += 3;
do_shutdown();
return 0;
case 8:
op4 = read_nibble( saturn.PC + 3 );
switch ( op4 ) {
case 0: /* INTON */
saturn.PC += 4;
do_inton();
return 0;
case 1: /* RSI... */
op5 = read_nibble( saturn.PC + 4 );
saturn.PC += 5;
do_reset_interrupt_system();
return 0;
case 2: /* LA... */
op5 = read_nibble( saturn.PC + 4 );
load_constant( saturn.A, op5 + 1, saturn.PC + 5 );
saturn.PC += 6 + op5;
return 0;
case 3: /* BUSCB */
saturn.PC += 4;
return 0;
case 4: /* ABIT=0 */
op5 = read_nibble( saturn.PC + 4 );
saturn.PC += 5;
clear_register_bit( saturn.A, op5 );
return 0;
case 5: /* ABIT=1 */
op5 = read_nibble( saturn.PC + 4 );
saturn.PC += 5;
set_register_bit( saturn.A, op5 );
return 0;
case 8: /* CBIT=0 */
op5 = read_nibble( saturn.PC + 4 );
saturn.PC += 5;
clear_register_bit( saturn.C, op5 );
return 0;
case 9: /* CBIT=1 */
op5 = read_nibble( saturn.PC + 4 );
saturn.PC += 5;
set_register_bit( saturn.C, op5 );
return 0;
case 6: /* ?ABIT=0 */
case 7: /* ?ABIT=1 */
case 0xa: /* ?CBIT=0 */
case 0xb: /* ?CBIT=1 */
op5 = read_nibble( saturn.PC + 4 );
if ( op4 < 8 )
REG = saturn.A;
else
REG = saturn.C;
if ( op4 == 6 || op4 == 0xa )
t = 0;
else
t = 1;
saturn.CARRY =
( get_register_bit( REG, op5 ) == t ) ? 1 : 0;
if ( saturn.CARRY ) {
saturn.PC += 5;
op6 = read_nibbles( saturn.PC, 2 );
if ( op6 ) {
if ( op6 & 0x80 )
op6 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op6 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 7;
}
return 0;
case 0xc: /* PC=(A) */
addr = dat_to_addr( saturn.A );
jumpaddr = read_nibbles( addr, 5 );
saturn.PC = jumpaddr;
return 0;
case 0xd: /* BUSCD */
saturn.PC += 4;
return 0;
case 0xe: /* PC=(C) */
addr = dat_to_addr( saturn.C );
jumpaddr = read_nibbles( addr, 5 );
saturn.PC = jumpaddr;
return 0;
case 0xf: /* INTOFF */
saturn.PC += 4;
do_intoff();
return 0;
default:
return 1;
}
case 9: /* C+P+1 */
saturn.PC += 3;
add_p_plus_one( saturn.C );
return 0;
case 0xa: /* RESET */
saturn.PC += 3;
do_reset();
return 0;
case 0xb: /* BUSCC */
saturn.PC += 3;
return 0;
case 0xc: /* C=P n */
op4 = read_nibble( saturn.PC + 3 );
saturn.PC += 4;
set_register_nibble( saturn.C, op4, saturn.P );
return 0;
case 0xd: /* P=C n */
op4 = read_nibble( saturn.PC + 3 );
saturn.PC += 4;
saturn.P = get_register_nibble( saturn.C, op4 );
return 0;
case 0xe: /* SREQ? */
saturn.PC += 3;
saturn.C[ 0 ] = 0;
saturn.SR = 0;
return 0;
case 0xf: /* CPEX n */
op4 = read_nibble( saturn.PC + 3 );
saturn.PC += 4;
t = get_register_nibble( saturn.C, op4 );
set_register_nibble( saturn.C, op4, saturn.P );
saturn.P = t;
return 0;
default:
return 1;
}
}
int decode_group_1( void ) {
int op, op2, op3, op4;
op2 = read_nibble( saturn.PC + 1 );
switch ( op2 ) {
case 0:
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* saturn.R0=A */
saturn.PC += 3;
copy_register( saturn.R0, saturn.A, W_FIELD );
return 0;
case 1: /* saturn.R1=A */
case 5:
saturn.PC += 3;
copy_register( saturn.R1, saturn.A, W_FIELD );
return 0;
case 2: /* saturn.R2=A */
case 6:
saturn.PC += 3;
copy_register( saturn.R2, saturn.A, W_FIELD );
return 0;
case 3: /* saturn.R3=A */
case 7:
saturn.PC += 3;
copy_register( saturn.R3, saturn.A, W_FIELD );
return 0;
case 4: /* saturn.R4=A */
saturn.PC += 3;
copy_register( saturn.R4, saturn.A, W_FIELD );
return 0;
case 8: /* saturn.R0=C */
saturn.PC += 3;
copy_register( saturn.R0, saturn.C, W_FIELD );
return 0;
case 9: /* saturn.R1=C */
case 0xd:
saturn.PC += 3;
copy_register( saturn.R1, saturn.C, W_FIELD );
return 0;
case 0xa: /* saturn.R2=C */
case 0xe:
saturn.PC += 3;
copy_register( saturn.R2, saturn.C, W_FIELD );
return 0;
case 0xb: /* saturn.R3=C */
case 0xf:
saturn.PC += 3;
copy_register( saturn.R3, saturn.C, W_FIELD );
return 0;
case 0xc: /* saturn.R4=C */
saturn.PC += 3;
copy_register( saturn.R4, saturn.C, W_FIELD );
return 0;
default:
return 1;
}
case 1:
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* A=R0 */
saturn.PC += 3;
copy_register( saturn.A, saturn.R0, W_FIELD );
return 0;
case 1: /* A=R1 */
case 5:
saturn.PC += 3;
copy_register( saturn.A, saturn.R1, W_FIELD );
return 0;
case 2: /* A=R2 */
case 6:
saturn.PC += 3;
copy_register( saturn.A, saturn.R2, W_FIELD );
return 0;
case 3: /* A=R3 */
case 7:
saturn.PC += 3;
copy_register( saturn.A, saturn.R3, W_FIELD );
return 0;
case 4: /* A=R4 */
saturn.PC += 3;
copy_register( saturn.A, saturn.R4, W_FIELD );
return 0;
case 8: /* C=R0 */
saturn.PC += 3;
copy_register( saturn.C, saturn.R0, W_FIELD );
return 0;
case 9: /* C=R1 */
case 0xd:
saturn.PC += 3;
copy_register( saturn.C, saturn.R1, W_FIELD );
return 0;
case 0xa: /* C=R2 */
case 0xe:
saturn.PC += 3;
copy_register( saturn.C, saturn.R2, W_FIELD );
return 0;
case 0xb: /* C=R3 */
case 0xf:
saturn.PC += 3;
copy_register( saturn.C, saturn.R3, W_FIELD );
return 0;
case 0xc: /* C=R4 */
saturn.PC += 3;
copy_register( saturn.C, saturn.R4, W_FIELD );
return 0;
default:
return 1;
}
case 2:
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* AR0EX */
saturn.PC += 3;
exchange_register( saturn.A, saturn.R0, W_FIELD );
return 0;
case 1: /* AR1EX */
case 5:
saturn.PC += 3;
exchange_register( saturn.A, saturn.R1, W_FIELD );
return 0;
case 2: /* AR2EX */
case 6:
saturn.PC += 3;
exchange_register( saturn.A, saturn.R2, W_FIELD );
return 0;
case 3: /* AR3EX */
case 7:
saturn.PC += 3;
exchange_register( saturn.A, saturn.R3, W_FIELD );
return 0;
case 4: /* AR4EX */
saturn.PC += 3;
exchange_register( saturn.A, saturn.R4, W_FIELD );
return 0;
case 8: /* CR0EX */
saturn.PC += 3;
exchange_register( saturn.C, saturn.R0, W_FIELD );
return 0;
case 9: /* CR1EX */
case 0xd:
saturn.PC += 3;
exchange_register( saturn.C, saturn.R1, W_FIELD );
return 0;
case 0xa: /* CR2EX */
case 0xe:
saturn.PC += 3;
exchange_register( saturn.C, saturn.R2, W_FIELD );
return 0;
case 0xb: /* CR3EX */
case 0xf:
saturn.PC += 3;
exchange_register( saturn.C, saturn.R3, W_FIELD );
return 0;
case 0xc: /* CR4EX */
saturn.PC += 3;
exchange_register( saturn.C, saturn.R4, W_FIELD );
return 0;
default:
return 1;
}
case 3:
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* D0=A */
saturn.PC += 3;
register_to_address( saturn.A, &saturn.D0, 0 );
return 0;
case 1: /* D1=A */
saturn.PC += 3;
register_to_address( saturn.A, &saturn.D1, 0 );
return 0;
case 2: /* AD0EX */
saturn.PC += 3;
exchange_reg( saturn.A, &saturn.D0, A_FIELD );
return 0;
case 3: /* AD1EX */
saturn.PC += 3;
exchange_reg( saturn.A, &saturn.D1, A_FIELD );
return 0;
case 4: /* D0=C */
saturn.PC += 3;
register_to_address( saturn.C, &saturn.D0, 0 );
return 0;
case 5: /* D1=C */
saturn.PC += 3;
register_to_address( saturn.C, &saturn.D1, 0 );
return 0;
case 6: /* CD0EX */
saturn.PC += 3;
exchange_reg( saturn.C, &saturn.D0, A_FIELD );
return 0;
case 7: /* CD1EX */
saturn.PC += 3;
exchange_reg( saturn.C, &saturn.D1, A_FIELD );
return 0;
case 8: /* D0=AS */
saturn.PC += 3;
register_to_address( saturn.A, &saturn.D0, 1 );
return 0;
case 9: /* saturn.D1=AS */
saturn.PC += 3;
register_to_address( saturn.A, &saturn.D1, 1 );
return 0;
case 0xa: /* AD0XS */
saturn.PC += 3;
exchange_reg( saturn.A, &saturn.D0, IN_FIELD );
return 0;
case 0xb: /* AD1XS */
saturn.PC += 3;
exchange_reg( saturn.A, &saturn.D1, IN_FIELD );
return 0;
case 0xc: /* D0=CS */
saturn.PC += 3;
register_to_address( saturn.C, &saturn.D0, 1 );
return 0;
case 0xd: /* D1=CS */
saturn.PC += 3;
register_to_address( saturn.C, &saturn.D1, 1 );
return 0;
case 0xe: /* CD0XS */
saturn.PC += 3;
exchange_reg( saturn.C, &saturn.D0, IN_FIELD );
return 0;
case 0xf: /* CD1XS */
saturn.PC += 3;
exchange_reg( saturn.C, &saturn.D1, IN_FIELD );
return 0;
default:
return 1;
}
case 4:
op3 = read_nibble( saturn.PC + 2 );
op = op3 < 8 ? 0xf : 6;
switch ( op3 & 7 ) {
case 0: /* DAT0=A */
saturn.PC += 3;
store( saturn.D0, saturn.A, op );
return 0;
case 1: /* DAT1=A */
saturn.PC += 3;
store( saturn.D1, saturn.A, op );
return 0;
case 2: /* A=DAT0 */
saturn.PC += 3;
recall( saturn.A, saturn.D0, op );
return 0;
case 3: /* A=DAT1 */
saturn.PC += 3;
recall( saturn.A, saturn.D1, op );
return 0;
case 4: /* DAT0=C */
saturn.PC += 3;
store( saturn.D0, saturn.C, op );
return 0;
case 5: /* DAT1=C */
saturn.PC += 3;
store( saturn.D1, saturn.C, op );
return 0;
case 6: /* C=DAT0 */
saturn.PC += 3;
recall( saturn.C, saturn.D0, op );
return 0;
case 7: /* C=DAT1 */
saturn.PC += 3;
recall( saturn.C, saturn.D1, op );
return 0;
default:
return 1;
}
case 5:
op3 = read_nibble( saturn.PC + 2 );
op4 = read_nibble( saturn.PC + 3 );
if ( op3 >= 8 ) {
switch ( op3 & 7 ) {
case 0: /* DAT0=A */
saturn.PC += 4;
store_n( saturn.D0, saturn.A, op4 + 1 );
return 0;
case 1: /* DAT1=A */
saturn.PC += 4;
store_n( saturn.D1, saturn.A, op4 + 1 );
return 0;
case 2: /* A=DAT0 */
saturn.PC += 4;
recall_n( saturn.A, saturn.D0, op4 + 1 );
return 0;
case 3: /* A=DAT1 */
saturn.PC += 4;
recall_n( saturn.A, saturn.D1, op4 + 1 );
return 0;
case 4: /* DAT0=C */
saturn.PC += 4;
store_n( saturn.D0, saturn.C, op4 + 1 );
return 0;
case 5: /* DAT1=C */
saturn.PC += 4;
store_n( saturn.D1, saturn.C, op4 + 1 );
return 0;
case 6: /* C=DAT0 */
saturn.PC += 4;
recall_n( saturn.C, saturn.D0, op4 + 1 );
return 0;
case 7: /* C=DAT1 */
saturn.PC += 4;
recall_n( saturn.C, saturn.D1, op4 + 1 );
return 0;
default:
return 1;
}
} else {
switch ( op3 ) {
case 0: /* DAT0=A */
saturn.PC += 4;
store( saturn.D0, saturn.A, op4 );
return 0;
case 1: /* DAT1=A */
saturn.PC += 4;
store( saturn.D1, saturn.A, op4 );
return 0;
case 2: /* A=DAT0 */
saturn.PC += 4;
recall( saturn.A, saturn.D0, op4 );
return 0;
case 3: /* A=DAT1 */
saturn.PC += 4;
recall( saturn.A, saturn.D1, op4 );
return 0;
case 4: /* DAT0=C */
saturn.PC += 4;
store( saturn.D0, saturn.C, op4 );
return 0;
case 5: /* DAT1=C */
saturn.PC += 4;
store( saturn.D1, saturn.C, op4 );
return 0;
case 6: /* C=DAT0 */
saturn.PC += 4;
recall( saturn.C, saturn.D0, op4 );
return 0;
case 7: /* C=DAT1 */
saturn.PC += 4;
recall( saturn.C, saturn.D1, op4 );
return 0;
default:
return 1;
}
}
case 6:
op3 = read_nibble( saturn.PC + 2 );
saturn.PC += 3;
add_address( &saturn.D0, op3 + 1 );
return 0;
case 7:
op3 = read_nibble( saturn.PC + 2 );
saturn.PC += 3;
add_address( &saturn.D1, op3 + 1 );
return 0;
case 8:
op3 = read_nibble( saturn.PC + 2 );
saturn.PC += 3;
add_address( &saturn.D0, -( op3 + 1 ) );
return 0;
case 9:
load_addr( &saturn.D0, saturn.PC + 2, 2 );
saturn.PC += 4;
return 0;
case 0xa:
load_addr( &saturn.D0, saturn.PC + 2, 4 );
saturn.PC += 6;
return 0;
case 0xb:
load_addr( &saturn.D0, saturn.PC + 2, 5 );
saturn.PC += 7;
return 0;
case 0xc:
op3 = read_nibble( saturn.PC + 2 );
saturn.PC += 3;
add_address( &saturn.D1, -( op3 + 1 ) );
return 0;
case 0xd:
load_addr( &saturn.D1, saturn.PC + 2, 2 );
saturn.PC += 4;
return 0;
case 0xe:
load_addr( &saturn.D1, saturn.PC + 2, 4 );
saturn.PC += 6;
return 0;
case 0xf:
load_addr( &saturn.D1, saturn.PC + 2, 5 );
saturn.PC += 7;
return 0;
default:
return 1;
}
}
static inline int decode_8_thru_f( int op1 ) {
int op2, op3, op4, op5, op6;
op2 = read_nibble( saturn.PC + 1 );
switch ( op1 ) {
case 8:
switch ( op2 ) {
case 0:
return decode_group_80();
case 1:
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* ASLC */
saturn.PC += 3;
shift_left_circ_register( saturn.A, W_FIELD );
return 0;
case 1: /* BSLC */
saturn.PC += 3;
shift_left_circ_register( saturn.B, W_FIELD );
return 0;
case 2: /* CSLC */
saturn.PC += 3;
shift_left_circ_register( saturn.C, W_FIELD );
return 0;
case 3: /* DSLC */
saturn.PC += 3;
shift_left_circ_register( saturn.D, W_FIELD );
return 0;
case 4: /* ASRC */
saturn.PC += 3;
shift_right_circ_register( saturn.A, W_FIELD );
return 0;
case 5: /* BSRC */
saturn.PC += 3;
shift_right_circ_register( saturn.B, W_FIELD );
return 0;
case 6: /* CSRC */
saturn.PC += 3;
shift_right_circ_register( saturn.C, W_FIELD );
return 0;
case 7: /* DSRC */
saturn.PC += 3;
shift_right_circ_register( saturn.D, W_FIELD );
return 0;
case 8: /* R = R +/- CON */
op4 = read_nibble( saturn.PC + 3 );
op5 = read_nibble( saturn.PC + 4 );
op6 = read_nibble( saturn.PC + 5 );
if ( op5 < 8 ) { /* PLUS */
switch ( op5 & 3 ) {
case 0: /* A=A+CON */
saturn.PC += 6;
add_register_constant( saturn.A, op4,
op6 + 1 );
return 0;
case 1: /* B=B+CON */
saturn.PC += 6;
add_register_constant( saturn.B, op4,
op6 + 1 );
return 0;
case 2: /* C=C+CON */
saturn.PC += 6;
add_register_constant( saturn.C, op4,
op6 + 1 );
return 0;
case 3: /* D=D+CON */
saturn.PC += 6;
add_register_constant( saturn.D, op4,
op6 + 1 );
return 0;
default:
return 1;
}
} else { /* MINUS */
switch ( op5 & 3 ) {
case 0: /* A=A-CON */
saturn.PC += 6;
sub_register_constant( saturn.A, op4,
op6 + 1 );
return 0;
case 1: /* B=B-CON */
saturn.PC += 6;
sub_register_constant( saturn.B, op4,
op6 + 1 );
return 0;
case 2: /* C=C-CON */
saturn.PC += 6;
sub_register_constant( saturn.C, op4,
op6 + 1 );
return 0;
case 3: /* D=D-CON */
saturn.PC += 6;
sub_register_constant( saturn.D, op4,
op6 + 1 );
return 0;
default:
return 1;
}
}
case 9: /* R SRB FIELD */
op4 = read_nibble( saturn.PC + 3 );
op5 = read_nibble( saturn.PC + 4 );
switch ( op5 & 3 ) {
case 0:
saturn.PC += 5;
shift_right_bit_register( saturn.A, op4 );
return 0;
case 1:
saturn.PC += 5;
shift_right_bit_register( saturn.B, op4 );
return 0;
case 2:
saturn.PC += 5;
shift_right_bit_register( saturn.C, op4 );
return 0;
case 3:
saturn.PC += 5;
shift_right_bit_register( saturn.D, op4 );
return 0;
default:
return 1;
}
case 0xa: /* R = R FIELD, etc. */
op4 = read_nibble( saturn.PC + 3 );
op5 = read_nibble( saturn.PC + 4 );
op6 = read_nibble( saturn.PC + 5 );
switch ( op5 ) {
case 0:
switch ( op6 ) {
case 0: /* saturn.R0=A */
saturn.PC += 6;
copy_register( saturn.R0, saturn.A,
op4 );
return 0;
case 1: /* saturn.R1=A */
case 5:
saturn.PC += 6;
copy_register( saturn.R1, saturn.A,
op4 );
return 0;
case 2: /* saturn.R2=A */
case 6:
saturn.PC += 6;
copy_register( saturn.R2, saturn.A,
op4 );
return 0;
case 3: /* saturn.R3=A */
case 7:
saturn.PC += 6;
copy_register( saturn.R3, saturn.A,
op4 );
return 0;
case 4: /* saturn.R4=A */
saturn.PC += 6;
copy_register( saturn.R4, saturn.A,
op4 );
return 0;
case 8: /* saturn.R0=C */
saturn.PC += 6;
copy_register( saturn.R0, saturn.C,
op4 );
return 0;
case 9: /* saturn.R1=C */
case 0xd:
saturn.PC += 6;
copy_register( saturn.R1, saturn.C,
op4 );
return 0;
case 0xa: /* saturn.R2=C */
case 0xe:
saturn.PC += 6;
copy_register( saturn.R2, saturn.C,
op4 );
return 0;
case 0xb: /* saturn.R3=C */
case 0xf:
saturn.PC += 6;
copy_register( saturn.R3, saturn.C,
op4 );
return 0;
case 0xc: /* saturn.R4=C */
saturn.PC += 6;
copy_register( saturn.R4, saturn.C,
op4 );
return 0;
default:
return 1;
}
case 1:
switch ( op6 ) {
case 0: /* A=R0 */
saturn.PC += 6;
copy_register( saturn.A, saturn.R0,
op4 );
return 0;
case 1: /* A=R1 */
case 5:
saturn.PC += 6;
copy_register( saturn.A, saturn.R1,
op4 );
return 0;
case 2: /* A=R2 */
case 6:
saturn.PC += 6;
copy_register( saturn.A, saturn.R2,
op4 );
return 0;
case 3: /* A=R3 */
case 7:
saturn.PC += 6;
copy_register( saturn.A, saturn.R3,
op4 );
return 0;
case 4: /* A=R4 */
saturn.PC += 6;
copy_register( saturn.A, saturn.R4,
op4 );
return 0;
case 8: /* C=R0 */
saturn.PC += 6;
copy_register( saturn.C, saturn.R0,
op4 );
return 0;
case 9: /* C=R1 */
case 0xd:
saturn.PC += 6;
copy_register( saturn.C, saturn.R1,
op4 );
return 0;
case 0xa: /* C=R2 */
case 0xe:
saturn.PC += 6;
copy_register( saturn.C, saturn.R2,
op4 );
return 0;
case 0xb: /* C=R3 */
case 0xf:
saturn.PC += 6;
copy_register( saturn.C, saturn.R3,
op4 );
return 0;
case 0xc: /* C=R4 */
saturn.PC += 6;
copy_register( saturn.C, saturn.R4,
op4 );
return 0;
default:
return 1;
}
case 2:
switch ( op6 ) {
case 0: /* AR0EX */
saturn.PC += 6;
exchange_register( saturn.A,
saturn.R0, op4 );
return 0;
case 1: /* AR1EX */
case 5:
saturn.PC += 6;
exchange_register( saturn.A,
saturn.R1, op4 );
return 0;
case 2: /* AR2EX */
case 6:
saturn.PC += 6;
exchange_register( saturn.A,
saturn.R2, op4 );
return 0;
case 3: /* AR3EX */
case 7:
saturn.PC += 6;
exchange_register( saturn.A,
saturn.R3, op4 );
return 0;
case 4: /* AR4EX */
saturn.PC += 6;
exchange_register( saturn.A,
saturn.R4, op4 );
return 0;
case 8: /* CR0EX */
saturn.PC += 6;
exchange_register( saturn.C,
saturn.R0, op4 );
return 0;
case 9: /* CR1EX */
case 0xd:
saturn.PC += 6;
exchange_register( saturn.C,
saturn.R1, op4 );
return 0;
case 0xa: /* CR2EX */
case 0xe:
saturn.PC += 6;
exchange_register( saturn.C,
saturn.R2, op4 );
return 0;
case 0xb: /* CR3EX */
case 0xf:
saturn.PC += 6;
exchange_register( saturn.C,
saturn.R3, op4 );
return 0;
case 0xc: /* CR4EX */
saturn.PC += 6;
exchange_register( saturn.C,
saturn.R4, op4 );
return 0;
default:
return 1;
}
default:
return 1;
}
case 0xb:
op4 = read_nibble( saturn.PC + 3 );
switch ( op4 ) {
case 2: /* PC=A */
jumpaddr = dat_to_addr( saturn.A );
saturn.PC = jumpaddr;
return 0;
case 3: /* PC=C */
jumpaddr = dat_to_addr( saturn.C );
saturn.PC = jumpaddr;
return 0;
case 4: /* A=PC */
saturn.PC += 4;
addr_to_dat( saturn.PC, saturn.A );
return 0;
case 5: /* C=PC */
saturn.PC += 4;
addr_to_dat( saturn.PC, saturn.C );
return 0;
case 6: /* APCEX */
saturn.PC += 4;
jumpaddr = dat_to_addr( saturn.A );
addr_to_dat( saturn.PC, saturn.A );
saturn.PC = jumpaddr;
return 0;
case 7: /* CPCEX */
saturn.PC += 4;
jumpaddr = dat_to_addr( saturn.C );
addr_to_dat( saturn.PC, saturn.C );
saturn.PC = jumpaddr;
return 0;
default:
return 1;
}
case 0xc: /* ASRB */
saturn.PC += 3;
shift_right_bit_register( saturn.A, W_FIELD );
return 0;
case 0xd: /* BSRB */
saturn.PC += 3;
shift_right_bit_register( saturn.B, W_FIELD );
return 0;
case 0xe: /* CSRB */
saturn.PC += 3;
shift_right_bit_register( saturn.C, W_FIELD );
return 0;
case 0xf: /* DSRB */
saturn.PC += 3;
shift_right_bit_register( saturn.D, W_FIELD );
return 0;
default:
return 1;
}
case 2:
op3 = read_nibble( saturn.PC + 2 );
saturn.PC += 3;
clear_hardware_stat( op3 );
return 0;
case 3:
op3 = read_nibble( saturn.PC + 2 );
saturn.CARRY = is_zero_hardware_stat( op3 );
if ( saturn.CARRY ) {
saturn.PC += 3;
op4 = read_nibbles( saturn.PC, 2 );
if ( op4 ) {
if ( op4 & 0x80 )
op4 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op4 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 5;
}
return 0;
case 4:
case 5:
op3 = read_nibble( saturn.PC + 2 );
if ( op2 == 4 ) {
saturn.PC += 3;
clear_program_stat( op3 );
} else {
saturn.PC += 3;
set_program_stat( op3 );
}
return 0;
case 6:
case 7:
op3 = read_nibble( saturn.PC + 2 );
if ( op2 == 6 )
saturn.CARRY = ( get_program_stat( op3 ) == 0 ) ? 1 : 0;
else
saturn.CARRY = ( get_program_stat( op3 ) != 0 ) ? 1 : 0;
if ( saturn.CARRY ) {
saturn.PC += 3;
op4 = read_nibbles( saturn.PC, 2 );
if ( op4 ) {
if ( op4 & 0x80 )
op4 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op4 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 5;
}
return 0;
case 8:
case 9:
op3 = read_nibble( saturn.PC + 2 );
if ( op2 == 8 )
saturn.CARRY = ( saturn.P != op3 ) ? 1 : 0;
else
saturn.CARRY = ( saturn.P == op3 ) ? 1 : 0;
if ( saturn.CARRY ) {
saturn.PC += 3;
op4 = read_nibbles( saturn.PC, 2 );
if ( op4 ) {
if ( op4 & 0x80 )
op4 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op4 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 5;
}
return 0;
case 0xa:
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* ?A=B */
saturn.CARRY = is_equal_register(
saturn.A, saturn.B, A_FIELD );
break;
case 1: /* ?B=C */
saturn.CARRY = is_equal_register(
saturn.B, saturn.C, A_FIELD );
break;
case 2: /* ?A=C */
saturn.CARRY = is_equal_register(
saturn.A, saturn.C, A_FIELD );
break;
case 3: /* ?C=D */
saturn.CARRY = is_equal_register(
saturn.C, saturn.D, A_FIELD );
break;
case 4: /* ?A#B */
saturn.CARRY = is_not_equal_register(
saturn.A, saturn.B, A_FIELD );
break;
case 5: /* ?B#C */
saturn.CARRY = is_not_equal_register(
saturn.B, saturn.C, A_FIELD );
break;
case 6: /* ?A#C */
saturn.CARRY = is_not_equal_register(
saturn.A, saturn.C, A_FIELD );
break;
case 7: /* ?C#D */
saturn.CARRY = is_not_equal_register(
saturn.C, saturn.D, A_FIELD );
break;
case 8: /* ?A=0 */
saturn.CARRY =
is_zero_register( saturn.A, A_FIELD );
break;
case 9: /* ?B=0 */
saturn.CARRY =
is_zero_register( saturn.B, A_FIELD );
break;
case 0xa: /* ?C=0 */
saturn.CARRY =
is_zero_register( saturn.C, A_FIELD );
break;
case 0xb: /* ?D=0 */
saturn.CARRY =
is_zero_register( saturn.D, A_FIELD );
break;
case 0xc: /* ?A#0 */
saturn.CARRY =
is_not_zero_register( saturn.A, A_FIELD );
break;
case 0xd: /* ?B#0 */
saturn.CARRY =
is_not_zero_register( saturn.B, A_FIELD );
break;
case 0xe: /* ?C#0 */
saturn.CARRY =
is_not_zero_register( saturn.C, A_FIELD );
break;
case 0xf: /* ?D#0 */
saturn.CARRY =
is_not_zero_register( saturn.D, A_FIELD );
break;
default:
return 1;
}
if ( saturn.CARRY ) {
saturn.PC += 3;
op4 = read_nibbles( saturn.PC, 2 );
if ( op4 ) {
if ( op4 & 0x80 )
op4 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op4 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 5;
}
return 0;
case 0xb:
op3 = read_nibble( saturn.PC + 2 );
switch ( op3 ) {
case 0: /* ?A>B */
saturn.CARRY = is_greater_register(
saturn.A, saturn.B, A_FIELD );
break;
case 1: /* ?B>C */
saturn.CARRY = is_greater_register(
saturn.B, saturn.C, A_FIELD );
break;
case 2: /* ?C>A */
saturn.CARRY = is_greater_register(
saturn.C, saturn.A, A_FIELD );
break;
case 3: /* ?D>C */
saturn.CARRY = is_greater_register(
saturn.D, saturn.C, A_FIELD );
break;
case 4: /* ?A<B */
saturn.CARRY =
is_less_register( saturn.A, saturn.B, A_FIELD );
break;
case 5: /* ?B<C */
saturn.CARRY =
is_less_register( saturn.B, saturn.C, A_FIELD );
break;
case 6: /* ?C<A */
saturn.CARRY =
is_less_register( saturn.C, saturn.A, A_FIELD );
break;
case 7: /* ?D<C */
saturn.CARRY =
is_less_register( saturn.D, saturn.C, A_FIELD );
break;
case 8: /* ?A>=B */
saturn.CARRY = is_greater_or_equal_register(
saturn.A, saturn.B, A_FIELD );
break;
case 9: /* ?B>=C */
saturn.CARRY = is_greater_or_equal_register(
saturn.B, saturn.C, A_FIELD );
break;
case 0xa: /* ?C>=A */
saturn.CARRY = is_greater_or_equal_register(
saturn.C, saturn.A, A_FIELD );
break;
case 0xb: /* ?D>=C */
saturn.CARRY = is_greater_or_equal_register(
saturn.D, saturn.C, A_FIELD );
break;
case 0xc: /* ?A<=B */
saturn.CARRY = is_less_or_equal_register(
saturn.A, saturn.B, A_FIELD );
break;
case 0xd: /* ?B<=C */
saturn.CARRY = is_less_or_equal_register(
saturn.B, saturn.C, A_FIELD );
break;
case 0xe: /* ?C<=A */
saturn.CARRY = is_less_or_equal_register(
saturn.C, saturn.A, A_FIELD );
break;
case 0xf: /* ?D<=C */
saturn.CARRY = is_less_or_equal_register(
saturn.D, saturn.C, A_FIELD );
break;
default:
return 1;
}
if ( saturn.CARRY ) {
saturn.PC += 3;
op4 = read_nibbles( saturn.PC, 2 );
if ( op4 ) {
if ( op4 & 0x80 )
op4 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op4 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 5;
}
return 0;
case 0xc:
op3 = read_nibbles( saturn.PC + 2, 4 );
if ( op3 & 0x8000 )
op3 |= jumpmasks[ 4 ];
jumpaddr = ( saturn.PC + op3 + 2 ) & 0xfffff;
saturn.PC = jumpaddr;
return 0;
case 0xd:
op3 = read_nibbles( saturn.PC + 2, 5 );
jumpaddr = op3;
saturn.PC = jumpaddr;
return 0;
case 0xe:
op3 = read_nibbles( saturn.PC + 2, 4 );
if ( op3 & 0x8000 )
op3 |= jumpmasks[ 4 ];
jumpaddr = ( saturn.PC + op3 + 6 ) & 0xfffff;
push_return_addr( saturn.PC + 6 );
saturn.PC = jumpaddr;
return 0;
case 0xf:
op3 = read_nibbles( saturn.PC + 2, 5 );
jumpaddr = op3;
push_return_addr( saturn.PC + 7 );
saturn.PC = jumpaddr;
return 0;
default:
return 1;
}
case 9:
op3 = read_nibble( saturn.PC + 2 );
if ( op2 < 8 ) {
switch ( op3 ) {
case 0: /* ?A=B */
saturn.CARRY =
is_equal_register( saturn.A, saturn.B, op2 );
break;
case 1: /* ?B=C */
saturn.CARRY =
is_equal_register( saturn.B, saturn.C, op2 );
break;
case 2: /* ?A=C */
saturn.CARRY =
is_equal_register( saturn.A, saturn.C, op2 );
break;
case 3: /* ?C=D */
saturn.CARRY =
is_equal_register( saturn.C, saturn.D, op2 );
break;
case 4: /* ?A#B */
saturn.CARRY =
is_not_equal_register( saturn.A, saturn.B, op2 );
break;
case 5: /* ?B#C */
saturn.CARRY =
is_not_equal_register( saturn.B, saturn.C, op2 );
break;
case 6: /* ?A#C */
saturn.CARRY =
is_not_equal_register( saturn.A, saturn.C, op2 );
break;
case 7: /* ?C#D */
saturn.CARRY =
is_not_equal_register( saturn.C, saturn.D, op2 );
break;
case 8: /* ?A=0 */
saturn.CARRY = is_zero_register( saturn.A, op2 );
break;
case 9: /* ?B=0 */
saturn.CARRY = is_zero_register( saturn.B, op2 );
break;
case 0xa: /* ?C=0 */
saturn.CARRY = is_zero_register( saturn.C, op2 );
break;
case 0xb: /* ?D=0 */
saturn.CARRY = is_zero_register( saturn.D, op2 );
break;
case 0xc: /* ?A#0 */
saturn.CARRY = is_not_zero_register( saturn.A, op2 );
break;
case 0xd: /* ?B#0 */
saturn.CARRY = is_not_zero_register( saturn.B, op2 );
break;
case 0xe: /* ?C#0 */
saturn.CARRY = is_not_zero_register( saturn.C, op2 );
break;
case 0xf: /* ?D#0 */
saturn.CARRY = is_not_zero_register( saturn.D, op2 );
break;
default:
return 1;
}
} else {
op2 &= 7;
switch ( op3 ) {
case 0: /* ?A>B */
saturn.CARRY =
is_greater_register( saturn.A, saturn.B, op2 );
break;
case 1: /* ?B>C */
saturn.CARRY =
is_greater_register( saturn.B, saturn.C, op2 );
break;
case 2: /* ?C>A */
saturn.CARRY =
is_greater_register( saturn.C, saturn.A, op2 );
break;
case 3: /* ?D>C */
saturn.CARRY =
is_greater_register( saturn.D, saturn.C, op2 );
break;
case 4: /* ?A<B */
saturn.CARRY =
is_less_register( saturn.A, saturn.B, op2 );
break;
case 5: /* ?B<C */
saturn.CARRY =
is_less_register( saturn.B, saturn.C, op2 );
break;
case 6: /* ?C<A */
saturn.CARRY =
is_less_register( saturn.C, saturn.A, op2 );
break;
case 7: /* ?D<C */
saturn.CARRY =
is_less_register( saturn.D, saturn.C, op2 );
break;
case 8: /* ?A>=B */
saturn.CARRY = is_greater_or_equal_register(
saturn.A, saturn.B, op2 );
break;
case 9: /* ?B>=C */
saturn.CARRY = is_greater_or_equal_register(
saturn.B, saturn.C, op2 );
break;
case 0xa: /* ?C>=A */
saturn.CARRY = is_greater_or_equal_register(
saturn.C, saturn.A, op2 );
break;
case 0xb: /* ?D>=C */
saturn.CARRY = is_greater_or_equal_register(
saturn.D, saturn.C, op2 );
break;
case 0xc: /* ?A<=B */
saturn.CARRY = is_less_or_equal_register(
saturn.A, saturn.B, op2 );
break;
case 0xd: /* ?B<=C */
saturn.CARRY = is_less_or_equal_register(
saturn.B, saturn.C, op2 );
break;
case 0xe: /* ?C<=A */
saturn.CARRY = is_less_or_equal_register(
saturn.C, saturn.A, op2 );
break;
case 0xf: /* ?D<=C */
saturn.CARRY = is_less_or_equal_register(
saturn.D, saturn.C, op2 );
break;
default:
return 1;
}
}
if ( saturn.CARRY ) {
saturn.PC += 3;
op4 = read_nibbles( saturn.PC, 2 );
if ( op4 ) {
if ( op4 & 0x80 )
op4 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op4 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 5;
}
return 0;
case 0xa:
op3 = read_nibble( saturn.PC + 2 );
if ( op2 < 8 ) {
switch ( op3 ) {
case 0: /* A=A+B */
saturn.PC += 3;
add_register( saturn.A, saturn.A, saturn.B, op2 );
return 0;
case 1: /* B=B+C */
saturn.PC += 3;
add_register( saturn.B, saturn.B, saturn.C, op2 );
return 0;
case 2: /* C=C+A */
saturn.PC += 3;
add_register( saturn.C, saturn.C, saturn.A, op2 );
return 0;
case 3: /* D=D+C */
saturn.PC += 3;
add_register( saturn.D, saturn.D, saturn.C, op2 );
return 0;
case 4: /* A=A+A */
saturn.PC += 3;
add_register( saturn.A, saturn.A, saturn.A, op2 );
return 0;
case 5: /* B=B+B */
saturn.PC += 3;
add_register( saturn.B, saturn.B, saturn.B, op2 );
return 0;
case 6: /* C=C+C */
saturn.PC += 3;
add_register( saturn.C, saturn.C, saturn.C, op2 );
return 0;
case 7: /* D=D+D */
saturn.PC += 3;
add_register( saturn.D, saturn.D, saturn.D, op2 );
return 0;
case 8: /* B=B+A */
saturn.PC += 3;
add_register( saturn.B, saturn.B, saturn.A, op2 );
return 0;
case 9: /* C=C+B */
saturn.PC += 3;
add_register( saturn.C, saturn.C, saturn.B, op2 );
return 0;
case 0xa: /* A=A+C */
saturn.PC += 3;
add_register( saturn.A, saturn.A, saturn.C, op2 );
return 0;
case 0xb: /* C=C+D */
saturn.PC += 3;
add_register( saturn.C, saturn.C, saturn.D, op2 );
return 0;
case 0xc: /* A=A-1 */
saturn.PC += 3;
dec_register( saturn.A, op2 );
return 0;
case 0xd: /* B=B-1 */
saturn.PC += 3;
dec_register( saturn.B, op2 );
return 0;
case 0xe: /* C=C-1 */
saturn.PC += 3;
dec_register( saturn.C, op2 );
return 0;
case 0xf: /* D=D-1 */
saturn.PC += 3;
dec_register( saturn.D, op2 );
return 0;
default:
return 1;
}
} else {
op2 &= 7;
switch ( op3 ) {
case 0: /* A=0 */
saturn.PC += 3;
zero_register( saturn.A, op2 );
return 0;
case 1: /* B=0 */
saturn.PC += 3;
zero_register( saturn.B, op2 );
return 0;
case 2: /* C=0 */
saturn.PC += 3;
zero_register( saturn.C, op2 );
return 0;
case 3: /* D=0 */
saturn.PC += 3;
zero_register( saturn.D, op2 );
return 0;
case 4: /* A=B */
saturn.PC += 3;
copy_register( saturn.A, saturn.B, op2 );
return 0;
case 5: /* B=C */
saturn.PC += 3;
copy_register( saturn.B, saturn.C, op2 );
return 0;
case 6: /* C=A */
saturn.PC += 3;
copy_register( saturn.C, saturn.A, op2 );
return 0;
case 7: /* D=C */
saturn.PC += 3;
copy_register( saturn.D, saturn.C, op2 );
return 0;
case 8: /* B=A */
saturn.PC += 3;
copy_register( saturn.B, saturn.A, op2 );
return 0;
case 9: /* C=B */
saturn.PC += 3;
copy_register( saturn.C, saturn.B, op2 );
return 0;
case 0xa: /* A=C */
saturn.PC += 3;
copy_register( saturn.A, saturn.C, op2 );
return 0;
case 0xb: /* C=D */
saturn.PC += 3;
copy_register( saturn.C, saturn.D, op2 );
return 0;
case 0xc: /* ABEX */
saturn.PC += 3;
exchange_register( saturn.A, saturn.B, op2 );
return 0;
case 0xd: /* BCEX */
saturn.PC += 3;
exchange_register( saturn.B, saturn.C, op2 );
return 0;
case 0xe: /* ACEX */
saturn.PC += 3;
exchange_register( saturn.A, saturn.C, op2 );
return 0;
case 0xf: /* CDEX */
saturn.PC += 3;
exchange_register( saturn.C, saturn.D, op2 );
return 0;
default:
return 1;
}
}
case 0xb:
op3 = read_nibble( saturn.PC + 2 );
if ( op2 < 8 ) {
switch ( op3 ) {
case 0: /* A=A-B */
saturn.PC += 3;
sub_register( saturn.A, saturn.A, saturn.B, op2 );
return 0;
case 1: /* B=B-C */
saturn.PC += 3;
sub_register( saturn.B, saturn.B, saturn.C, op2 );
return 0;
case 2: /* C=C-A */
saturn.PC += 3;
sub_register( saturn.C, saturn.C, saturn.A, op2 );
return 0;
case 3: /* D=D-C */
saturn.PC += 3;
sub_register( saturn.D, saturn.D, saturn.C, op2 );
return 0;
case 4: /* A=A+1 */
saturn.PC += 3;
inc_register( saturn.A, op2 );
return 0;
case 5: /* B=B+1 */
saturn.PC += 3;
inc_register( saturn.B, op2 );
return 0;
case 6: /* C=C+1 */
saturn.PC += 3;
inc_register( saturn.C, op2 );
return 0;
case 7: /* D=D+1 */
saturn.PC += 3;
inc_register( saturn.D, op2 );
return 0;
case 8: /* B=B-A */
saturn.PC += 3;
sub_register( saturn.B, saturn.B, saturn.A, op2 );
return 0;
case 9: /* C=C-B */
saturn.PC += 3;
sub_register( saturn.C, saturn.C, saturn.B, op2 );
return 0;
case 0xa: /* A=A-C */
saturn.PC += 3;
sub_register( saturn.A, saturn.A, saturn.C, op2 );
return 0;
case 0xb: /* C=C-D */
saturn.PC += 3;
sub_register( saturn.C, saturn.C, saturn.D, op2 );
return 0;
case 0xc: /* A=B-A */
saturn.PC += 3;
sub_register( saturn.A, saturn.B, saturn.A, op2 );
return 0;
case 0xd: /* B=C-B */
saturn.PC += 3;
sub_register( saturn.B, saturn.C, saturn.B, op2 );
return 0;
case 0xe: /* C=A-C */
saturn.PC += 3;
sub_register( saturn.C, saturn.A, saturn.C, op2 );
return 0;
case 0xf: /* D=C-D */
saturn.PC += 3;
sub_register( saturn.D, saturn.C, saturn.D, op2 );
return 0;
default:
return 1;
}
} else {
op2 &= 7;
switch ( op3 ) {
case 0: /* ASL */
saturn.PC += 3;
shift_left_register( saturn.A, op2 );
return 0;
case 1: /* BSL */
saturn.PC += 3;
shift_left_register( saturn.B, op2 );
return 0;
case 2: /* CSL */
saturn.PC += 3;
shift_left_register( saturn.C, op2 );
return 0;
case 3: /* DSL */
saturn.PC += 3;
shift_left_register( saturn.D, op2 );
return 0;
case 4: /* ASR */
saturn.PC += 3;
shift_right_register( saturn.A, op2 );
return 0;
case 5: /* BSR */
saturn.PC += 3;
shift_right_register( saturn.B, op2 );
return 0;
case 6: /* CSR */
saturn.PC += 3;
shift_right_register( saturn.C, op2 );
return 0;
case 7: /* DSR */
saturn.PC += 3;
shift_right_register( saturn.D, op2 );
return 0;
case 8: /* A=-A */
saturn.PC += 3;
complement_2_register( saturn.A, op2 );
return 0;
case 9: /* B=-B */
saturn.PC += 3;
complement_2_register( saturn.B, op2 );
return 0;
case 0xa: /* C=-C */
saturn.PC += 3;
complement_2_register( saturn.C, op2 );
return 0;
case 0xb: /* D=-D */
saturn.PC += 3;
complement_2_register( saturn.D, op2 );
return 0;
case 0xc: /* A=-A-1 */
saturn.PC += 3;
complement_1_register( saturn.A, op2 );
return 0;
case 0xd: /* B=-B-1 */
saturn.PC += 3;
complement_1_register( saturn.B, op2 );
return 0;
case 0xe: /* C=-C-1 */
saturn.PC += 3;
complement_1_register( saturn.C, op2 );
return 0;
case 0xf: /* D=-D-1 */
saturn.PC += 3;
complement_1_register( saturn.D, op2 );
return 0;
default:
return 1;
}
}
case 0xc:
switch ( op2 ) {
case 0: /* A=A+B */
saturn.PC += 2;
add_register( saturn.A, saturn.A, saturn.B, A_FIELD );
return 0;
case 1: /* B=B+C */
saturn.PC += 2;
add_register( saturn.B, saturn.B, saturn.C, A_FIELD );
return 0;
case 2: /* C=C+A */
saturn.PC += 2;
add_register( saturn.C, saturn.C, saturn.A, A_FIELD );
return 0;
case 3: /* D=D+C */
saturn.PC += 2;
add_register( saturn.D, saturn.D, saturn.C, A_FIELD );
return 0;
case 4: /* A=A+A */
saturn.PC += 2;
add_register( saturn.A, saturn.A, saturn.A, A_FIELD );
return 0;
case 5: /* B=B+B */
saturn.PC += 2;
add_register( saturn.B, saturn.B, saturn.B, A_FIELD );
return 0;
case 6: /* C=C+C */
saturn.PC += 2;
add_register( saturn.C, saturn.C, saturn.C, A_FIELD );
return 0;
case 7: /* D=D+D */
saturn.PC += 2;
add_register( saturn.D, saturn.D, saturn.D, A_FIELD );
return 0;
case 8: /* B=B+A */
saturn.PC += 2;
add_register( saturn.B, saturn.B, saturn.A, A_FIELD );
return 0;
case 9: /* C=C+B */
saturn.PC += 2;
add_register( saturn.C, saturn.C, saturn.B, A_FIELD );
return 0;
case 0xa: /* A=A+C */
saturn.PC += 2;
add_register( saturn.A, saturn.A, saturn.C, A_FIELD );
return 0;
case 0xb: /* C=C+D */
saturn.PC += 2;
add_register( saturn.C, saturn.C, saturn.D, A_FIELD );
return 0;
case 0xc: /* A=A-1 */
saturn.PC += 2;
dec_register( saturn.A, A_FIELD );
return 0;
case 0xd: /* B=B-1 */
saturn.PC += 2;
dec_register( saturn.B, A_FIELD );
return 0;
case 0xe: /* C=C-1 */
saturn.PC += 2;
dec_register( saturn.C, A_FIELD );
return 0;
case 0xf: /* D=D-1 */
saturn.PC += 2;
dec_register( saturn.D, A_FIELD );
return 0;
default:
return 1;
}
case 0xd:
switch ( op2 ) {
case 0: /* A=0 */
saturn.PC += 2;
zero_register( saturn.A, A_FIELD );
return 0;
case 1: /* B=0 */
saturn.PC += 2;
zero_register( saturn.B, A_FIELD );
return 0;
case 2: /* C=0 */
saturn.PC += 2;
zero_register( saturn.C, A_FIELD );
return 0;
case 3: /* D=0 */
saturn.PC += 2;
zero_register( saturn.D, A_FIELD );
return 0;
case 4: /* A=B */
saturn.PC += 2;
copy_register( saturn.A, saturn.B, A_FIELD );
return 0;
case 5: /* B=C */
saturn.PC += 2;
copy_register( saturn.B, saturn.C, A_FIELD );
return 0;
case 6: /* C=A */
saturn.PC += 2;
copy_register( saturn.C, saturn.A, A_FIELD );
return 0;
case 7: /* D=C */
saturn.PC += 2;
copy_register( saturn.D, saturn.C, A_FIELD );
return 0;
case 8: /* B=A */
saturn.PC += 2;
copy_register( saturn.B, saturn.A, A_FIELD );
return 0;
case 9: /* C=B */
saturn.PC += 2;
copy_register( saturn.C, saturn.B, A_FIELD );
return 0;
case 0xa: /* A=C */
saturn.PC += 2;
copy_register( saturn.A, saturn.C, A_FIELD );
return 0;
case 0xb: /* C=D */
saturn.PC += 2;
copy_register( saturn.C, saturn.D, A_FIELD );
return 0;
case 0xc: /* ABEX */
saturn.PC += 2;
exchange_register( saturn.A, saturn.B, A_FIELD );
return 0;
case 0xd: /* BCEX */
saturn.PC += 2;
exchange_register( saturn.B, saturn.C, A_FIELD );
return 0;
case 0xe: /* ACEX */
saturn.PC += 2;
exchange_register( saturn.A, saturn.C, A_FIELD );
return 0;
case 0xf: /* CDEX */
saturn.PC += 2;
exchange_register( saturn.C, saturn.D, A_FIELD );
return 0;
default:
return 1;
}
case 0xe:
switch ( op2 ) {
case 0: /* A=A-B */
saturn.PC += 2;
sub_register( saturn.A, saturn.A, saturn.B, A_FIELD );
return 0;
case 1: /* B=B-C */
saturn.PC += 2;
sub_register( saturn.B, saturn.B, saturn.C, A_FIELD );
return 0;
case 2: /* C=C-A */
saturn.PC += 2;
sub_register( saturn.C, saturn.C, saturn.A, A_FIELD );
return 0;
case 3: /* D=D-C */
saturn.PC += 2;
sub_register( saturn.D, saturn.D, saturn.C, A_FIELD );
return 0;
case 4: /* A=A+1 */
saturn.PC += 2;
inc_register( saturn.A, A_FIELD );
return 0;
case 5: /* B=B+1 */
saturn.PC += 2;
inc_register( saturn.B, A_FIELD );
return 0;
case 6: /* C=C+1 */
saturn.PC += 2;
inc_register( saturn.C, A_FIELD );
return 0;
case 7: /* D=D+1 */
saturn.PC += 2;
inc_register( saturn.D, A_FIELD );
return 0;
case 8: /* B=B-A */
saturn.PC += 2;
sub_register( saturn.B, saturn.B, saturn.A, A_FIELD );
return 0;
case 9: /* C=C-B */
saturn.PC += 2;
sub_register( saturn.C, saturn.C, saturn.B, A_FIELD );
return 0;
case 0xa: /* A=A-C */
saturn.PC += 2;
sub_register( saturn.A, saturn.A, saturn.C, A_FIELD );
return 0;
case 0xb: /* C=C-D */
saturn.PC += 2;
sub_register( saturn.C, saturn.C, saturn.D, A_FIELD );
return 0;
case 0xc: /* A=B-A */
saturn.PC += 2;
sub_register( saturn.A, saturn.B, saturn.A, A_FIELD );
return 0;
case 0xd: /* B=C-B */
saturn.PC += 2;
sub_register( saturn.B, saturn.C, saturn.B, A_FIELD );
return 0;
case 0xe: /* C=A-C */
saturn.PC += 2;
sub_register( saturn.C, saturn.A, saturn.C, A_FIELD );
return 0;
case 0xf: /* D=C-D */
saturn.PC += 2;
sub_register( saturn.D, saturn.C, saturn.D, A_FIELD );
return 0;
default:
return 1;
}
case 0xf:
switch ( op2 ) {
case 0: /* ASL */
saturn.PC += 2;
shift_left_register( saturn.A, A_FIELD );
return 0;
case 1: /* BSL */
saturn.PC += 2;
shift_left_register( saturn.B, A_FIELD );
return 0;
case 2: /* CSL */
saturn.PC += 2;
shift_left_register( saturn.C, A_FIELD );
return 0;
case 3: /* DSL */
saturn.PC += 2;
shift_left_register( saturn.D, A_FIELD );
return 0;
case 4: /* ASR */
saturn.PC += 2;
shift_right_register( saturn.A, A_FIELD );
return 0;
case 5: /* BSR */
saturn.PC += 2;
shift_right_register( saturn.B, A_FIELD );
return 0;
case 6: /* CSR */
saturn.PC += 2;
shift_right_register( saturn.C, A_FIELD );
return 0;
case 7: /* DSR */
saturn.PC += 2;
shift_right_register( saturn.D, A_FIELD );
return 0;
case 8: /* A=-A */
saturn.PC += 2;
complement_2_register( saturn.A, A_FIELD );
return 0;
case 9: /* B=-B */
saturn.PC += 2;
complement_2_register( saturn.B, A_FIELD );
return 0;
case 0xa: /* C=-C */
saturn.PC += 2;
complement_2_register( saturn.C, A_FIELD );
return 0;
case 0xb: /* D=-D */
saturn.PC += 2;
complement_2_register( saturn.D, A_FIELD );
return 0;
case 0xc: /* A=-A-1 */
saturn.PC += 2;
complement_1_register( saturn.A, A_FIELD );
return 0;
case 0xd: /* B=-B-1 */
saturn.PC += 2;
complement_1_register( saturn.B, A_FIELD );
return 0;
case 0xe: /* C=-C-1 */
saturn.PC += 2;
complement_1_register( saturn.C, A_FIELD );
return 0;
case 0xf: /* D=-D-1 */
saturn.PC += 2;
complement_1_register( saturn.D, A_FIELD );
return 0;
default:
return 1;
}
default:
return 1;
}
}
inline int step_instruction( void ) {
int op0, op1, op2, op3;
int stop = 0;
jumpaddr = 0;
op0 = read_nibble( saturn.PC );
switch ( op0 ) {
case 0:
op1 = read_nibble( saturn.PC + 1 );
switch ( op1 ) {
case 0: /* RTNSXM */
saturn.XM = 1;
saturn.PC = pop_return_addr();
break;
case 1: /* RTN */
saturn.PC = pop_return_addr();
break;
case 2: /* RTNSC */
saturn.CARRY = 1;
saturn.PC = pop_return_addr();
break;
case 3: /* RTNCC */
saturn.CARRY = 0;
saturn.PC = pop_return_addr();
break;
case 4: /* SETHEX */
saturn.PC += 2;
saturn.hexmode = HEX;
break;
case 5: /* SETDEC */
saturn.PC += 2;
saturn.hexmode = DEC;
break;
case 6: /* RSTK=C */
jumpaddr = dat_to_addr( saturn.C );
push_return_addr( jumpaddr );
saturn.PC += 2;
break;
case 7: /* C=RSTK */
saturn.PC += 2;
jumpaddr = pop_return_addr();
addr_to_dat( jumpaddr, saturn.C );
break;
case 8: /* CLRST */
saturn.PC += 2;
clear_status();
break;
case 9: /* C=ST */
saturn.PC += 2;
status_to_register( saturn.C );
break;
case 0xa: /* ST=C */
saturn.PC += 2;
register_to_status( saturn.C );
break;
case 0xb: /* CSTEX */
saturn.PC += 2;
swap_register_status( saturn.C );
break;
case 0xc: /* P=P+1 */
saturn.PC += 2;
if ( saturn.P == 0xf ) {
saturn.P = 0;
saturn.CARRY = 1;
} else {
saturn.P += 1;
saturn.CARRY = 0;
}
break;
case 0xd: /* P=P-1 */
saturn.PC += 2;
if ( saturn.P == 0 ) {
saturn.P = 0xf;
saturn.CARRY = 1;
} else {
saturn.P -= 1;
saturn.CARRY = 0;
}
break;
case 0xe:
op2 = read_nibble( saturn.PC + 2 );
op3 = read_nibble( saturn.PC + 3 );
switch ( op3 ) {
case 0: /* A=A&B */
saturn.PC += 4;
and_register( saturn.A, saturn.A, saturn.B, op2 );
break;
case 1: /* B=B&C */
saturn.PC += 4;
and_register( saturn.B, saturn.B, saturn.C, op2 );
break;
case 2: /* C=C&A */
saturn.PC += 4;
and_register( saturn.C, saturn.C, saturn.A, op2 );
break;
case 3: /* D=D&C */
saturn.PC += 4;
and_register( saturn.D, saturn.D, saturn.C, op2 );
break;
case 4: /* B=B&A */
saturn.PC += 4;
and_register( saturn.B, saturn.B, saturn.A, op2 );
break;
case 5: /* C=C&B */
saturn.PC += 4;
and_register( saturn.C, saturn.C, saturn.B, op2 );
break;
case 6: /* A=A&C */
saturn.PC += 4;
and_register( saturn.A, saturn.A, saturn.C, op2 );
break;
case 7: /* C=C&D */
saturn.PC += 4;
and_register( saturn.C, saturn.C, saturn.D, op2 );
break;
case 8: /* A=A!B */
saturn.PC += 4;
or_register( saturn.A, saturn.A, saturn.B, op2 );
break;
case 9: /* B=B!C */
saturn.PC += 4;
or_register( saturn.B, saturn.B, saturn.C, op2 );
break;
case 0xa: /* C=C!A */
saturn.PC += 4;
or_register( saturn.C, saturn.C, saturn.A, op2 );
break;
case 0xb: /* D=D!C */
saturn.PC += 4;
or_register( saturn.D, saturn.D, saturn.C, op2 );
break;
case 0xc: /* B=B!A */
saturn.PC += 4;
or_register( saturn.B, saturn.B, saturn.A, op2 );
break;
case 0xd: /* C=C!B */
saturn.PC += 4;
or_register( saturn.C, saturn.C, saturn.B, op2 );
break;
case 0xe: /* A=A!C */
saturn.PC += 4;
or_register( saturn.A, saturn.A, saturn.C, op2 );
break;
case 0xf: /* C=C!D */
saturn.PC += 4;
or_register( saturn.C, saturn.C, saturn.D, op2 );
break;
default:
stop = 1;
break;
}
break;
case 0xf: /* RTI */
do_return_interupt();
break;
default:
stop = 1;
break;
}
break;
case 1:
stop = decode_group_1();
break;
case 2:
op2 = read_nibble( saturn.PC + 1 );
saturn.PC += 2;
saturn.P = op2;
break;
case 3:
op2 = read_nibble( saturn.PC + 1 );
load_constant( saturn.C, op2 + 1, saturn.PC + 2 );
saturn.PC += 3 + op2;
break;
case 4:
op2 = read_nibbles( saturn.PC + 1, 2 );
if ( op2 == 0x02 ) {
saturn.PC += 3;
} else if ( saturn.CARRY != 0 ) {
if ( op2 ) {
if ( op2 & 0x80 )
op2 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op2 + 1 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 3;
}
break;
case 5:
if ( saturn.CARRY == 0 ) {
op2 = read_nibbles( saturn.PC + 1, 2 );
if ( op2 ) {
if ( op2 & 0x80 )
op2 |= jumpmasks[ 2 ];
jumpaddr = ( saturn.PC + op2 + 1 ) & 0xfffff;
saturn.PC = jumpaddr;
} else {
saturn.PC = pop_return_addr();
}
} else {
saturn.PC += 3;
}
break;
case 6:
op2 = read_nibbles( saturn.PC + 1, 3 );
if ( op2 == 0x003 ) {
saturn.PC += 4;
} else if ( op2 == 0x004 ) {
op3 = read_nibbles( saturn.PC + 4, 1 );
saturn.PC += 5;
if ( op3 != 0 ) {
enter_debugger |= TRAP_INSTRUCTION;
return 1;
}
} else {
if ( op2 & 0x800 )
op2 |= jumpmasks[ 3 ];
jumpaddr = ( op2 + saturn.PC + 1 ) & 0xfffff;
saturn.PC = jumpaddr;
}
break;
case 7:
op2 = read_nibbles( saturn.PC + 1, 3 );
if ( op2 & 0x800 )
op2 |= jumpmasks[ 3 ];
jumpaddr = ( op2 + saturn.PC + 4 ) & 0xfffff;
push_return_addr( saturn.PC + 4 );
saturn.PC = jumpaddr;
break;
default:
stop = decode_8_thru_f( op0 );
break;
}
instructions++;
if ( stop ) {
enter_debugger |= ILLEGAL_INSTRUCTION;
}
return stop;
}
inline void schedule( void ) {
t1_t2_ticks ticks;
unsigned long steps;
static unsigned long old_stat_instr;
static unsigned long old_sched_instr;
steps = instructions - old_sched_instr;
old_sched_instr = instructions;
if ( ( sched_timer2 -= steps ) <= 0 ) {
if ( !saturn.intenable ) {
sched_timer2 = SCHED_TIMER2;
} else {
sched_timer2 = saturn.t2_tick;
}
saturn.t2_instr += steps;
if ( saturn.t2_ctrl & 0x01 ) {
saturn.timer2--;
}
if ( saturn.timer2 == 0 && ( saturn.t2_ctrl & 0x02 ) ) {
saturn.t2_ctrl |= 0x08;
do_interupt();
}
}
schedule_event = sched_timer2;
if ( device_check ) {
device_check = 0;
if ( ( sched_display -= steps ) <= 0 ) {
if ( device.display_touched )
device.display_touched -= steps;
if ( device.display_touched < 0 )
device.display_touched = 1;
}
check_devices();
sched_display = SCHED_NEVER;
if ( device.display_touched ) {
if ( device.display_touched < sched_display )
sched_display = device.display_touched - 1;
if ( sched_display < schedule_event )
schedule_event = sched_display;
}
}
if ( ( sched_receive -= steps ) <= 0 ) {
sched_receive = SCHED_RECEIVE;
if ( ( saturn.rcs & 0x01 ) == 0 ) {
receive_char();
}
}
if ( sched_receive < schedule_event )
schedule_event = sched_receive;
if ( ( sched_adjtime -= steps ) <= 0 ) {
sched_adjtime = SCHED_ADJTIME;
if ( saturn.PC < SrvcIoStart || saturn.PC > SrvcIoEnd ) {
ticks = get_t1_t2();
if ( saturn.t2_ctrl & 0x01 ) {
saturn.timer2 = ticks.t2_ticks;
}
if ( ( saturn.t2_ctrl & 0x08 ) == 0 && saturn.timer2 <= 0 ) {
if ( saturn.t2_ctrl & 0x02 ) {
saturn.t2_ctrl |= 0x08;
do_interupt();
}
}
adj_time_pending = 0;
saturn.timer1 = set_t1 - ticks.t1_ticks;
if ( ( saturn.t1_ctrl & 0x08 ) == 0 && saturn.timer1 <= 0 ) {
if ( saturn.t1_ctrl & 0x02 ) {
saturn.t1_ctrl |= 0x08;
do_interupt();
}
}
saturn.timer1 &= 0x0f;
} else {
adj_time_pending = 1;
}
}
if ( sched_adjtime < schedule_event )
schedule_event = sched_adjtime;
if ( ( sched_timer1 -= steps ) <= 0 ) {
if ( !saturn.intenable ) {
sched_timer1 = SCHED_TIMER1;
} else {
sched_timer1 = saturn.t1_tick;
}
saturn.t1_instr += steps;
saturn.timer1 = ( saturn.timer1 - 1 ) & 0xf;
if ( saturn.timer1 == 0 && ( saturn.t1_ctrl & 0x02 ) ) {
saturn.t1_ctrl |= 0x08;
do_interupt();
}
}
if ( sched_timer1 < schedule_event )
schedule_event = sched_timer1;
if ( ( sched_statistics -= steps ) <= 0 ) {
sched_statistics = SCHED_STATISTICS;
run = get_timer( RUN_TIMER );
#ifndef SIMPLE_64
s_1 = ( run.hi << 19 ) | ( run.lo >> 13 );
s_16 = ( run.hi << 23 ) | ( run.lo >> 9 );
#endif
delta_t_1 = s_1 - old_s_1;
delta_t_16 = s_16 - old_s_16;
old_s_1 = s_1;
old_s_16 = s_16;
delta_i = instructions - old_stat_instr;
old_stat_instr = instructions;
if ( delta_t_1 > 0 ) {
t1_i_per_tick = ( ( NR_SAMPLES - 1 ) * t1_i_per_tick +
( delta_i / delta_t_16 ) ) /
NR_SAMPLES;
t2_i_per_tick = t1_i_per_tick / 512;
saturn.i_per_s = ( ( NR_SAMPLES - 1 ) * saturn.i_per_s +
( delta_i / delta_t_1 ) ) /
NR_SAMPLES;
} else {
t1_i_per_tick = 8192;
t2_i_per_tick = 16;
}
saturn.t1_tick = t1_i_per_tick;
saturn.t2_tick = t2_i_per_tick;
}
if ( sched_statistics < schedule_event )
schedule_event = sched_statistics;
if ( ( sched_instr_rollover -= steps ) <= 0 ) {
sched_instr_rollover = SCHED_INSTR_ROLLOVER;
instructions = 1;
old_sched_instr = 1;
reset_timer( RUN_TIMER );
reset_timer( IDLE_TIMER );
start_timer( RUN_TIMER );
}
if ( sched_instr_rollover < schedule_event )
schedule_event = sched_instr_rollover;
schedule_event--;
if ( got_alarm ) {
got_alarm = 0;
#if defined( GUI_IS_X11 )
if ( disp.display_update )
refresh_display();
#endif
get_ui_event();
}
}
void emulate( void ) {
struct timeval tv;
struct timeval tv2;
struct timezone tz;
reset_timer( T1_TIMER );
reset_timer( RUN_TIMER );
reset_timer( IDLE_TIMER );
set_accesstime();
start_timer( T1_TIMER );
start_timer( RUN_TIMER );
sched_timer1 = t1_i_per_tick = saturn.t1_tick;
sched_timer2 = t2_i_per_tick = saturn.t2_tick;
set_t1 = saturn.timer1;
do {
step_instruction();
for ( int i = 0; i < ( int )( sizeof( saturn.keybuf.rows ) /
sizeof( saturn.keybuf.rows[ 0 ] ) );
i++ ) {
if ( saturn.keybuf.rows[ i ] || throttle ) {
/* Throttling speed if needed */
gettimeofday( &tv, &tz );
while ( ( tv.tv_sec == tv2.tv_sec ) &&
( ( tv.tv_usec - tv2.tv_usec ) < 2 ) ) {
gettimeofday( &tv, &tz );
}
tv2.tv_usec = tv.tv_usec;
tv2.tv_sec = tv.tv_sec;
break;
}
}
if ( schedule_event < 0 ) {
fprintf( stderr, "bug" );
schedule_event = 0;
}
if ( schedule_event-- <= 0 ) {
schedule();
}
} while ( !enter_debugger );
}