droid48-gwh/jni/emulate.c

2489 lines
70 KiB
C

/*
* This file is part of x48, an emulator of the HP-48sx Calculator.
* Copyright (C) 1994 Eddie C. Dost (ecd@dressler.de)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* $Log: emulate.c,v $
* Revision 1.16 1995/01/11 18:20:01 ecd
* major update to support HP48 G/GX
*
* Revision 1.15 1994/12/07 20:20:50 ecd
* fiddled around with sound
*
* Revision 1.15 1994/12/07 20:20:50 ecd
* fiddled around with sound
*
* Revision 1.14 1994/11/28 02:00:51 ecd
* added TRAP instruction 64001
* played with output register ...
* removed some unused switch statements
*
* Revision 1.13 1994/11/04 03:42:34 ecd
* changed includes
*
* Revision 1.12 1994/11/02 19:13:04 ecd
* fixed missing log
*
*
* $Id: emulate.c,v 1.16 1995/01/11 18:20:01 ecd Exp ecd $
*/
#include "global.h"
#include <stdio.h>
#include "hp48.h"
#include "hp48_emu.h"
#include "device.h"
#include "timer.h"
#include "debugger.h"
#if 0
#define DEBUG_TIMER
#define DEBUG_SCHED
#define DEBUG_DISP_SCHED
#endif
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
#ifdef __FunctionProto__
decode_group_80(void)
#else
decode_group_80()
#endif
{
int t, op3, op4, op5, op6;
unsigned char *REG;
long addr;
op3 = read_nibble(saturn.PC + 2);
//LOGI("-------%d", op3);
switch (op3) {
case 0: /* OUT=CS */
saturn.PC += 3;
copy_register(saturn.OUT, saturn.C, OUTS_FIELD);
#if 0
check_out_register();
#endif
return 0;
case 1: /* OUT=C */
saturn.PC += 3;
copy_register(saturn.OUT, saturn.C, OUT_FIELD);
#if 0
check_out_register();
#endif
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
#ifdef __FunctionProto__
decode_group_1(void)
#else
decode_group_1()
#endif
{
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;
}
}
inline int
#ifdef __FunctionProto__
decode_8_thru_f(int op1)
#else
decode_8_thru_f(op1)
int op1;
#endif
{
int op2, op3, op4, op5, op6;
op2 = read_nibble(saturn.PC + 1);
// LOGI("----- %d", op2);
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
#ifdef __FunctionProto__
step_instruction(void)
#else
step_instruction()
#endif
{
int op0, op1, op2, op3;
int stop = 0;
jumpaddr = 0;
op0 = read_nibble(saturn.PC);
// LOGI("----- %d", op0);
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;
}
// LOGI("-----2");
instructions++;
if (stop) {
enter_debugger |= ILLEGAL_INSTRUCTION;
}
return stop;
}
inline void
#ifdef __FunctionProto__
schedule(void)
#else
schedule()
#endif
{
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;
#ifdef DEBUG_SCHED
fprintf(stderr, "schedule called after %ld instructions\n", steps);
#endif
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;
#ifdef DEBUG_SCHED
fprintf(stderr, "next timer 2 step: %ld, event: %ld\n",
sched_timer2, schedule_event);
#endif
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;
#ifdef DEBUG_DISP_SCHED
fprintf(stderr, "check_device: disp_when %d, disp_touched %d\n",
sched_display, device.display_touched);
#endif
}
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;
#ifdef DEBUG_SCHED
fprintf(stderr, "next receive: %ld, event: %ld\n",
sched_receive, schedule_event);
#endif
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;
#ifdef DEBUG_SCHED
fprintf(stderr, "next adjtime: %ld, event: %ld\n",
sched_adjtime, schedule_event);
#endif
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;
#ifdef DEBUG_SCHED
fprintf(stderr, "next timer 1 step: %ld, event: %ld\n",
sched_timer1, schedule_event);
#endif
if ((sched_statistics -= steps) <= 0) {
sched_statistics = SCHED_STATISTICS;
run = get_timer(RUN_TIMER);
s_1 = (run.hi << 19) | (run.lo >> 13);
s_16 = (run.hi << 23) | (run.lo >> 9);
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;
#ifdef DEBUG_TIMER
if (delta_t_1 > 0) {
#if 0
fprintf(stderr, "I/s = %ld, T1 I/TICK = %d (%ld), T2 I/TICK = %d (%ld)\n",
saturn.i_per_s, saturn.t1_tick, t1_i_per_tick,
saturn.t2_tick, t2_i_per_tick);
#else
fprintf(stderr, "I/s = %ld, T1 I/TICK = %d, T2 I/TICK = %d (%ld)\n",
saturn.i_per_s, saturn.t1_tick, saturn.t2_tick, t2_i_per_tick);
#endif
}
#endif
}
if (sched_statistics < schedule_event)
schedule_event = sched_statistics;
#ifdef DEBUG_SCHED
fprintf(stderr, "next statistics: %ld, event: %ld\n",
sched_statistics, schedule_event);
#endif
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;
#ifdef DEBUG_SCHED
fprintf(stderr, "next instruction rollover: %ld, event: %ld\n",
sched_instr_rollover, schedule_event);
#endif
schedule_event--;
if (got_alarm) {
got_alarm = 0;
#ifdef HAVE_XSHM
if (disp.display_update) refresh_display();
#endif
GetEvent();
//usleep(500);
}
// GetEvent();
}
int
#ifdef __FunctionProto__
emulate(void)
#else
emulate()
#endif
{
struct timeval tv;
struct timeval tv2;
#ifndef SOLARIS
struct timezone tz;
#endif
reset_timer(T1_TIMER);
reset_timer(RUN_TIMER);
reset_timer(IDLE_TIMER);
start_timer(T1_TIMER);
set_accesstime();
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();
if (--schedule_event <= 0) {
schedule();
}
} while (exit_state);
*/
do {
step_instruction();
/* 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;
usleep(3);
*/
if (schedule_event-- == 0)
{
schedule();
}
} while (!enter_debugger); // exit_state
return 0;
}