#include #include #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_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_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 ); }