/* * 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 #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 1 check_out_register(); #endif return 0; case 1: /* OUT=C */ saturn.PC += 3; copy_register(saturn.OUT, saturn.C, OUT_FIELD); #if 1 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_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 #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; }