#include <stdio.h>
#include <unistd.h>

#include <sys/time.h>

#include "debugger.h" /* enter_debugger, TRAP_INSTRUCTION, ILLEGAL_INSTRUCTION */
#include "emulate.h"
#include "memory.h"
#include "options.h" /* throttle */
#include "registers.h"
#include "serial.h"
#include "timers.h"

#include "ui4x/common.h"

/* #define P_FIELD 0  /\* unused? *\/ */
/* #define WP_FIELD 1 /\* unused? *\/ */
/* #define XS_FIELD 2 /\* unused? *\/ */
/* #define X_FIELD 3  /\* unused? *\/ */
/* #define S_FIELD 4  /\* unused? *\/ */
/* #define M_FIELD 5  /\* unused? *\/ */
/* #define B_FIELD 6  /\* unused? *\/ */
#define W_FIELD 7
#define A_FIELD 15
#define IN_FIELD 16
#define OUT_FIELD 17
#define OUTS_FIELD 18

#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 NB_SAMPLES 10

device_t device;
int set_t1;
long schedule_event = 0;
long sched_adjtime = SCHED_ADJTIME;
bool adj_time_pending = false;
bool device_check = false;

saturn_t saturn;
bool sigalarm_triggered = false;
long sched_timer1 = SCHED_TIMER1;
long sched_timer2 = SCHED_TIMER2;
unsigned long t1_i_per_tick;
unsigned long t2_i_per_tick;

static bool interrupt_called = false;
static bool first_press = true; // PATCH

static unsigned long instructions = 0;

static unsigned long s_1 = 0;
static unsigned long s_16 = 0;
static unsigned long old_s_1 = 0;
static unsigned long old_s_16 = 0;
static unsigned long delta_t_1;
static unsigned long delta_t_16;
static unsigned long delta_i;

static long sched_instr_rollover = SCHED_INSTR_ROLLOVER;
static long sched_receive = SCHED_RECEIVE;
static long sched_statistics = SCHED_STATISTICS;

/* used in step_instruction_* */
static Address jumpmasks[] = { 0xffffffff, 0xfffffff0, 0xffffff00, 0xfffff000, 0xffff0000, 0xfff00000, 0xff000000, 0xf0000000 };
static short conf_tab[] = { 1, 2, 2, 2, 2, 0 };

static inline void push_return_addr( Address addr )
{
    if ( ++saturn.rstk_ptr >= NB_RSTK ) {
        for ( int i = 1; i < NB_RSTK; i++ )
            saturn.rstk[ i - 1 ] = saturn.rstk[ i ];

        saturn.rstk_ptr--;
    }
    saturn.rstk[ saturn.rstk_ptr ] = addr;
}

static inline long pop_return_addr( void )
{
    if ( saturn.rstk_ptr < 0 )
        return 0;

    return saturn.rstk[ saturn.rstk_ptr-- ];
}

static inline void do_in( void )
{
    int i, in = 0, out = 0;

    for ( i = 2; i >= 0; i-- ) {
        out <<= 4;
        out |= saturn.out[ i ];
    }

    for ( i = 0; i < KEYS_BUFFER_SIZE; i++ )
        if ( out & ( 1 << i ) )
            in |= saturn.keybuf[ i ];

    // PATCH
    // http://svn.berlios.de/wsvn/x48?op=comp&compare[]=/trunk@12&compare[]=/trunk@13
    // PAS TERRIBLE VISIBLEMENT

    if ( saturn.pc == 0x00E31 && !first_press &&
         ( ( out & 0x10 && in & 0x1 ) ||  // keys are Backspace
           ( out & 0x40 && in & 0x7 ) ||  // right, left & down
           ( out & 0x80 && in & 0x2 ) ) ) // up arrows
    {
        for ( i = 0; i < KEYS_BUFFER_SIZE; i++ )
            if ( out & ( 1 << i ) )
                saturn.keybuf[ i ] = 0;
        first_press = true;
    } else
        first_press = false;

    // FIN PATCH

    for ( i = 0; i < 4; i++ ) {
        saturn.in[ i ] = in & 0xf;
        in >>= 4;
    }
}

static inline int get_program_stat( int n ) { return saturn.pstat[ n ]; }

static inline void set_register_nibble( unsigned char* reg, int n, unsigned char val ) { reg[ n ] = val; }

static inline unsigned char get_register_nibble( unsigned char* reg, int n ) { return reg[ n ]; }

static inline void set_register_bit( unsigned char* reg, int n ) { reg[ n / 4 ] |= ( 1 << ( n % 4 ) ); }

static inline void clear_register_bit( unsigned char* reg, int n ) { reg[ n / 4 ] &= ~( 1 << ( n % 4 ) ); }

static inline int get_register_bit( unsigned char* reg, int n ) { return ( ( int )( reg[ n / 4 ] & ( 1 << ( n % 4 ) ) ) > 0 ) ? 1 : 0; }


static inline void load_constant( unsigned char* reg, int n, Address addr )
{
    int p = saturn.p;

    for ( int i = 0; i < n; i++ ) {
        reg[ p ] = bus_fetch_nibble( addr + i );
        p = ( p + 1 ) & 0xf;
    }
}

static inline void register_to_address( unsigned char* reg, Address* dat, int s )
{
    int n = ( s ) ? 4 : 5;

    for ( int i = 0; i < n; i++ ) {
        *dat &= ~nibble_masks[ i ];
        *dat |= ( reg[ i ] & 0x0f ) << ( i * 4 );
    }
}

static inline long dat_to_addr( unsigned char* dat )
{
    Address addr = 0;

    for ( int i = 4; i >= 0; i-- ) {
        addr <<= 4;
        addr |= ( dat[ i ] & 0xf );
    }

    return addr;
}

static inline void addr_to_dat( Address addr, unsigned char* dat )
{
    for ( int i = 0; i < 5; i++ ) {
        dat[ i ] = ( addr & 0xf );
        addr >>= 4;
    }
}

static inline void add_address( Address* dat, int add )
{
    *dat += add;

    saturn.carry = ( *dat & ( Address )0xfff00000 ) ? 1 : 0;

    *dat &= 0xfffff;
}

static inline void store( Address dat, unsigned char* reg, int code )
{
    int s = get_start( code );
    int e = get_end( code );

    for ( int i = s; i <= e; i++ )
        bus_write_nibble( dat++, reg[ i ] );
}

static inline void store_n( Address dat, unsigned char* reg, int n )
{
    for ( int i = 0; i < n; i++ )
        bus_write_nibble( dat++, reg[ i ] );
}

static inline void recall( unsigned char* reg, Address dat, int code )
{
    int s = get_start( code );
    int e = get_end( code );

    for ( int i = s; i <= e; i++ )
        reg[ i ] = read_nibble_crc( dat++ );
}

static inline void recall_n( unsigned char* reg, Address dat, int n )
{
    for ( int i = 0; i < n; i++ )
        reg[ i ] = read_nibble_crc( dat++ );
}

static bool step_instruction_00e( void )
{
    bool illegal_instruction = false;

    int op2 = bus_fetch_nibble( saturn.pc + 2 );

    switch ( bus_fetch_nibble( saturn.pc + 3 ) ) {
        case 0: /* A=A&B */
            saturn.pc += 4;
            and_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ B ], op2 );
            break;
        case 1: /* B=B&C */
            saturn.pc += 4;
            and_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ C ], op2 );
            break;
        case 2: /* C=C&A */
            saturn.pc += 4;
            and_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ A ], op2 );
            break;
        case 3: /* D=D&C */
            saturn.pc += 4;
            and_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ C ], op2 );
            break;
        case 4: /* B=B&A */
            saturn.pc += 4;
            and_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ A ], op2 );
            break;
        case 5: /* C=C&B */
            saturn.pc += 4;
            and_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ B ], op2 );
            break;
        case 6: /* A=A&C */
            saturn.pc += 4;
            and_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ C ], op2 );
            break;
        case 7: /* C=C&D */
            saturn.pc += 4;
            and_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ D ], op2 );
            break;
        case 8: /* A=A!B */
            saturn.pc += 4;
            or_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ B ], op2 );
            break;
        case 9: /* B=B!C */
            saturn.pc += 4;
            or_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ C ], op2 );
            break;
        case 0xa: /* C=C!A */
            saturn.pc += 4;
            or_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ A ], op2 );
            break;
        case 0xb: /* D=D!C */
            saturn.pc += 4;
            or_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ C ], op2 );
            break;
        case 0xc: /* B=B!A */
            saturn.pc += 4;
            or_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ A ], op2 );
            break;
        case 0xd: /* C=C!B */
            saturn.pc += 4;
            or_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ B ], op2 );
            break;
        case 0xe: /* A=A!C */
            saturn.pc += 4;
            or_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ C ], op2 );
            break;
        case 0xf: /* C=C!D */
            saturn.pc += 4;
            or_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ D ], op2 );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_00( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 1 ) ) {
        case 0: /* RTNSXM */
            saturn.st[ 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 */
            push_return_addr( dat_to_addr( saturn.reg[ C ] ) );
            saturn.pc += 2;
            break;
        case 7: /* C=RSTK */
            saturn.pc += 2;
            addr_to_dat( pop_return_addr(), saturn.reg[ C ] );
            break;
        case 8: /* CLRST */
            saturn.pc += 2;
            for ( int i = 0; i < 12; i++ )
                saturn.pstat[ i ] = 0;
            break;
        case 9: /* C=ST */
            saturn.pc += 2;
            for ( int i = 0; i < 12; i++ )
                if ( saturn.pstat[ i ] )
                    saturn.reg[ C ][ i / 4 ] |= 1 << ( i % 4 );
                else
                    saturn.reg[ C ][ i / 4 ] &= ~( 1 << ( i % 4 ) ) & 0xf;
            break;
        case 0xa: /* ST=C */
            saturn.pc += 2;
            for ( int i = 0; i < 12; i++ )
                saturn.pstat[ i ] = ( saturn.reg[ C ][ i / 4 ] >> ( i % 4 ) ) & 1;
            break;
        case 0xb: /* CSTEX */
            saturn.pc += 2;
            {
                int tmp;

                for ( int i = 0; i < 12; i++ ) {
                    tmp = saturn.pstat[ i ];
                    saturn.pstat[ i ] = ( saturn.reg[ C ][ i / 4 ] >> ( i % 4 ) ) & 1;
                    if ( tmp )
                        saturn.reg[ C ][ i / 4 ] |= 1 << ( i % 4 );
                    else
                        saturn.reg[ C ][ i / 4 ] &= ~( 1 << ( i % 4 ) ) & 0xf;
                }
            }
            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:
            illegal_instruction = step_instruction_00e();
            break;
        case 0xf: /* RTI */
            if ( saturn.int_pending ) {
                saturn.int_pending = false;
                saturn.interruptable = false;
                saturn.pc = 0xf;
            } else {
                saturn.pc = pop_return_addr();
                saturn.interruptable = true;

                if ( adj_time_pending ) {
                    schedule_event = 0;
                    sched_adjtime = 0;
                }
            }
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_010( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* saturn.reg_r[ 0 ]=A */
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 0 ], saturn.reg[ A ], W_FIELD );
            break;
        case 1: /* saturn.reg_r[ 1 ]=A */
        case 5:
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 1 ], saturn.reg[ A ], W_FIELD );
            break;
        case 2: /* saturn.reg_r[ 2 ]=A */
        case 6:
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 2 ], saturn.reg[ A ], W_FIELD );
            break;
        case 3: /* saturn.reg_r[ 3 ]=A */
        case 7:
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 3 ], saturn.reg[ A ], W_FIELD );
            break;
        case 4: /* saturn.reg_r[ 4 ]=A */
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 4 ], saturn.reg[ A ], W_FIELD );
            break;
        case 8: /* saturn.reg_r[ 0 ]=C */
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 0 ], saturn.reg[ C ], W_FIELD );
            break;
        case 9: /* saturn.reg_r[ 1 ]=C */
        case 0xd:
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 1 ], saturn.reg[ C ], W_FIELD );
            break;
        case 0xa: /* saturn.reg_r[ 2 ]=C */
        case 0xe:
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 2 ], saturn.reg[ C ], W_FIELD );
            break;
        case 0xb: /* saturn.reg_r[ 3 ]=C */
        case 0xf:
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 3 ], saturn.reg[ C ], W_FIELD );
            break;
        case 0xc: /* saturn.reg_r[ 4 ]=C */
            saturn.pc += 3;
            copy_register( saturn.reg_r[ 4 ], saturn.reg[ C ], W_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_011( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* A=R0 */
            saturn.pc += 3;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 0 ], W_FIELD );
            break;
        case 1: /* A=R1 */
        case 5:
            saturn.pc += 3;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 1 ], W_FIELD );
            break;
        case 2: /* A=R2 */
        case 6:
            saturn.pc += 3;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 2 ], W_FIELD );
            break;
        case 3: /* A=R3 */
        case 7:
            saturn.pc += 3;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 3 ], W_FIELD );
            break;
        case 4: /* A=R4 */
            saturn.pc += 3;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 4 ], W_FIELD );
            break;
        case 8: /* C=R0 */
            saturn.pc += 3;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 0 ], W_FIELD );
            break;
        case 9: /* C=R1 */
        case 0xd:
            saturn.pc += 3;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 1 ], W_FIELD );
            break;
        case 0xa: /* C=R2 */
        case 0xe:
            saturn.pc += 3;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 2 ], W_FIELD );
            break;
        case 0xb: /* C=R3 */
        case 0xf:
            saturn.pc += 3;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 3 ], W_FIELD );
            break;
        case 0xc: /* C=R4 */
            saturn.pc += 3;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 4 ], W_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_012( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* AR0EX */
            saturn.pc += 3;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 0 ], W_FIELD );
            break;
        case 1: /* AR1EX */
        case 5:
            saturn.pc += 3;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 1 ], W_FIELD );
            break;
        case 2: /* AR2EX */
        case 6:
            saturn.pc += 3;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 2 ], W_FIELD );
            break;
        case 3: /* AR3EX */
        case 7:
            saturn.pc += 3;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 3 ], W_FIELD );
            break;
        case 4: /* AR4EX */
            saturn.pc += 3;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 4 ], W_FIELD );
            break;
        case 8: /* CR0EX */
            saturn.pc += 3;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 0 ], W_FIELD );
            break;
        case 9: /* CR1EX */
        case 0xd:
            saturn.pc += 3;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 1 ], W_FIELD );
            break;
        case 0xa: /* CR2EX */
        case 0xe:
            saturn.pc += 3;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 2 ], W_FIELD );
            break;
        case 0xb: /* CR3EX */
        case 0xf:
            saturn.pc += 3;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 3 ], W_FIELD );
            break;
        case 0xc: /* CR4EX */
            saturn.pc += 3;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 4 ], W_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_013( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* D0=A */
            saturn.pc += 3;
            register_to_address( saturn.reg[ A ], &saturn.d[0], 0 );
            break;
        case 1: /* D1=A */
            saturn.pc += 3;
            register_to_address( saturn.reg[ A ], &saturn.d[1], 0 );
            break;
        case 2: /* AD0EX */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ A ], &saturn.d[0], A_FIELD );
            break;
        case 3: /* AD1EX */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ A ], &saturn.d[1], A_FIELD );
            break;
        case 4: /* D0=C */
            saturn.pc += 3;
            register_to_address( saturn.reg[ C ], &saturn.d[0], 0 );
            break;
        case 5: /* D1=C */
            saturn.pc += 3;
            register_to_address( saturn.reg[ C ], &saturn.d[1], 0 );
            break;
        case 6: /* CD0EX */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ C ], &saturn.d[0], A_FIELD );
            break;
        case 7: /* CD1EX */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ C ], &saturn.d[1], A_FIELD );
            break;
        case 8: /* D0=AS */
            saturn.pc += 3;
            register_to_address( saturn.reg[ A ], &saturn.d[0], 1 );
            break;
        case 9: /* saturn.d[1]=AS */
            saturn.pc += 3;
            register_to_address( saturn.reg[ A ], &saturn.d[1], 1 );
            break;
        case 0xa: /* AD0XS */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ A ], &saturn.d[0], IN_FIELD );
            break;
        case 0xb: /* AD1XS */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ A ], &saturn.d[1], IN_FIELD );
            break;
        case 0xc: /* D0=CS */
            saturn.pc += 3;
            register_to_address( saturn.reg[ C ], &saturn.d[0], 1 );
            break;
        case 0xd: /* D1=CS */
            saturn.pc += 3;
            register_to_address( saturn.reg[ C ], &saturn.d[1], 1 );
            break;
        case 0xe: /* CD0XS */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ C ], &saturn.d[0], IN_FIELD );
            break;
        case 0xf: /* CD1XS */
            saturn.pc += 3;
            exchange_reg( saturn.reg[ C ], &saturn.d[1], IN_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_014( void )
{
    bool illegal_instruction = false;

    int op3 = bus_fetch_nibble( saturn.pc + 2 );
    int opX = op3 < 8 ? 0xf : 6;
    switch ( op3 & 7 ) {
        case 0: /* DAT0=A */
            saturn.pc += 3;
            store( saturn.d[0], saturn.reg[ A ], opX );
            break;
        case 1: /* DAT1=A */
            saturn.pc += 3;
            store( saturn.d[1], saturn.reg[ A ], opX );
            break;
        case 2: /* A=DAT0 */
            saturn.pc += 3;
            recall( saturn.reg[ A ], saturn.d[0], opX );
            break;
        case 3: /* A=DAT1 */
            saturn.pc += 3;
            recall( saturn.reg[ A ], saturn.d[1], opX );
            break;
        case 4: /* DAT0=C */
            saturn.pc += 3;
            store( saturn.d[0], saturn.reg[ C ], opX );
            break;
        case 5: /* DAT1=C */
            saturn.pc += 3;
            store( saturn.d[1], saturn.reg[ C ], opX );
            break;
        case 6: /* C=DAT0 */
            saturn.pc += 3;
            recall( saturn.reg[ C ], saturn.d[0], opX );
            break;
        case 7: /* C=DAT1 */
            saturn.pc += 3;
            recall( saturn.reg[ C ], saturn.d[1], opX );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_015( void )
{
    bool illegal_instruction = false;

    int op3 = bus_fetch_nibble( saturn.pc + 2 );
    int op4 = bus_fetch_nibble( saturn.pc + 3 );
    if ( op3 >= 8 ) {
        switch ( op3 & 7 ) {
            case 0: /* DAT0=A */
                saturn.pc += 4;
                store_n( saturn.d[0], saturn.reg[ A ], op4 + 1 );
                break;
            case 1: /* DAT1=A */
                saturn.pc += 4;
                store_n( saturn.d[1], saturn.reg[ A ], op4 + 1 );
                break;
            case 2: /* A=DAT0 */
                saturn.pc += 4;
                recall_n( saturn.reg[ A ], saturn.d[0], op4 + 1 );
                break;
            case 3: /* A=DAT1 */
                saturn.pc += 4;
                recall_n( saturn.reg[ A ], saturn.d[1], op4 + 1 );
                break;
            case 4: /* DAT0=C */
                saturn.pc += 4;
                store_n( saturn.d[0], saturn.reg[ C ], op4 + 1 );
                break;
            case 5: /* DAT1=C */
                saturn.pc += 4;
                store_n( saturn.d[1], saturn.reg[ C ], op4 + 1 );
                break;
            case 6: /* C=DAT0 */
                saturn.pc += 4;
                recall_n( saturn.reg[ C ], saturn.d[0], op4 + 1 );
                break;
            case 7: /* C=DAT1 */
                saturn.pc += 4;
                recall_n( saturn.reg[ C ], saturn.d[1], op4 + 1 );
                break;
            default:
                illegal_instruction = true;
        }
    } else {
        switch ( op3 ) {
            case 0: /* DAT0=A */
                saturn.pc += 4;
                store( saturn.d[0], saturn.reg[ A ], op4 );
                break;
            case 1: /* DAT1=A */
                saturn.pc += 4;
                store( saturn.d[1], saturn.reg[ A ], op4 );
                break;
            case 2: /* A=DAT0 */
                saturn.pc += 4;
                recall( saturn.reg[ A ], saturn.d[0], op4 );
                break;
            case 3: /* A=DAT1 */
                saturn.pc += 4;
                recall( saturn.reg[ A ], saturn.d[1], op4 );
                break;
            case 4: /* DAT0=C */
                saturn.pc += 4;
                store( saturn.d[0], saturn.reg[ C ], op4 );
                break;
            case 5: /* DAT1=C */
                saturn.pc += 4;
                store( saturn.d[1], saturn.reg[ C ], op4 );
                break;
            case 6: /* C=DAT0 */
                saturn.pc += 4;
                recall( saturn.reg[ C ], saturn.d[0], op4 );
                break;
            case 7: /* C=DAT1 */
                saturn.pc += 4;
                recall( saturn.reg[ C ], saturn.d[1], op4 );
                break;
            default:
                illegal_instruction = true;
        }
    }

    return illegal_instruction;
}

static bool step_instruction_01( void )
{
    bool illegal_instruction = false;
    int op3;

    switch ( bus_fetch_nibble( saturn.pc + 1 ) ) {
        case 0:
            illegal_instruction = step_instruction_010();
            break;
        case 1:
            illegal_instruction = step_instruction_011();
            break;
        case 2:
            illegal_instruction = step_instruction_012();
            break;
        case 3:
            illegal_instruction = step_instruction_013();
            break;
        case 4:
            illegal_instruction = step_instruction_014();
            break;
        case 5:
            illegal_instruction = step_instruction_015();
            break;
        case 6:
            op3 = bus_fetch_nibble( saturn.pc + 2 );
            saturn.pc += 3;
            add_address( &saturn.d[0], op3 + 1 );
            break;
        case 7:
            op3 = bus_fetch_nibble( saturn.pc + 2 );
            saturn.pc += 3;
            add_address( &saturn.d[1], op3 + 1 );
            break;
        case 8:
            op3 = bus_fetch_nibble( saturn.pc + 2 );
            saturn.pc += 3;
            add_address( &saturn.d[0], -( op3 + 1 ) );
            break;
        case 9:
            load_addr( &saturn.d[0], saturn.pc + 2, 2 );
            saturn.pc += 4;
            break;
        case 0xa:
            load_addr( &saturn.d[0], saturn.pc + 2, 4 );
            saturn.pc += 6;
            break;
        case 0xb:
            load_addr( &saturn.d[0], saturn.pc + 2, 5 );
            saturn.pc += 7;
            break;
        case 0xc:
            op3 = bus_fetch_nibble( saturn.pc + 2 );
            saturn.pc += 3;
            add_address( &saturn.d[1], -( op3 + 1 ) );
            break;
        case 0xd:
            load_addr( &saturn.d[1], saturn.pc + 2, 2 );
            saturn.pc += 4;
            break;
        case 0xe:
            load_addr( &saturn.d[1], saturn.pc + 2, 4 );
            saturn.pc += 6;
            break;
        case 0xf:
            load_addr( &saturn.d[1], saturn.pc + 2, 5 );
            saturn.pc += 7;
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_02( void )
{
    bool illegal_instruction = false;

    int op2 = bus_fetch_nibble( saturn.pc + 1 );
    saturn.pc += 2;
    saturn.p = op2;

    return illegal_instruction;
}

static bool step_instruction_03( void )
{
    bool illegal_instruction = false;

    int op2 = bus_fetch_nibble( saturn.pc + 1 );
    load_constant( saturn.reg[ C ], op2 + 1, saturn.pc + 2 );
    saturn.pc += 3 + op2;

    return illegal_instruction;
}

static bool step_instruction_04( void )
{
    bool illegal_instruction = false;

    int 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 ];
                saturn.pc = ( saturn.pc + op2 + 1 ) & 0xfffff;
            } else
                saturn.pc = pop_return_addr();
        } else
            saturn.pc += 3;
    }

    return illegal_instruction;
}

static bool step_instruction_05( void )
{
    bool illegal_instruction = false;

    if ( saturn.carry == 0 ) {
        int op2 = read_nibbles( saturn.pc + 1, 2 );
        if ( op2 ) {
            if ( op2 & 0x80 )
                op2 |= jumpmasks[ 2 ];
            saturn.pc = ( saturn.pc + op2 + 1 ) & 0xfffff;
        } else
            saturn.pc = pop_return_addr();
    } else
        saturn.pc += 3;

    return illegal_instruction;
}

static bool step_instruction_06( void )
{
    bool illegal_instruction = false;

    int op2 = read_nibbles( saturn.pc + 1, 3 );
    if ( op2 == 0x003 )
        saturn.pc += 4;
    else {
        if ( op2 == 0x004 ) {
            int op3 = read_nibbles( saturn.pc + 4, 1 );
            saturn.pc += 5;
            if ( op3 != 0 ) {
                enter_debugger |= TRAP_INSTRUCTION;
                illegal_instruction = true;
                return illegal_instruction;
            }
        } else {
            if ( op2 & 0x800 )
                op2 |= jumpmasks[ 3 ];
            saturn.pc = ( op2 + saturn.pc + 1 ) & 0xfffff;
        }
    }

    return illegal_instruction;
}

static bool step_instruction_07( void )
{
    bool illegal_instruction = false;

    int op2 = read_nibbles( saturn.pc + 1, 3 );
    if ( op2 & 0x800 )
        op2 |= jumpmasks[ 3 ];
    push_return_addr( saturn.pc + 4 );
    saturn.pc = ( op2 + saturn.pc + 4 ) & 0xfffff;

    return illegal_instruction;
}

static bool step_instruction_08a( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* ?A=B */
            saturn.carry = is_equal_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 1: /* ?B=C */
            saturn.carry = is_equal_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 2: /* ?A=C */
            saturn.carry = is_equal_register( saturn.reg[ A ], saturn.reg[ C ], A_FIELD );
            break;
        case 3: /* ?C=D */
            saturn.carry = is_equal_register( saturn.reg[ C ], saturn.reg[ D ], A_FIELD );
            break;
        case 4: /* ?A#B */
            saturn.carry = is_not_equal_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 5: /* ?B#C */
            saturn.carry = is_not_equal_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 6: /* ?A#C */
            saturn.carry = is_not_equal_register( saturn.reg[ A ], saturn.reg[ C ], A_FIELD );
            break;
        case 7: /* ?C#D */
            saturn.carry = is_not_equal_register( saturn.reg[ C ], saturn.reg[ D ], A_FIELD );
            break;
        case 8: /* ?A=0 */
            saturn.carry = is_zero_register( saturn.reg[ A ], A_FIELD );
            break;
        case 9: /* ?B=0 */
            saturn.carry = is_zero_register( saturn.reg[ B ], A_FIELD );
            break;
        case 0xa: /* ?C=0 */
            saturn.carry = is_zero_register( saturn.reg[ C ], A_FIELD );
            break;
        case 0xb: /* ?D=0 */
            saturn.carry = is_zero_register( saturn.reg[ D ], A_FIELD );
            break;
        case 0xc: /* ?A#0 */
            saturn.carry = is_not_zero_register( saturn.reg[ A ], A_FIELD );
            break;
        case 0xd: /* ?B#0 */
            saturn.carry = is_not_zero_register( saturn.reg[ B ], A_FIELD );
            break;
        case 0xe: /* ?C#0 */
            saturn.carry = is_not_zero_register( saturn.reg[ C ], A_FIELD );
            break;
        case 0xf: /* ?D#0 */
            saturn.carry = is_not_zero_register( saturn.reg[ D ], A_FIELD );
            break;
        default:
            illegal_instruction = true;
    }
    if ( saturn.carry ) {
        saturn.pc += 3;

        int op3 = read_nibbles( saturn.pc, 2 );

        if ( op3 ) {
            if ( op3 & 0x80 )
                op3 |= jumpmasks[ 2 ];
            saturn.pc = ( saturn.pc + op3 ) & 0xfffff;
        } else
            saturn.pc = pop_return_addr();
    } else
        saturn.pc += 5;

    return illegal_instruction;
}

static bool step_instruction_0808( void )
{
    bool illegal_instruction = false;

    int op4 = bus_fetch_nibble( saturn.pc + 3 );
    int op5;

    switch ( op4 ) {
        case 0: /* INTON */
            saturn.pc += 4;
            saturn.kbd_ien = true;
            break;
        case 1: /* RSI... */
            op5 = bus_fetch_nibble( saturn.pc + 4 );
            saturn.pc += 5;
            {
                saturn.kbd_ien = true;
                int gen_intr = 0;
                for ( int i = 0; i < KEYS_BUFFER_SIZE; i++ ) {
                    if ( saturn.keybuf[ i ] != 0 ) {
                        gen_intr = 1;
                        break;
                    }
                }
                if ( gen_intr )
                    do_kbd_int();
            }
            break;
        case 2: /* LA... */
            op5 = bus_fetch_nibble( saturn.pc + 4 );
            load_constant( saturn.reg[ A ], op5 + 1, saturn.pc + 5 );
            saturn.pc += 6 + op5;
            break;
        case 3: /* BUSCB */
            saturn.pc += 4;
            break;
        case 4: /* ABIT=0 */
            op5 = bus_fetch_nibble( saturn.pc + 4 );
            saturn.pc += 5;
            clear_register_bit( saturn.reg[ A ], op5 );
            break;
        case 5: /* ABIT=1 */
            op5 = bus_fetch_nibble( saturn.pc + 4 );
            saturn.pc += 5;
            set_register_bit( saturn.reg[ A ], op5 );
            break;
        case 8: /* CBIT=0 */
            op5 = bus_fetch_nibble( saturn.pc + 4 );
            saturn.pc += 5;
            clear_register_bit( saturn.reg[ C ], op5 );
            break;
        case 9: /* CBIT=1 */
            op5 = bus_fetch_nibble( saturn.pc + 4 );
            saturn.pc += 5;
            set_register_bit( saturn.reg[ C ], op5 );
            break;
        case 6:   /* ?ABIT=0 */
        case 7:   /* ?ABIT=1 */
        case 0xa: /* ?CBIT=0 */
        case 0xb: /* ?CBIT=1 */
            op5 = bus_fetch_nibble( saturn.pc + 4 );
            saturn.carry = ( get_register_bit( ( op4 < 8 ) ? saturn.reg[ A ] : saturn.reg[ C ], op5 ) == ( ( op4 == 6 || op4 == 0xa ) ? 0 : 1 ) ) ? 1 : 0;
            if ( saturn.carry ) {
                saturn.pc += 5;
                int op6 = read_nibbles( saturn.pc, 2 );
                if ( op6 ) {
                    if ( op6 & 0x80 )
                        op6 |= jumpmasks[ 2 ];
                    saturn.pc = ( saturn.pc + op6 ) & 0xfffff;
                } else
                    saturn.pc = pop_return_addr();
            } else
                saturn.pc += 7;
            break;
        case 0xc: /* PC=(A) */
            saturn.pc = read_nibbles( dat_to_addr( saturn.reg[ A ] ), 5 );
            break;
        case 0xd: /* BUSCD */
            saturn.pc += 4;
            break;
        case 0xe: /* PC=(C) */
            saturn.pc = read_nibbles( dat_to_addr( saturn.reg[ C ] ), 5 );
            break;
        case 0xf: /* INTOFF */
            saturn.pc += 4;
            saturn.kbd_ien = false;
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_080( void )
{
    bool illegal_instruction = false;

    int op4;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* OUT=CS */
            saturn.pc += 3;
            copy_register( saturn.out, saturn.reg[ C ], OUTS_FIELD );
            break;
        case 1: /* OUT=C */
            saturn.pc += 3;
            copy_register( saturn.out, saturn.reg[ C ], OUT_FIELD );
            break;
        case 2: /* A=IN */
            saturn.pc += 3;
            do_in();
            copy_register( saturn.reg[ A ], saturn.in, IN_FIELD );
            break;
        case 3: /* C=IN */
            saturn.pc += 3;
            do_in();
            copy_register( saturn.reg[ C ], saturn.in, IN_FIELD );
            break;
        case 4: /* UNCNFG */
            saturn.pc += 3;
            {
                int i;
                unsigned int conf = 0;

                for ( i = 4; i >= 0; i-- ) {
                    conf <<= 4;
                    conf |= saturn.reg[ C ][ i ];
                }

                for ( i = 0; i < 6; i++ ) {
                    if ( saturn.mem_cntl[ i ].config[ 0 ] == conf ) {
                        saturn.mem_cntl[ i ].unconfigured = conf_tab[ i ];

                        saturn.mem_cntl[ i ].config[ 0 ] = 0x0;
                        saturn.mem_cntl[ i ].config[ 1 ] = 0x0;
                        break;
                    }
                }
            }
            break;
        case 5: /* CONFIG */
            saturn.pc += 3;
            {
                int i;
                unsigned long conf = 0;

                for ( i = 4; i >= 0; i-- ) {
                    conf <<= 4;
                    conf |= saturn.reg[ C ][ i ];
                }

                for ( i = 0; i < 6; i++ ) {
                    if ( saturn.mem_cntl[ i ].unconfigured ) {
                        saturn.mem_cntl[ i ].unconfigured--;
                        saturn.mem_cntl[ i ].config[ saturn.mem_cntl[ i ].unconfigured ] = conf;
                        break;
                    }
                }
            }
            break;
        case 6: /* C=ID */
            saturn.pc += 3;
            {
                int i;
                static int chip_id[] = { 0, 0, 0, 0, 0x05, 0xf6, 0x07, 0xf8, 0x01, 0xf2, 0, 0 };

                for ( i = 0; i < 6; i++ )
                    if ( saturn.mem_cntl[ i ].unconfigured )
                        break;

                int id = ( i < 6 ) ? chip_id[ 2 * i + ( 2 - saturn.mem_cntl[ i ].unconfigured ) ] : 0;

                for ( i = 0; i < 3; i++ ) {
                    saturn.reg[ C ][ i ] = id & 0x0f;
                    id >>= 4;
                }
            }
            break;
        case 7: /* SHUTDN */
            saturn.pc += 3;
            if ( config.inhibit_shutdown )
                break;

            stop_timer( RUN_TIMER );
            start_timer( IDLE_TIMER );

            if ( is_zero_register( saturn.out, OUT_FIELD ) ) {
                saturn.interruptable = true;
                saturn.int_pending = false;
            }

            {
                bool wake = in_debugger;
                t1_t2_ticks ticks;

                do {
                    pause();

                    if ( sigalarm_triggered ) {
                        sigalarm_triggered = false;

                        ui_update_display();

                        ticks = get_t1_t2();
                        if ( saturn.t2_ctrl & 0x01 )
                            saturn.timer2 = ticks.t2_ticks;

                        saturn.timer1 = set_t1 - ticks.t1_ticks;
                        set_t1 = ticks.t1_ticks;

                        interrupt_called = false;
                        ui_get_event();
                        if ( interrupt_called )
                            wake = true;

                        if ( saturn.timer2 <= 0 ) {
                            if ( saturn.t2_ctrl & 0x04 )
                                wake = true;

                            if ( saturn.t2_ctrl & 0x02 ) {
                                wake = true;
                                saturn.t2_ctrl |= 0x08;
                                do_interupt();
                            }
                        }

                        if ( saturn.timer1 <= 0 ) {
                            saturn.timer1 &= 0x0f;
                            if ( saturn.t1_ctrl & 0x04 )
                                wake = true;

                            if ( saturn.t1_ctrl & 0x03 ) {
                                wake = true;
                                saturn.t1_ctrl |= 0x08;
                                do_interupt();
                            }
                        }

                        if ( !wake ) {
                            interrupt_called = false;
                            receive_char();
                            if ( interrupt_called )
                                wake = true;
                        }
                    }

                    if ( enter_debugger )
                        wake = true;
                } while ( !wake );

                stop_timer( IDLE_TIMER );
                start_timer( RUN_TIMER );
            }
            break;
        case 8:
            illegal_instruction = step_instruction_0808();
            break;
        case 9: /* C+P+1 */
            saturn.pc += 3;
            add_p_plus_one( saturn.reg[ C ] );
            break;
        case 0xa: /* RESET */
            saturn.pc += 3;
            for ( int i = 0; i < 6; i++ ) {
                saturn.mem_cntl[ i ].unconfigured = conf_tab[ i ];

                saturn.mem_cntl[ i ].config[ 0 ] = 0x0;
                saturn.mem_cntl[ i ].config[ 1 ] = 0x0;
            }
            break;
        case 0xb: /* BUSCC */
            saturn.pc += 3;
            break;
        case 0xc: /* C=P n */
            op4 = bus_fetch_nibble( saturn.pc + 3 );
            saturn.pc += 4;
            set_register_nibble( saturn.reg[ C ], op4, saturn.p );
            break;
        case 0xd: /* P=C n */
            op4 = bus_fetch_nibble( saturn.pc + 3 );
            saturn.pc += 4;
            saturn.p = get_register_nibble( saturn.reg[ C ], op4 );
            break;
        case 0xe: /* SREQ? */
            saturn.pc += 3;
            saturn.reg[ C ][ 0 ] = 0;
            saturn.st[ SR ] = 0;
            break;
        case 0xf: /* CPEX n */
            op4 = bus_fetch_nibble( saturn.pc + 3 );
            saturn.pc += 4;
            {
                int tmp = get_register_nibble( saturn.reg[ C ], op4 );
                set_register_nibble( saturn.reg[ C ], op4, saturn.p );
                saturn.p = tmp;
            }
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_0818( void )
{
    bool illegal_instruction = false;

    int op3 = bus_fetch_nibble( saturn.pc + 3 );
    int op5 = bus_fetch_nibble( saturn.pc + 5 );

    int op4 = bus_fetch_nibble( saturn.pc + 4 );
    if ( op4 < 8 ) { /* PLUS */
        switch ( op4 & 3 ) {
            case 0: /* A=A+CON */
                saturn.pc += 6;
                add_register_constant( saturn.reg[ A ], op3, op5 + 1 );
                break;
            case 1: /* B=B+CON */
                saturn.pc += 6;
                add_register_constant( saturn.reg[ B ], op3, op5 + 1 );
                break;
            case 2: /* C=C+CON */
                saturn.pc += 6;
                add_register_constant( saturn.reg[ C ], op3, op5 + 1 );
                break;
            case 3: /* D=D+CON */
                saturn.pc += 6;
                add_register_constant( saturn.reg[ D ], op3, op5 + 1 );
                break;
            default:
                illegal_instruction = true;
        }
    } else { /* MINUS */
        switch ( op4 & 3 ) {
            case 0: /* A=A-CON */
                saturn.pc += 6;
                sub_register_constant( saturn.reg[ A ], op3, op5 + 1 );
                break;
            case 1: /* B=B-CON */
                saturn.pc += 6;
                sub_register_constant( saturn.reg[ B ], op3, op5 + 1 );
                break;
            case 2: /* C=C-CON */
                saturn.pc += 6;
                sub_register_constant( saturn.reg[ C ], op3, op5 + 1 );
                break;
            case 3: /* D=D-CON */
                saturn.pc += 6;
                sub_register_constant( saturn.reg[ D ], op3, op5 + 1 );
                break;
            default:
                illegal_instruction = true;
        }
    }

    return illegal_instruction;
}

static bool step_instruction_0819( void )
{
    bool illegal_instruction = false;

    int op3 = bus_fetch_nibble( saturn.pc + 3 );
    int op4 = bus_fetch_nibble( saturn.pc + 4 );
    switch ( op4 & 3 ) {
        case 0:
            saturn.pc += 5;
            shift_right_bit_register( saturn.reg[ A ], op3 );
            break;
        case 1:
            saturn.pc += 5;
            shift_right_bit_register( saturn.reg[ B ], op3 );
            break;
        case 2:
            saturn.pc += 5;
            shift_right_bit_register( saturn.reg[ C ], op3 );
            break;
        case 3:
            saturn.pc += 5;
            shift_right_bit_register( saturn.reg[ D ], op3 );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_081a0( void )
{
    bool illegal_instruction = false;
    int op3 = bus_fetch_nibble( saturn.pc + 3 );

    switch ( bus_fetch_nibble( saturn.pc + 5 ) ) {
        case 0: /* saturn.reg_r[ 0 ]=A */
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 0 ], saturn.reg[ A ], op3 );
            break;
        case 1: /* saturn.reg_r[ 1 ]=A */
        case 5:
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 1 ], saturn.reg[ A ], op3 );
            break;
        case 2: /* saturn.reg_r[ 2 ]=A */
        case 6:
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 2 ], saturn.reg[ A ], op3 );
            break;
        case 3: /* saturn.reg_r[ 3 ]=A */
        case 7:
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 3 ], saturn.reg[ A ], op3 );
            break;
        case 4: /* saturn.reg_r[ 4 ]=A */
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 4 ], saturn.reg[ A ], op3 );
            break;
        case 8: /* saturn.reg_r[ 0 ]=C */
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 0 ], saturn.reg[ C ], op3 );
            break;
        case 9: /* saturn.reg_r[ 1 ]=C */
        case 0xd:
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 1 ], saturn.reg[ C ], op3 );
            break;
        case 0xa: /* saturn.reg_r[ 2 ]=C */
        case 0xe:
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 2 ], saturn.reg[ C ], op3 );
            break;
        case 0xb: /* saturn.reg_r[ 3 ]=C */
        case 0xf:
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 3 ], saturn.reg[ C ], op3 );
            break;
        case 0xc: /* saturn.reg_r[ 4 ]=C */
            saturn.pc += 6;
            copy_register( saturn.reg_r[ 4 ], saturn.reg[ C ], op3 );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_081a1( void )
{
    bool illegal_instruction = false;
    int op3 = bus_fetch_nibble( saturn.pc + 3 );

    switch ( bus_fetch_nibble( saturn.pc + 5 ) ) {
        case 0: /* A=R0 */
            saturn.pc += 6;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 0 ], op3 );
            break;
        case 1: /* A=R1 */
        case 5:
            saturn.pc += 6;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 1 ], op3 );
            break;
        case 2: /* A=R2 */
        case 6:
            saturn.pc += 6;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 2 ], op3 );
            break;
        case 3: /* A=R3 */
        case 7:
            saturn.pc += 6;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 3 ], op3 );
            break;
        case 4: /* A=R4 */
            saturn.pc += 6;
            copy_register( saturn.reg[ A ], saturn.reg_r[ 4 ], op3 );
            break;
        case 8: /* C=R0 */
            saturn.pc += 6;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 0 ], op3 );
            break;
        case 9: /* C=R1 */
        case 0xd:
            saturn.pc += 6;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 1 ], op3 );
            break;
        case 0xa: /* C=R2 */
        case 0xe:
            saturn.pc += 6;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 2 ], op3 );
            break;
        case 0xb: /* C=R3 */
        case 0xf:
            saturn.pc += 6;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 3 ], op3 );
            break;
        case 0xc: /* C=R4 */
            saturn.pc += 6;
            copy_register( saturn.reg[ C ], saturn.reg_r[ 4 ], op3 );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_081a2( void )
{
    bool illegal_instruction = false;
    int op3 = bus_fetch_nibble( saturn.pc + 3 );

    switch ( bus_fetch_nibble( saturn.pc + 5 ) ) {
        case 0: /* AR0EX */
            saturn.pc += 6;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 0 ], op3 );
            break;
        case 1: /* AR1EX */
        case 5:
            saturn.pc += 6;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 1 ], op3 );
            break;
        case 2: /* AR2EX */
        case 6:
            saturn.pc += 6;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 2 ], op3 );
            break;
        case 3: /* AR3EX */
        case 7:
            saturn.pc += 6;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 3 ], op3 );
            break;
        case 4: /* AR4EX */
            saturn.pc += 6;
            exchange_register( saturn.reg[ A ], saturn.reg_r[ 4 ], op3 );
            break;
        case 8: /* CR0EX */
            saturn.pc += 6;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 0 ], op3 );
            break;
        case 9: /* CR1EX */
        case 0xd:
            saturn.pc += 6;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 1 ], op3 );
            break;
        case 0xa: /* CR2EX */
        case 0xe:
            saturn.pc += 6;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 2 ], op3 );
            break;
        case 0xb: /* CR3EX */
        case 0xf:
            saturn.pc += 6;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 3 ], op3 );
            break;
        case 0xc: /* CR4EX */
            saturn.pc += 6;
            exchange_register( saturn.reg[ C ], saturn.reg_r[ 4 ], op3 );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_081a( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 4 ) ) {
        case 0:
            illegal_instruction = step_instruction_081a0();
            break;
        case 1:
            illegal_instruction = step_instruction_081a1();
            break;
        case 2:
            illegal_instruction = step_instruction_081a2();
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_081b( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 3 ) ) {
        case 2: /* PC=A */
            saturn.pc = dat_to_addr( saturn.reg[ A ] );
            break;
        case 3: /* PC=C */
            saturn.pc = dat_to_addr( saturn.reg[ C ] );
            break;
        case 4: /* A=PC */
            saturn.pc += 4;
            addr_to_dat( saturn.pc, saturn.reg[ A ] );
            break;
        case 5: /* C=PC */
            saturn.pc += 4;
            addr_to_dat( saturn.pc, saturn.reg[ C ] );
            break;
        case 6: /* APCEX */
            saturn.pc += 4;
            addr_to_dat( saturn.pc, saturn.reg[ A ] );
            saturn.pc = dat_to_addr( saturn.reg[ A ] );
            break;
        case 7: /* CPCEX */
            saturn.pc += 4;
            addr_to_dat( saturn.pc, saturn.reg[ C ] );
            saturn.pc = dat_to_addr( saturn.reg[ C ] );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_081( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* ASLC */
            saturn.pc += 3;
            shift_left_circ_register( saturn.reg[ A ], W_FIELD );
            break;
        case 1: /* BSLC */
            saturn.pc += 3;
            shift_left_circ_register( saturn.reg[ B ], W_FIELD );
            break;
        case 2: /* CSLC */
            saturn.pc += 3;
            shift_left_circ_register( saturn.reg[ C ], W_FIELD );
            break;
        case 3: /* DSLC */
            saturn.pc += 3;
            shift_left_circ_register( saturn.reg[ D ], W_FIELD );
            break;
        case 4: /* ASRC */
            saturn.pc += 3;
            shift_right_circ_register( saturn.reg[ A ], W_FIELD );
            break;
        case 5: /* BSRC */
            saturn.pc += 3;
            shift_right_circ_register( saturn.reg[ B ], W_FIELD );
            break;
        case 6: /* CSRC */
            saturn.pc += 3;
            shift_right_circ_register( saturn.reg[ C ], W_FIELD );
            break;
        case 7: /* DSRC */
            saturn.pc += 3;
            shift_right_circ_register( saturn.reg[ D ], W_FIELD );
            break;
        case 8: /* R = R +/- CON */
            illegal_instruction = step_instruction_0818();
            break;
        case 9: /* R SRB FIELD */
            illegal_instruction = step_instruction_0819();
            break;
        case 0xa: /* R = R FIELD, etc. */
            illegal_instruction = step_instruction_081a();
            break;
        case 0xb:
            illegal_instruction = step_instruction_081b();
            break;
        case 0xc: /* ASRB */
            saturn.pc += 3;
            shift_right_bit_register( saturn.reg[ A ], W_FIELD );
            break;
        case 0xd: /* BSRB */
            saturn.pc += 3;
            shift_right_bit_register( saturn.reg[ B ], W_FIELD );
            break;
        case 0xe: /* CSRB */
            saturn.pc += 3;
            shift_right_bit_register( saturn.reg[ C ], W_FIELD );
            break;
        case 0xf: /* DSRB */
            saturn.pc += 3;
            shift_right_bit_register( saturn.reg[ D ], W_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_08b( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 2 ) ) {
        case 0: /* ?A>B */
            saturn.carry = is_greater_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 1: /* ?B>C */
            saturn.carry = is_greater_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 2: /* ?C>A */
            saturn.carry = is_greater_register( saturn.reg[ C ], saturn.reg[ A ], A_FIELD );
            break;
        case 3: /* ?D>C */
            saturn.carry = is_greater_register( saturn.reg[ D ], saturn.reg[ C ], A_FIELD );
            break;
        case 4: /* ?A<B */
            saturn.carry = is_less_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 5: /* ?B<C */
            saturn.carry = is_less_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 6: /* ?C<A */
            saturn.carry = is_less_register( saturn.reg[ C ], saturn.reg[ A ], A_FIELD );
            break;
        case 7: /* ?D<C */
            saturn.carry = is_less_register( saturn.reg[ D ], saturn.reg[ C ], A_FIELD );
            break;
        case 8: /* ?A>=B */
            saturn.carry = is_greater_or_equal_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 9: /* ?B>=C */
            saturn.carry = is_greater_or_equal_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xa: /* ?C>=A */
            saturn.carry = is_greater_or_equal_register( saturn.reg[ C ], saturn.reg[ A ], A_FIELD );
            break;
        case 0xb: /* ?D>=C */
            saturn.carry = is_greater_or_equal_register( saturn.reg[ D ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xc: /* ?A<=B */
            saturn.carry = is_less_or_equal_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 0xd: /* ?B<=C */
            saturn.carry = is_less_or_equal_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xe: /* ?C<=A */
            saturn.carry = is_less_or_equal_register( saturn.reg[ C ], saturn.reg[ A ], A_FIELD );
            break;
        case 0xf: /* ?D<=C */
            saturn.carry = is_less_or_equal_register( saturn.reg[ D ], saturn.reg[ C ], A_FIELD );
            break;
        default:
            illegal_instruction = true;
    }
    if ( saturn.carry ) {
        saturn.pc += 3;

        int op3 = read_nibbles( saturn.pc, 2 );

        if ( op3 ) {
            if ( op3 & 0x80 )
                op3 |= jumpmasks[ 2 ];
            saturn.pc = ( saturn.pc + op3 ) & 0xfffff;
        } else
            saturn.pc = pop_return_addr();
    } else
        saturn.pc += 5;

    return illegal_instruction;
}

static bool step_instruction_08( void )
{
    bool illegal_instruction = false;
    int op1 = bus_fetch_nibble( saturn.pc + 1 );
    int op2, op3;

    switch ( op1 ) {
        case 0:
            illegal_instruction = step_instruction_080();
            break;
        case 1:
            illegal_instruction = step_instruction_081();
            break;
        case 2:
            op2 = bus_fetch_nibble( saturn.pc + 2 );
            saturn.pc += 3;
            if ( op2 & 1 )
                saturn.st[ XM ] = 0;
            if ( op2 & 2 )
                saturn.st[ SB ] = 0;
            if ( op2 & 4 )
                saturn.st[ SR ] = 0;
            if ( op2 & 8 )
                saturn.st[ MP ] = 0;
            break;
        case 3:
            op2 = bus_fetch_nibble( saturn.pc + 2 );
            saturn.carry = 1;
            if ( op2 & 1 )
                if ( saturn.st[ XM ] != 0 )
                    saturn.carry = 0;
            if ( op2 & 2 )
                if ( saturn.st[ SB ] != 0 )
                    saturn.carry = 0;
            if ( op2 & 4 )
                if ( saturn.st[ SR ] != 0 )
                    saturn.carry = 0;
            if ( op2 & 8 )
                if ( saturn.st[ MP ] != 0 )
                    saturn.carry = 0;

            if ( saturn.carry ) {
                saturn.pc += 3;
                op3 = read_nibbles( saturn.pc, 2 );
                if ( op3 ) {
                    if ( op3 & 0x80 )
                        op3 |= jumpmasks[ 2 ];
                    saturn.pc = ( saturn.pc + op3 ) & 0xfffff;
                } else
                    saturn.pc = pop_return_addr();
            } else
                saturn.pc += 5;
            break;
        case 4:
        case 5:
            op2 = bus_fetch_nibble( saturn.pc + 2 );
            saturn.pc += 3;
            saturn.pstat[ op2 ] = ( op1 == 4 ) ? 0 : 1;
            break;
        case 6:
        case 7:
            op2 = bus_fetch_nibble( saturn.pc + 2 );
            if ( op1 == 6 )
                saturn.carry = ( get_program_stat( op2 ) == 0 ) ? 1 : 0;
            else
                saturn.carry = ( get_program_stat( op2 ) != 0 ) ? 1 : 0;
            if ( saturn.carry ) {
                saturn.pc += 3;
                op3 = read_nibbles( saturn.pc, 2 );
                if ( op3 ) {
                    if ( op3 & 0x80 )
                        op3 |= jumpmasks[ 2 ];
                    saturn.pc = ( saturn.pc + op3 ) & 0xfffff;
                } else
                    saturn.pc = pop_return_addr();
            } else
                saturn.pc += 5;
            break;
        case 8:
        case 9:
            op2 = bus_fetch_nibble( saturn.pc + 2 );
            if ( op1 == 8 )
                saturn.carry = ( saturn.p != op2 ) ? 1 : 0;
            else
                saturn.carry = ( saturn.p == op2 ) ? 1 : 0;
            if ( saturn.carry ) {
                saturn.pc += 3;
                op3 = read_nibbles( saturn.pc, 2 );
                if ( op3 ) {
                    if ( op3 & 0x80 )
                        op3 |= jumpmasks[ 2 ];
                    saturn.pc = ( saturn.pc + op3 ) & 0xfffff;
                } else
                    saturn.pc = pop_return_addr();
            } else
                saturn.pc += 5;
            break;
        case 0xa:
            illegal_instruction = step_instruction_08a();
            break;
        case 0xb:
            illegal_instruction = step_instruction_08b();
            break;
        case 0xc:
            op2 = read_nibbles( saturn.pc + 2, 4 );
            if ( op2 & 0x8000 )
                op2 |= jumpmasks[ 4 ];
            saturn.pc = ( saturn.pc + op2 + 2 ) & 0xfffff;
            break;
        case 0xd:
            op2 = read_nibbles( saturn.pc + 2, 5 );
            saturn.pc = op2;
            break;
        case 0xe:
            op2 = read_nibbles( saturn.pc + 2, 4 );
            if ( op2 & 0x8000 )
                op2 |= jumpmasks[ 4 ];
            push_return_addr( saturn.pc + 6 );
            saturn.pc = ( saturn.pc + op2 + 6 ) & 0xfffff;
            break;
        case 0xf:
            op2 = read_nibbles( saturn.pc + 2, 5 );
            push_return_addr( saturn.pc + 7 );
            saturn.pc = op2;
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_09( void )
{
    bool illegal_instruction = false;
    int op1 = bus_fetch_nibble( saturn.pc + 1 );
    int op2 = bus_fetch_nibble( saturn.pc + 2 );

    if ( op1 < 8 ) {
        switch ( op2 ) {
            case 0: /* ?A=B */
                saturn.carry = is_equal_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 1: /* ?B=C */
                saturn.carry = is_equal_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 2: /* ?A=C */
                saturn.carry = is_equal_register( saturn.reg[ A ], saturn.reg[ C ], op1 );
                break;
            case 3: /* ?C=D */
                saturn.carry = is_equal_register( saturn.reg[ C ], saturn.reg[ D ], op1 );
                break;
            case 4: /* ?A#B */
                saturn.carry = is_not_equal_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 5: /* ?B#C */
                saturn.carry = is_not_equal_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 6: /* ?A#C */
                saturn.carry = is_not_equal_register( saturn.reg[ A ], saturn.reg[ C ], op1 );
                break;
            case 7: /* ?C#D */
                saturn.carry = is_not_equal_register( saturn.reg[ C ], saturn.reg[ D ], op1 );
                break;
            case 8: /* ?A=0 */
                saturn.carry = is_zero_register( saturn.reg[ A ], op1 );
                break;
            case 9: /* ?B=0 */
                saturn.carry = is_zero_register( saturn.reg[ B ], op1 );
                break;
            case 0xa: /* ?C=0 */
                saturn.carry = is_zero_register( saturn.reg[ C ], op1 );
                break;
            case 0xb: /* ?D=0 */
                saturn.carry = is_zero_register( saturn.reg[ D ], op1 );
                break;
            case 0xc: /* ?A#0 */
                saturn.carry = is_not_zero_register( saturn.reg[ A ], op1 );
                break;
            case 0xd: /* ?B#0 */
                saturn.carry = is_not_zero_register( saturn.reg[ B ], op1 );
                break;
            case 0xe: /* ?C#0 */
                saturn.carry = is_not_zero_register( saturn.reg[ C ], op1 );
                break;
            case 0xf: /* ?D#0 */
                saturn.carry = is_not_zero_register( saturn.reg[ D ], op1 );
                break;
            default:
                illegal_instruction = true;
        }
    } else {
        op1 &= 7;
        switch ( op2 ) {
            case 0: /* ?A>B */
                saturn.carry = is_greater_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 1: /* ?B>C */
                saturn.carry = is_greater_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 2: /* ?C>A */
                saturn.carry = is_greater_register( saturn.reg[ C ], saturn.reg[ A ], op1 );
                break;
            case 3: /* ?D>C */
                saturn.carry = is_greater_register( saturn.reg[ D ], saturn.reg[ C ], op1 );
                break;
            case 4: /* ?A<B */
                saturn.carry = is_less_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 5: /* ?B<C */
                saturn.carry = is_less_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 6: /* ?C<A */
                saturn.carry = is_less_register( saturn.reg[ C ], saturn.reg[ A ], op1 );
                break;
            case 7: /* ?D<C */
                saturn.carry = is_less_register( saturn.reg[ D ], saturn.reg[ C ], op1 );
                break;
            case 8: /* ?A>=B */
                saturn.carry = is_greater_or_equal_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 9: /* ?B>=C */
                saturn.carry = is_greater_or_equal_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 0xa: /* ?C>=A */
                saturn.carry = is_greater_or_equal_register( saturn.reg[ C ], saturn.reg[ A ], op1 );
                break;
            case 0xb: /* ?D>=C */
                saturn.carry = is_greater_or_equal_register( saturn.reg[ D ], saturn.reg[ C ], op1 );
                break;
            case 0xc: /* ?A<=B */
                saturn.carry = is_less_or_equal_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 0xd: /* ?B<=C */
                saturn.carry = is_less_or_equal_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 0xe: /* ?C<=A */
                saturn.carry = is_less_or_equal_register( saturn.reg[ C ], saturn.reg[ A ], op1 );
                break;
            case 0xf: /* ?D<=C */
                saturn.carry = is_less_or_equal_register( saturn.reg[ D ], saturn.reg[ C ], op1 );
                break;
            default:
                illegal_instruction = true;
        }
    }
    if ( saturn.carry ) {
        saturn.pc += 3;

        int op3 = read_nibbles( saturn.pc, 2 );

        if ( op3 ) {
            if ( op3 & 0x80 )
                op3 |= jumpmasks[ 2 ];
            saturn.pc = ( saturn.pc + op3 ) & 0xfffff;
        } else {
            saturn.pc = pop_return_addr();
        }
    } else {
        saturn.pc += 5;
    }

    return illegal_instruction;
}

static bool step_instruction_0a( void )
{
    bool illegal_instruction = false;
    int op1 = bus_fetch_nibble( saturn.pc + 1 );
    int op2 = bus_fetch_nibble( saturn.pc + 2 );

    if ( op1 < 8 ) {
        switch ( op2 ) {
            case 0: /* A=A+B */
                saturn.pc += 3;
                add_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 1: /* B=B+C */
                saturn.pc += 3;
                add_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 2: /* C=C+A */
                saturn.pc += 3;
                add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ A ], op1 );
                break;
            case 3: /* D=D+C */
                saturn.pc += 3;
                add_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ C ], op1 );
                break;
            case 4: /* A=A+A */
                saturn.pc += 3;
                add_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ A ], op1 );
                break;
            case 5: /* B=B+B */
                saturn.pc += 3;
                add_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ B ], op1 );
                break;
            case 6: /* C=C+C */
                saturn.pc += 3;
                add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ C ], op1 );
                break;
            case 7: /* D=D+D */
                saturn.pc += 3;
                add_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ D ], op1 );
                break;
            case 8: /* B=B+A */
                saturn.pc += 3;
                add_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ A ], op1 );
                break;
            case 9: /* C=C+B */
                saturn.pc += 3;
                add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ B ], op1 );
                break;
            case 0xa: /* A=A+C */
                saturn.pc += 3;
                add_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ C ], op1 );
                break;
            case 0xb: /* C=C+D */
                saturn.pc += 3;
                add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ D ], op1 );
                break;
            case 0xc: /* A=A-1 */
                saturn.pc += 3;
                dec_register( saturn.reg[ A ], op1 );
                break;
            case 0xd: /* B=B-1 */
                saturn.pc += 3;
                dec_register( saturn.reg[ B ], op1 );
                break;
            case 0xe: /* C=C-1 */
                saturn.pc += 3;
                dec_register( saturn.reg[ C ], op1 );
                break;
            case 0xf: /* D=D-1 */
                saturn.pc += 3;
                dec_register( saturn.reg[ D ], op1 );
                break;
            default:
                illegal_instruction = true;
        }
    } else {
        op1 &= 7;
        switch ( op2 ) {
            case 0: /* A=0 */
                saturn.pc += 3;
                zero_register( saturn.reg[ A ], op1 );
                break;
            case 1: /* B=0 */
                saturn.pc += 3;
                zero_register( saturn.reg[ B ], op1 );
                break;
            case 2: /* C=0 */
                saturn.pc += 3;
                zero_register( saturn.reg[ C ], op1 );
                break;
            case 3: /* D=0 */
                saturn.pc += 3;
                zero_register( saturn.reg[ D ], op1 );
                break;
            case 4: /* A=B */
                saturn.pc += 3;
                copy_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 5: /* B=C */
                saturn.pc += 3;
                copy_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 6: /* C=A */
                saturn.pc += 3;
                copy_register( saturn.reg[ C ], saturn.reg[ A ], op1 );
                break;
            case 7: /* D=C */
                saturn.pc += 3;
                copy_register( saturn.reg[ D ], saturn.reg[ C ], op1 );
                break;
            case 8: /* B=A */
                saturn.pc += 3;
                copy_register( saturn.reg[ B ], saturn.reg[ A ], op1 );
                break;
            case 9: /* C=B */
                saturn.pc += 3;
                copy_register( saturn.reg[ C ], saturn.reg[ B ], op1 );
                break;
            case 0xa: /* A=C */
                saturn.pc += 3;
                copy_register( saturn.reg[ A ], saturn.reg[ C ], op1 );
                break;
            case 0xb: /* C=D */
                saturn.pc += 3;
                copy_register( saturn.reg[ C ], saturn.reg[ D ], op1 );
                break;
            case 0xc: /* ABEX */
                saturn.pc += 3;
                exchange_register( saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 0xd: /* BCEX */
                saturn.pc += 3;
                exchange_register( saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 0xe: /* ACEX */
                saturn.pc += 3;
                exchange_register( saturn.reg[ A ], saturn.reg[ C ], op1 );
                break;
            case 0xf: /* CDEX */
                saturn.pc += 3;
                exchange_register( saturn.reg[ C ], saturn.reg[ D ], op1 );
                break;
            default:
                illegal_instruction = true;
        }
    }

    return illegal_instruction;
}

static bool step_instruction_0b( void )
{
    bool illegal_instruction = false;
    int op1 = bus_fetch_nibble( saturn.pc + 1 );
    int op2 = bus_fetch_nibble( saturn.pc + 2 );

    if ( op1 < 8 ) {
        switch ( op2 ) {
            case 0: /* A=A-B */
                saturn.pc += 3;
                sub_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ B ], op1 );
                break;
            case 1: /* B=B-C */
                saturn.pc += 3;
                sub_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ C ], op1 );
                break;
            case 2: /* C=C-A */
                saturn.pc += 3;
                sub_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ A ], op1 );
                break;
            case 3: /* D=D-C */
                saturn.pc += 3;
                sub_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ C ], op1 );
                break;
            case 4: /* A=A+1 */
                saturn.pc += 3;
                inc_register( saturn.reg[ A ], op1 );
                break;
            case 5: /* B=B+1 */
                saturn.pc += 3;
                inc_register( saturn.reg[ B ], op1 );
                break;
            case 6: /* C=C+1 */
                saturn.pc += 3;
                inc_register( saturn.reg[ C ], op1 );
                break;
            case 7: /* D=D+1 */
                saturn.pc += 3;
                inc_register( saturn.reg[ D ], op1 );
                break;
            case 8: /* B=B-A */
                saturn.pc += 3;
                sub_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ A ], op1 );
                break;
            case 9: /* C=C-B */
                saturn.pc += 3;
                sub_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ B ], op1 );
                break;
            case 0xa: /* A=A-C */
                saturn.pc += 3;
                sub_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ C ], op1 );
                break;
            case 0xb: /* C=C-D */
                saturn.pc += 3;
                sub_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ D ], op1 );
                break;
            case 0xc: /* A=B-A */
                saturn.pc += 3;
                sub_register( saturn.reg[ A ], saturn.reg[ B ], saturn.reg[ A ], op1 );
                break;
            case 0xd: /* B=C-B */
                saturn.pc += 3;
                sub_register( saturn.reg[ B ], saturn.reg[ C ], saturn.reg[ B ], op1 );
                break;
            case 0xe: /* C=A-C */
                saturn.pc += 3;
                sub_register( saturn.reg[ C ], saturn.reg[ A ], saturn.reg[ C ], op1 );
                break;
            case 0xf: /* D=C-D */
                saturn.pc += 3;
                sub_register( saturn.reg[ D ], saturn.reg[ C ], saturn.reg[ D ], op1 );
                break;
            default:
                illegal_instruction = true;
        }
    } else {
        op1 &= 7;
        switch ( op2 ) {
            case 0: /* ASL */
                saturn.pc += 3;
                shift_left_register( saturn.reg[ A ], op1 );
                break;
            case 1: /* BSL */
                saturn.pc += 3;
                shift_left_register( saturn.reg[ B ], op1 );
                break;
            case 2: /* CSL */
                saturn.pc += 3;
                shift_left_register( saturn.reg[ C ], op1 );
                break;
            case 3: /* DSL */
                saturn.pc += 3;
                shift_left_register( saturn.reg[ D ], op1 );
                break;
            case 4: /* ASR */
                saturn.pc += 3;
                shift_right_register( saturn.reg[ A ], op1 );
                break;
            case 5: /* BSR */
                saturn.pc += 3;
                shift_right_register( saturn.reg[ B ], op1 );
                break;
            case 6: /* CSR */
                saturn.pc += 3;
                shift_right_register( saturn.reg[ C ], op1 );
                break;
            case 7: /* DSR */
                saturn.pc += 3;
                shift_right_register( saturn.reg[ D ], op1 );
                break;
            case 8: /* A=-A */
                saturn.pc += 3;
                complement_2_register( saturn.reg[ A ], op1 );
                break;
            case 9: /* B=-B */
                saturn.pc += 3;
                complement_2_register( saturn.reg[ B ], op1 );
                break;
            case 0xa: /* C=-C */
                saturn.pc += 3;
                complement_2_register( saturn.reg[ C ], op1 );
                break;
            case 0xb: /* D=-D */
                saturn.pc += 3;
                complement_2_register( saturn.reg[ D ], op1 );
                break;
            case 0xc: /* A=-A-1 */
                saturn.pc += 3;
                complement_1_register( saturn.reg[ A ], op1 );
                break;
            case 0xd: /* B=-B-1 */
                saturn.pc += 3;
                complement_1_register( saturn.reg[ B ], op1 );
                break;
            case 0xe: /* C=-C-1 */
                saturn.pc += 3;
                complement_1_register( saturn.reg[ C ], op1 );
                break;
            case 0xf: /* D=-D-1 */
                saturn.pc += 3;
                complement_1_register( saturn.reg[ D ], op1 );
                break;
            default:
                illegal_instruction = true;
        }
    }

    return illegal_instruction;
}

static bool step_instruction_0c( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 1 ) ) {
        case 0: /* A=A+B */
            saturn.pc += 2;
            add_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 1: /* B=B+C */
            saturn.pc += 2;
            add_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 2: /* C=C+A */
            saturn.pc += 2;
            add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ A ], A_FIELD );
            break;
        case 3: /* D=D+C */
            saturn.pc += 2;
            add_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ C ], A_FIELD );
            break;
        case 4: /* A=A+A */
            saturn.pc += 2;
            add_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ A ], A_FIELD );
            break;
        case 5: /* B=B+B */
            saturn.pc += 2;
            add_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ B ], A_FIELD );
            break;
        case 6: /* C=C+C */
            saturn.pc += 2;
            add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ C ], A_FIELD );
            break;
        case 7: /* D=D+D */
            saturn.pc += 2;
            add_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ D ], A_FIELD );
            break;
        case 8: /* B=B+A */
            saturn.pc += 2;
            add_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ A ], A_FIELD );
            break;
        case 9: /* C=C+B */
            saturn.pc += 2;
            add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ B ], A_FIELD );
            break;
        case 0xa: /* A=A+C */
            saturn.pc += 2;
            add_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xb: /* C=C+D */
            saturn.pc += 2;
            add_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ D ], A_FIELD );
            break;
        case 0xc: /* A=A-1 */
            saturn.pc += 2;
            dec_register( saturn.reg[ A ], A_FIELD );
            break;
        case 0xd: /* B=B-1 */
            saturn.pc += 2;
            dec_register( saturn.reg[ B ], A_FIELD );
            break;
        case 0xe: /* C=C-1 */
            saturn.pc += 2;
            dec_register( saturn.reg[ C ], A_FIELD );
            break;
        case 0xf: /* D=D-1 */
            saturn.pc += 2;
            dec_register( saturn.reg[ D ], A_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_0d( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 1 ) ) {
        case 0: /* A=0 */
            saturn.pc += 2;
            zero_register( saturn.reg[ A ], A_FIELD );
            break;
        case 1: /* B=0 */
            saturn.pc += 2;
            zero_register( saturn.reg[ B ], A_FIELD );
            break;
        case 2: /* C=0 */
            saturn.pc += 2;
            zero_register( saturn.reg[ C ], A_FIELD );
            break;
        case 3: /* D=0 */
            saturn.pc += 2;
            zero_register( saturn.reg[ D ], A_FIELD );
            break;
        case 4: /* A=B */
            saturn.pc += 2;
            copy_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 5: /* B=C */
            saturn.pc += 2;
            copy_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 6: /* C=A */
            saturn.pc += 2;
            copy_register( saturn.reg[ C ], saturn.reg[ A ], A_FIELD );
            break;
        case 7: /* D=C */
            saturn.pc += 2;
            copy_register( saturn.reg[ D ], saturn.reg[ C ], A_FIELD );
            break;
        case 8: /* B=A */
            saturn.pc += 2;
            copy_register( saturn.reg[ B ], saturn.reg[ A ], A_FIELD );
            break;
        case 9: /* C=B */
            saturn.pc += 2;
            copy_register( saturn.reg[ C ], saturn.reg[ B ], A_FIELD );
            break;
        case 0xa: /* A=C */
            saturn.pc += 2;
            copy_register( saturn.reg[ A ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xb: /* C=D */
            saturn.pc += 2;
            copy_register( saturn.reg[ C ], saturn.reg[ D ], A_FIELD );
            break;
        case 0xc: /* ABEX */
            saturn.pc += 2;
            exchange_register( saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 0xd: /* BCEX */
            saturn.pc += 2;
            exchange_register( saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xe: /* ACEX */
            saturn.pc += 2;
            exchange_register( saturn.reg[ A ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xf: /* CDEX */
            saturn.pc += 2;
            exchange_register( saturn.reg[ C ], saturn.reg[ D ], A_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_0e( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 1 ) ) {
        case 0: /* A=A-B */
            saturn.pc += 2;
            sub_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ B ], A_FIELD );
            break;
        case 1: /* B=B-C */
            saturn.pc += 2;
            sub_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ C ], A_FIELD );
            break;
        case 2: /* C=C-A */
            saturn.pc += 2;
            sub_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ A ], A_FIELD );
            break;
        case 3: /* D=D-C */
            saturn.pc += 2;
            sub_register( saturn.reg[ D ], saturn.reg[ D ], saturn.reg[ C ], A_FIELD );
            break;
        case 4: /* A=A+1 */
            saturn.pc += 2;
            inc_register( saturn.reg[ A ], A_FIELD );
            break;
        case 5: /* B=B+1 */
            saturn.pc += 2;
            inc_register( saturn.reg[ B ], A_FIELD );
            break;
        case 6: /* C=C+1 */
            saturn.pc += 2;
            inc_register( saturn.reg[ C ], A_FIELD );
            break;
        case 7: /* D=D+1 */
            saturn.pc += 2;
            inc_register( saturn.reg[ D ], A_FIELD );
            break;
        case 8: /* B=B-A */
            saturn.pc += 2;
            sub_register( saturn.reg[ B ], saturn.reg[ B ], saturn.reg[ A ], A_FIELD );
            break;
        case 9: /* C=C-B */
            saturn.pc += 2;
            sub_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ B ], A_FIELD );
            break;
        case 0xa: /* A=A-C */
            saturn.pc += 2;
            sub_register( saturn.reg[ A ], saturn.reg[ A ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xb: /* C=C-D */
            saturn.pc += 2;
            sub_register( saturn.reg[ C ], saturn.reg[ C ], saturn.reg[ D ], A_FIELD );
            break;
        case 0xc: /* A=B-A */
            saturn.pc += 2;
            sub_register( saturn.reg[ A ], saturn.reg[ B ], saturn.reg[ A ], A_FIELD );
            break;
        case 0xd: /* B=C-B */
            saturn.pc += 2;
            sub_register( saturn.reg[ B ], saturn.reg[ C ], saturn.reg[ B ], A_FIELD );
            break;
        case 0xe: /* C=A-C */
            saturn.pc += 2;
            sub_register( saturn.reg[ C ], saturn.reg[ A ], saturn.reg[ C ], A_FIELD );
            break;
        case 0xf: /* D=C-D */
            saturn.pc += 2;
            sub_register( saturn.reg[ D ], saturn.reg[ C ], saturn.reg[ D ], A_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

static bool step_instruction_0f( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc + 1 ) ) {
        case 0: /* ASL */
            saturn.pc += 2;
            shift_left_register( saturn.reg[ A ], A_FIELD );
            break;
        case 1: /* BSL */
            saturn.pc += 2;
            shift_left_register( saturn.reg[ B ], A_FIELD );
            break;
        case 2: /* CSL */
            saturn.pc += 2;
            shift_left_register( saturn.reg[ C ], A_FIELD );
            break;
        case 3: /* DSL */
            saturn.pc += 2;
            shift_left_register( saturn.reg[ D ], A_FIELD );
            break;
        case 4: /* ASR */
            saturn.pc += 2;
            shift_right_register( saturn.reg[ A ], A_FIELD );
            break;
        case 5: /* BSR */
            saturn.pc += 2;
            shift_right_register( saturn.reg[ B ], A_FIELD );
            break;
        case 6: /* CSR */
            saturn.pc += 2;
            shift_right_register( saturn.reg[ C ], A_FIELD );
            break;
        case 7: /* DSR */
            saturn.pc += 2;
            shift_right_register( saturn.reg[ D ], A_FIELD );
            break;
        case 8: /* A=-A */
            saturn.pc += 2;
            complement_2_register( saturn.reg[ A ], A_FIELD );
            break;
        case 9: /* B=-B */
            saturn.pc += 2;
            complement_2_register( saturn.reg[ B ], A_FIELD );
            break;
        case 0xa: /* C=-C */
            saturn.pc += 2;
            complement_2_register( saturn.reg[ C ], A_FIELD );
            break;
        case 0xb: /* D=-D */
            saturn.pc += 2;
            complement_2_register( saturn.reg[ D ], A_FIELD );
            break;
        case 0xc: /* A=-A-1 */
            saturn.pc += 2;
            complement_1_register( saturn.reg[ A ], A_FIELD );
            break;
        case 0xd: /* B=-B-1 */
            saturn.pc += 2;
            complement_1_register( saturn.reg[ B ], A_FIELD );
            break;
        case 0xe: /* C=-C-1 */
            saturn.pc += 2;
            complement_1_register( saturn.reg[ C ], A_FIELD );
            break;
        case 0xf: /* D=-D-1 */
            saturn.pc += 2;
            complement_1_register( saturn.reg[ D ], A_FIELD );
            break;
        default:
            illegal_instruction = true;
    }

    return illegal_instruction;
}

/**********/
/* public */
/**********/
void do_interupt( void )
{
    interrupt_called = true;
    if ( saturn.interruptable ) {
        push_return_addr( saturn.pc );
        saturn.pc = 0xf;
        saturn.interruptable = false;
    }
}

void do_kbd_int( void )
{
    do_interupt();
    if ( !saturn.interruptable )
        saturn.int_pending = true;
}

void load_addr( Address* dat, Address addr, int n )
{
    for ( int i = 0; i < n; i++ ) {
        *dat &= ~nibble_masks[ i ];
        *dat |= bus_fetch_nibble( addr + i ) << ( i * 4 );
    }
}

void step_instruction( void )
{
    bool illegal_instruction = false;

    switch ( bus_fetch_nibble( saturn.pc ) ) {
        case 0:
            illegal_instruction = step_instruction_00();
            break;
        case 1:
            illegal_instruction = step_instruction_01();
            break;
        case 2:
            illegal_instruction = step_instruction_02();
            break;
        case 3:
            illegal_instruction = step_instruction_03();
            break;
        case 4:
            illegal_instruction = step_instruction_04();
            break;
        case 5:
            illegal_instruction = step_instruction_05();
            break;
        case 6:
            illegal_instruction = step_instruction_06();
            break;
        case 7:
            illegal_instruction = step_instruction_07();
            break;
        case 8:
            illegal_instruction = step_instruction_08();
            break;
        case 9:
            illegal_instruction = step_instruction_09();
            break;
        case 0xa:
            illegal_instruction = step_instruction_0a();
            break;
        case 0xb:
            illegal_instruction = step_instruction_0b();
            break;
        case 0xc:
            illegal_instruction = step_instruction_0c();
            break;
        case 0xd:
            illegal_instruction = step_instruction_0d();
            break;
        case 0xe:
            illegal_instruction = step_instruction_0e();
            break;
        case 0xf:
            illegal_instruction = step_instruction_0f();
            break;
        default:
            illegal_instruction = true;
    }
    instructions++;

    if ( illegal_instruction )
        enter_debugger |= ILLEGAL_INSTRUCTION;
}

void schedule( void )
{
    t1_t2_ticks ticks;
    unsigned long steps;
    static unsigned long old_stat_instr;
    static unsigned long old_sched_instr;

    steps = instructions - old_sched_instr;
    old_sched_instr = instructions;

    if ( ( sched_timer2 -= steps ) <= 0 ) {
        if ( !saturn.interruptable )
            sched_timer2 = SCHED_TIMER2;
        else
            sched_timer2 = saturn.t2_tick;

        saturn.t2_instr += steps;
        if ( saturn.t2_ctrl & 0x01 )
            saturn.timer2--;

        if ( saturn.timer2 == 0 && ( saturn.t2_ctrl & 0x02 ) ) {
            saturn.t2_ctrl |= 0x08;
            do_interupt();
        }
    }
    schedule_event = sched_timer2;

    if ( device_check ) {
        device_check = false;

        /* check_device() */

        /* serial */
        if ( device.baud_touched ) {
            device.baud_touched = false;
            serial_baud( saturn.baud );
        }

        if ( device.ioc_touched ) {
            device.ioc_touched = false;
            if ( ( saturn.io_ctrl & 0x02 ) && ( saturn.rcs & 0x01 ) )
                do_interupt();
        }

        if ( device.rbr_touched ) {
            device.rbr_touched = false;
            receive_char();
        }

        if ( device.tbr_touched ) {
            device.tbr_touched = false;
            transmit_char();
        }

        if ( device.t1_touched ) {
            saturn.t1_instr = 0;
            sched_timer1 = saturn.t1_tick;
            restart_timer( T1_TIMER );
            set_t1 = saturn.timer1;
            device.t1_touched = false;
        }

        if ( device.t2_touched ) {
            saturn.t2_instr = 0;
            sched_timer2 = saturn.t2_tick;
            device.t2_touched = false;
        }
        /* end check_device() */
    }

    if ( ( sched_receive -= steps ) <= 0 ) {
        sched_receive = SCHED_RECEIVE;
        if ( ( saturn.rcs & 0x01 ) == 0 )
            receive_char();
    }
    if ( sched_receive < schedule_event )
        schedule_event = sched_receive;

    if ( ( sched_adjtime -= steps ) <= 0 ) {
        sched_adjtime = SCHED_ADJTIME;

        if ( saturn.pc < SrvcIoStart || saturn.pc > SrvcIoEnd ) {
            ticks = get_t1_t2();
            if ( saturn.t2_ctrl & 0x01 )
                saturn.timer2 = ticks.t2_ticks;

            if ( ( saturn.t2_ctrl & 0x08 ) == 0 && saturn.timer2 <= 0 ) {
                if ( saturn.t2_ctrl & 0x02 ) {
                    saturn.t2_ctrl |= 0x08;
                    do_interupt();
                }
            }

            adj_time_pending = false;

            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 = true;
    }
    if ( sched_adjtime < schedule_event )
        schedule_event = sched_adjtime;

    if ( ( sched_timer1 -= steps ) <= 0 ) {
        if ( !saturn.interruptable )
            sched_timer1 = SCHED_TIMER1;
        else
            sched_timer1 = saturn.t1_tick;

        saturn.t1_instr += steps;
        saturn.timer1 = ( saturn.timer1 - 1 ) & 0xf;
        if ( saturn.timer1 == 0 && ( saturn.t1_ctrl & 0x02 ) ) {
            saturn.t1_ctrl |= 0x08;
            do_interupt();
        }
    }
    if ( sched_timer1 < schedule_event )
        schedule_event = sched_timer1;

    if ( ( sched_statistics -= steps ) <= 0 ) {
        sched_statistics = SCHED_STATISTICS;
        /* run = get_timer( RUN_TIMER ); */
        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 = ( ( NB_SAMPLES - 1 ) * t1_i_per_tick + ( delta_i / delta_t_16 ) ) / NB_SAMPLES;
            t2_i_per_tick = t1_i_per_tick / 512;
            saturn.i_per_s = ( ( NB_SAMPLES - 1 ) * saturn.i_per_s + ( delta_i / delta_t_1 ) ) / NB_SAMPLES;
        } else {
            t1_i_per_tick = 8192;
            t2_i_per_tick = 16;
        }
        saturn.t1_tick = t1_i_per_tick;
        saturn.t2_tick = t2_i_per_tick;
    }
    if ( sched_statistics < schedule_event )
        schedule_event = sched_statistics;

    if ( ( sched_instr_rollover -= steps ) <= 0 ) {
        sched_instr_rollover = SCHED_INSTR_ROLLOVER;
        instructions = 1;
        old_sched_instr = 1;
        reset_timer( RUN_TIMER );
        reset_timer( IDLE_TIMER );
        start_timer( RUN_TIMER );
    }
    if ( sched_instr_rollover < schedule_event )
        schedule_event = sched_instr_rollover;

    schedule_event--;

    if ( sigalarm_triggered ) {
        sigalarm_triggered = false;

        ui_update_display();
        ui_get_event();
    }
}
