/* =======================================================================
    C25LIB.C - Library of routines for building TMS320C25 simulators
    Copyright (C) 1995 Will Ware

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   ======================================================================= */

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "conio.h"
#include "c25lib.h"

extern void __write(char *s);

/* ======================================================================= */
/*                           #define statements                            */
/* ======================================================================= */

#define X(z)
#define NUM_OPCODES  (sizeof(opcodes) / sizeof(opcodes[0]))
#define MAX_NUM_BRKPTS  100
#define read_bit(var, bit)    ((int) (((var) >> (bit)) & 1))
#define set_bit(var, flag, bit)  \
    var = (var & ~(1 << bit)) | (flag ? (1 << bit) : 0)
#define SIGN_BIT 0x80000000L
#define sign32(x) ((x) & SIGN_BIT)
#define phex(n)  { sprintf(toprint, "%04X ", n); __write(toprint); }
#define plhex(n)  { sprintf(toprint, "%08lX ", n); __write(toprint); }

typedef void (*voidfunc) (void);

typedef struct Sspecmem
    {
    int addr, rdef, wdef;
    } specmem;

typedef struct Smemblk
    {
    char prog_data, high_addr;
    int contents[256];
    struct Smemblk *next;
    } memblk;

typedef struct Sinstruction_format
    {
    char *pattern, cycle_type;
    voidfunc f, rpt_prep;
    char *opname;
    } instruction_format;

typedef struct Sbitmask_format
    {
    int andmask, pattern;
    } bitmask_format;

/* ======================================================================= */
/*                           Public Functions                              */
/* ======================================================================= */

int *program_memory_address(int a);
void write_program_memory(int a, int d);
int read_program_memory(int a);
int *data_memory_address(int a);
void write_data_memory(int a, int d);
int read_data_memory(int a);
void advance(void);
void initialize_simulator(void);
void c25_reset(void);
void c25_interrupt(enum int_type n);

void load_hex_files(char *word);
void load_map_file(char *word);
int lookup_symbol(char *name);
char * disassemble(int instruc);
int print_matching_symbols(int a);
int print_all_symbols(void);
void short_report(void);
void long_report(void);
void add_breakpoint(int a);
void delete_breakpoint(int a);
void clear_all_breakpoints(void);
int go_til_breakpoint(long int n);

/* ======================================================================= */
/*                           Static Functions                              */
/* ======================================================================= */

static memblk *allot_block(char p_d, char high_addr);
static memblk *search_for_block(char p_d, char hi_addr);
static memblk *find_prog_block(char hi_addr);
static memblk *find_data_block(char hi_addr);
static int in_block_b0(int a);
static int on_chip_rom(int a);
static int on_chip_ram(int a);
static int get_address(void);
static void sign_extend(long int *x);
static void fetch_operand(void);
static void store_result(int x);
static int detect_overflow(long number_added);
static void saturate_overflow(long number_added);
static void add_operand(int c_in);
static void sub_operand(int c_in);
static void cb(int flag);
static void cbs(int flag, int dest);
static void shift_operand(void);
static void long_immediate_with_shift(void);
static long int shifted_p(void);
static void repeat_instruction(void);
static int c25_hardware_pop(void);
static void c25_hardware_push(int d);
static void check_dmov_okay(int addr, char *instruc_name);
static void handle_non_branch(int a,
    int pi_di, int pi_de, int pe_di, int pe_de, int pr_di, int pr_de);
static void handle_branch(int a,
    int pi_di_i, int pi_de_i, int pe_di_i, int pe_de_i,
        int pr_di_i, int pr_de_i,
    int pi_di_r, int pi_de_r, int pe_di_r, int pe_de_r,
        int pr_di_r, int pr_de_r,
    int pi_di_e, int pi_de_e, int pe_di_e, int pe_de_e,
        int pr_di_e, int pr_de_e);
static int get_pmem_wait_states(int addr);
static int get_dmem_wait_states(int addr);
static int lookup(int instruc);
static void burn_cycles(int cycle_type, int n);
static void service_pending_interrupts(void);

static void Abs(void);
static void Add(void);
static void Addc(void);
static void Addh(void);
static void Addk(void);
static void Adds(void);
static void Addt(void);
static void Adlk(void);
static void Adrk(void);
static void And(void);
static void Andk(void);
static void Apac(void);
static void B(void);
static void Bacc(void);
static void Banz(void);
static void Bbnz(void);
static void Bbz(void);
static void Bc(void);
static void Bgez(void);
static void Bgz(void);
static void Bioz(void);
static void Bit(void);
static void Bitt(void);
static void Blez(void);
static void Blkd_rpt_prep(void);
static void Blkd(void);
static void Blkp(void);
static void Blz(void);
static void Bnc(void);
static void Bnv(void);
static void Bnz(void);
static void Bv(void);
static void Bz(void);
static void Cala(void);
static void Call(void);
static void Cmpl(void);
static void Cmpr(void);
static void Cnfd(void);
static void Cnfp(void);
static void Dint(void);
static void Dmov(void);
static void Eint(void);
static void Fort(void);
static void Idle(void);
static void In(void);
static void Lac(void);
static void Lack(void);
static void Lact(void);
static void Lalk(void);
static void Lar(void);
static void Lark(void);
static void Larp(void);
static void Ldp(void);
static void Ldpk(void);
static void Lph(void);
static void Lrlk(void);
static void Lst(void);
static void Lst1(void);
static void Lt(void);
static void Lta(void);
static void Ltd(void);
static void Ltp(void);
static void Lts(void);
static void Mac(void);
static void Macd(void);
static void Mar(void);
static void Mpy(void);
static void Mpya(void);
static void Mpyk(void);
static void Mpys(void);
static void Mpyu(void);
static void Neg(void);
static void Nop(void);
static void Norm(void);
static void Or(void);
static void Ork(void);
static void Out(void);
static void Pac(void);
static void Pop(void);
static void Popd(void);
static void Pshd(void);
static void Push(void);
static void Rc(void);
static void Ret(void);
static void Rfsm(void) ;
static void Rhm(void) ;
static void Rol(void);
static void Ror(void);
static void Rovm(void);
static void Rpt(void);
static void Rptk(void);
static void Rsxm(void);
static void Rtc(void);
static void Rtxm(void);
static void Rxf(void);
static void Sach(void);
static void Sacl(void);
static void Sar(void);
static void Sblk(void);
static void Sbrk(void);
static void Sc(void) ;
static void Sfl(void);
static void Sfr(void);
static void Sfsm(void);
static void Shm(void);
static void Sovm(void);
static void Spac(void);
static void Sph(void);
static void Spl(void);
static void Spm(void);
static void Sqra(void);
static void Sqrs(void);
static void Sst(void);
static void Sst1(void);
static void Ssxm(void);
static void Stc(void);
static void Stxm(void);
static void Sub(void);
static void Subb(void);
static void Subc(void);
static void Subh(void);
static void Subk(void);
static void Subs(void);
static void Subt(void);
static void Sxf(void);
static void Tblr_rpt_prep(void);
static void Tblr(void);
static void Tblw(void);
static void Trap(void);
static void Xor(void);
static void Xork(void);
static void Zac(void);
static void Zalh(void);
static void Zalr(void);
static void Zals(void);

/* ======================================================================= */
/*                           Public Variables                              */
/* ======================================================================= */

/* C25 registers */

int arp, ov, ovm, intm, dp; /* st0 */
int arb, cnf, tc, sxm, c, hm, fsm, xf, fo, txm, pm; /* st1 */
long int accumulator, p_register;
unsigned int pc;
int t_register, _stack[8], _sp, ar[8];
int mp_mc_mode;
int ports[16], bio;
int drr, dxr, tim, prd, imr, greg;

unsigned long int steps_taken, cycles;
int warn_progmem_writes;
iufunc pwaitf = NULL, dwaitf = NULL, iwaitf = NULL;

/* ======================================================================= */
/*                           Static Variables                              */
/* ======================================================================= */

static char toprint[100];

static bitmask_format opcode_masks[200];

static int instruction, operand_address, branch_condition, branch_destination;

static int pending_interrupts;

static int port_addr, new_allotment, idle_mode = 0;
static long int operand, old_accum;
static int pfc, repeating, rptc;
static memblk *first_memblk = NULL;
static char fname[40], fhiline[80], floline[80];
static FILE *fhi, *flo;
static int breakpoint_list[MAX_NUM_BRKPTS], number_breakpoints = 0;

static instruction_format opcodes[] =
    {
    "1100111000011011", 0, Abs, NULL, "ABS",
    "0000xxxxxxxxxxxx", 1, Add, NULL, "ADD",
    "01000011xxxxxxxx", 1, Addc, NULL, "ADDC",
    "01001000xxxxxxxx", 1, Addh, NULL, "ADDH",
    "11001100xxxxxxxx", 0, Addk, NULL, "ADDK",
    "01001001xxxxxxxx", 1, Adds, NULL, "ADDS",
    "01001010xxxxxxxx", 1, Addt, NULL, "ADDT",
    "1101xxxx00000010", 2, Adlk, NULL, "ADLK",
    "01111110xxxxxxxx", 0, Adrk, NULL, "ADRK",
    "01001110xxxxxxxx", 0, And, NULL, "AND",
    "1101xxxx00000100", 2, Andk, NULL, "ANDK",
    "1100111000010101", 0, Apac, NULL, "APAC",
    "111111111xxxxxxx", 3, B, NULL, "B",
    "1100111000100101", 4, Bacc, NULL, "BACC",
    "111110111xxxxxxx", 3, Banz, NULL, "BANZ",
    "111110011xxxxxxx", 3, Bbnz, NULL, "BBNZ",
    "111110001xxxxxxx", 3, Bbz, NULL, "BBZ",
    "010111101xxxxxxx", 3, Bc, NULL, "BC",
    "111101001xxxxxxx", 3, Bgez, NULL, "BGEZ",
    "111100011xxxxxxx", 3, Bgz, NULL, "BGZ",
    "111110101xxxxxxx", 3, Bioz, NULL, "BIOZ",
    "1001xxxxxxxxxxxx", 1, Bit, NULL, "BIT",
    "01010111xxxxxxxx", 1, Bitt, NULL, "BITT",
    "111100101xxxxxxx", 3, Blez, NULL, "BLEZ",
    "11111101xxxxxxxx", 4, Blkd, Blkd_rpt_prep, "BLKD",
    "11111100xxxxxxxx", 4, Blkp, Blkd_rpt_prep, "BLKP",
    "111100111xxxxxxx", 3, Blz, NULL, "BLZ",
    "010111111xxxxxxx", 3, Bnc, NULL, "BNC",
    "111101111xxxxxxx", 3, Bnv, NULL, "BNV",
    "111101011xxxxxxx", 3, Bnz, NULL, "BNZ",
    "111100001xxxxxxx", 3, Bv, NULL, "BV",
    "111101101xxxxxxx", 3, Bz, NULL, "BZ",
    "1100111000100100", 4, Cala, NULL, "CALA",
    "111111101xxxxxxx", 3, Call, NULL, "CALL",
    "1100111000100111", 0, Cmpl, NULL, "CMPL",
    "11001110010100xx", 0, Cmpr, NULL, "CMPR",
    "1100111000000100", 0, Cnfd, NULL, "CNFD",
    "1100111000000101", 0, Cnfp, NULL, "CNFP",
    "1100111000000001", 0, Dint, NULL, "DINT",
    "01010110xxxxxxxx", 1, Dmov, NULL, "DMOV",
    "1100111000000000", 0, Eint, NULL, "EINT",
    "110011100000111x", 0, Fort, NULL, "FORT",
    "1100111000011111", 6, Idle, NULL, "IDLE",
    "1110xxxxxxxxxxxx", 7, In, NULL, "IN",
    "0010xxxxxxxxxxxx", 1, Lac, NULL, "LAC",
    "11001010xxxxxxxx", 0, Lack, NULL, "LACK",
    "01000010xxxxxxxx", 1, Lact, NULL, "LACT",
    "1101xxxx00000001", 2, Lalk, NULL, "LALK",
    "00110xxxxxxxxxxx", 1, Lar, NULL, "LAR",
    "11000xxxxxxxxxxx", 0, Lark, NULL, "LARK",
    "0101010110001xxx", 0, Larp, NULL, "LARP",
    "01010010xxxxxxxx", 1, Ldp, NULL, "LDP",
    "1100100xxxxxxxxx", 0, Ldpk, NULL, "LDPK",
    "01010011xxxxxxxx", 1, Lph, NULL, "LPH",
    "11010xxx00000000", 2, Lrlk, NULL, "LRLK",
    "01010000xxxxxxxx", 1, Lst, NULL, "LST",
    "01010001xxxxxxxx", 1, Lst1, NULL, "LST1",
    "00111100xxxxxxxx", 1, Lt, NULL, "LT",
    "00111101xxxxxxxx", 1, Lta, NULL, "LTA",
    "00111111xxxxxxxx", 1, Ltd, NULL, "LTD",
    "00111110xxxxxxxx", 1, Ltp, NULL, "LTP",
    "01011011xxxxxxxx", 1, Lts, NULL, "LTS",
    "01011101xxxxxxxx", 8, Mac, Blkd_rpt_prep, "MAC",
    "01011100xxxxxxxx", 8, Macd, Blkd_rpt_prep, "MACD",
    "0101010100000000", 0, Nop, NULL, "NOP",  /* special case of MAR */
    "01010101xxxxxxxx", 0, Mar, NULL, "MAR",
    "00111000xxxxxxxx", 1, Mpy, NULL, "MPY",
    "00111010xxxxxxxx", 1, Mpya, NULL, "MPYA",
    "101xxxxxxxxxxxxx", 0, Mpyk, NULL, "MPYK",
    "00111011xxxxxxxx", 1, Mpys, NULL, "MPYS",
    "11001111xxxxxxxx", 1, Mpyu, NULL, "MPYU",
    "1100111000100011", 0, Neg, NULL, "NEG",
    "1100111010100010", 0, Norm, NULL, "NORM",
    "01001101xxxxxxxx", 1, Or, NULL, "OR",
    "1101xxxx00000101", 2, Ork, NULL, "ORK",
    "1110xxxxxxxxxxxx", 7, Out, NULL, "OUT",
    "1100111000010100", 0, Pac, NULL, "PAC",
    "1100111000011101", 0, Pop, NULL, "POP",
    "01111010xxxxxxxx", 10, Popd, NULL, "POPD",
    "01010100xxxxxxxx", 1, Pshd, NULL, "PSHD",
    "1100111000011100", 0, Push, NULL, "PUSH",
    "1100111000110000", 0, Rc, NULL, "RC",
    "1100111000100110", 4, Ret, NULL, "RET",
    "1100111000110110", 0, Rfsm, NULL, "RFSM",
    "1100111000101000", 0, Rhm, NULL, "RHM",
    "1100111000110100", 0, Rol, NULL, "ROL",
    "1100111000110101", 0, Ror, NULL, "ROR",
    "1100111000000010", 0, Rovm, NULL, "ROVM",
    "01001011xxxxxxxx", 1, Rpt, NULL, "RPT",
    "11001011xxxxxxxx", 0, Rptk, NULL, "RPTK",
    "1100111000000110", 0, Rsxm, NULL, "RSXM",
    "1100111000110010", 0, Rtc, NULL, "RTC",
    "1100111000100000", 0, Rtxm, NULL, "RTXM",
    "1100111000001100", 0, Rxf, NULL, "RXF",
    "01101xxxxxxxxxxx", 10, Sach, NULL, "SACH",
    "01100xxxxxxxxxxx", 10, Sacl, NULL, "SACL",
    "01110xxxxxxxxxxx", 10, Sar, NULL, "SAR",
    "1101xxxx00000011", 2, Sblk, NULL, "SBLK",
    "01111111xxxxxxxx", 0, Sbrk, NULL, "SBRK",
    "1100111000110001", 0, Sc, NULL, "SC",
    "1100111000011000", 0, Sfl, NULL, "SFL",
    "1100111000011001", 0, Sfr, NULL, "SFR",
    "1100111000110111", 0, Sfsm, NULL, "SFSM",
    "1100111000111001", 0, Shm, NULL, "SHM",
    "1100111000000011", 0, Sovm, NULL, "SOVM",
    "1100111000010110", 0, Spac, NULL, "SPAC",
    "01111101xxxxxxxx", 10, Sph, NULL, "SPH",
    "01111100xxxxxxxx", 10, Spl, NULL, "SPL",
    "11001110000010xx", 0, Spm, NULL, "SPM",
    "00111001xxxxxxxx", 1, Sqra, NULL, "SQRA",
    "01011010xxxxxxxx", 1, Sqrs, NULL, "SQRS",
    "01111000xxxxxxxx", 10, Sst, NULL, "SST",
    "01111001xxxxxxxx", 10, Sst1, NULL, "SST1",
    "1100111000000111", 0, Ssxm, NULL, "SSXM",
    "1100111000110011", 0, Stc, NULL, "STC",
    "1100111000100001", 0, Stxm, NULL, "STXM",
    "0001xxxxxxxxxxxx", 1, Sub, NULL, "SUB",
    "01001111xxxxxxxx", 1, Subb, NULL, "SUBB",
    "01000111xxxxxxxx", 1, Subc, NULL, "SUBC",
    "01000100xxxxxxxx", 1, Subh, NULL, "SUBH",
    "11001101xxxxxxxx", 0, Subk, NULL, "SUBK",
    "01000101xxxxxxxx", 1, Subs, NULL, "SUBS",
    "01000110xxxxxxxx", 1, Subt, NULL, "SUBT",
    "1100111000001101", 0, Sxf, NULL, "SXF",
    "01011000xxxxxxxx", 9, Tblr, Tblr_rpt_prep, "TBLR",
    "01011001xxxxxxxx", 5, Tblw, Tblr_rpt_prep, "TBLW",
    "1100111000011110", 4, Trap, NULL, "TRAP",
    "01001100xxxxxxxx", 1, Xor, NULL, "XOR",
    "1101xxxx00000110", 2, Xork, NULL, "XORK",
    "1100101000000000", 0, Zac, NULL, "ZAC",
    "01000000xxxxxxxx", 1, Zalh, NULL, "ZALH",
    "01111011xxxxxxxx", 1, Zalr, NULL, "ZALR",
    "01000001xxxxxxxx", 1, Zals, NULL, "ZALS",
    };

/* ======================================================================= */
/*           Alternative Scheme for implementing Status Registers          */
/* ======================================================================= */

#if 0
int st0, st1;

#define read_bits(x,m,n)  ((x >> m) & n)
#define set_bits(x,amask,y,n) \
            x = (x & ~amask) | ((y << n) & amask)

#define arp     read_bits(st0,13,7)
#define ov      read_bits(st0,12,1)
#define ovm     read_bits(st0,11,1)
#define intm    read_bits(st0,9,1)
#define dp      read_bits(st0,0,0x1FF)

#define set_arp(x)     set_bits(st0,0xE000,x,13)
#define set_ov(x)      set_bits(st0,0x1000,x,12)
#define set_ovm(x)     set_bits(st0,0x0800,x,11)
#define set_intm(x)    set_bits(st0,0x0200,x,9)
#define set_dp(x)      set_bits(st0,0x01FF,x,0)

#define arb     read_bits(st1,13,7)
#define cnf     read_bits(st1,12,1)
#define tc      read_bits(st1,11,1)
#define sxm     read_bits(st1,10,1)
#define c       read_bits(st1,9,1)
#define hm      read_bits(st1,6,1)
#define fsm     read_bits(st1,5,1)
#define xf      read_bits(st1,4,1)
#define fo      read_bits(st1,3,1)
#define rxm     read_bits(st1,2,1)
#define pm      read_bits(st1,0,3)

#define set_arb(x)     set_bits(st1,0xE000,x,13)
#define set_cnf(x)     set_bits(st1,0x1000,x,12)
#define set_tc(x)      set_bits(st1,0x0800,x,11)
#define set_sxm(x)     set_bits(st1,0x0400,x,10)
#define set_c(x)       set_bits(st1,0x0200,x,9)
#define set_hm(x)      set_bits(st1,0x0040,x,6)
#define set_fsm(x)     set_bits(st1,0x0020,x,5)
#define set_xf(x)      set_bits(st1,0x0010,x,4)
#define set_fo(x)      set_bits(st1,0x0008,x,3)
#define set_rxm(x)     set_bits(st1,0x0004,x,2)
#define set_pm(x)      set_bits(st1,0x0003,x,0)
#endif

/* ======================================================================= */
/*                             Memory System                               */
/* ======================================================================= */

static memblk *allot_block(char p_d, char high_addr)
    {
    memblk *p, *q;

    q = malloc(sizeof(memblk));
    if (q == NULL)
        {
        sprintf(toprint, "Out of program/data memory!!\n");
        __write(toprint);
        exit(1);
        }

    if (first_memblk == NULL)
        {
        first_memblk = q;
        goto FillErUp;
        }
    for (p = first_memblk; p->next != NULL; p = p->next);
    p->next = q;
FillErUp:
    q->prog_data = p_d;
    q->high_addr = high_addr;
    q->next = NULL;
    new_allotment = 1;
    return q;
    }

static memblk *search_for_block(char p_d, char hi_addr)
    {
    memblk *p = NULL;

    for (p = first_memblk; p != NULL; p = p->next)
        if (p->prog_data == p_d && p->high_addr == hi_addr)
            return p;
    return NULL;
    }

static memblk *find_prog_block(char hi_addr)
    {
    memblk *p;

    new_allotment = 0;
    if ((p = search_for_block(1, hi_addr)) != NULL) return p;
    return allot_block(1, hi_addr);
    }

int *program_memory_address(int a)
    {
    memblk *x;

    x = find_prog_block((char) (a >> 8));
    return &(x->contents[a & 0xFF]);
    }

void write_program_memory(int a, int d)
    {
    if (warn_progmem_writes)
        {
        sprintf(toprint,
            "WARNING: Writing program memory, address %04X data %04X\n",
            a, d);
        __write(toprint);
        }
    *program_memory_address(a) = d;
    }

int read_program_memory(int a)
    {
    int d;

    d = *program_memory_address(a);
    if (new_allotment)
        {
        sprintf(toprint, "WARNING: Reading uninitialized program memory!\n");
        __write(toprint);
        }
    return d;
    }

static memblk *find_data_block(char hi_addr)
    {
    memblk *p;

    new_allotment = 0;
    if ((p = search_for_block(0, hi_addr)) != NULL) return p;
    return allot_block(0, hi_addr);
    }

int *data_memory_address(int a)
    {
    memblk *x;

    switch (a)
        {
        case 0: return &drr;
        case 1: return &dxr;
        case 2: return &tim;
        case 3: return &prd;
        case 4: return &imr;
        case 5: return &greg;
        }
    x = find_data_block((char) (a >> 8));
    return &(x->contents[a & 0xFF]);
    }

void write_data_memory(int a, int d)
    {
    *data_memory_address(a) = d;
    }

int read_data_memory(int a)
    {
    int d;

    d = *data_memory_address(a);
    if (new_allotment)
        {
        sprintf(toprint, "WARNING: Reading uninitialized data memory!\n");
        __write(toprint);
        }
    return d;
    }

/* ======================================================================= */
/*                      Instruction helpers                                */
/* ======================================================================= */

static int in_block_b0(int a)
    {
    if (cnf) return (a & 0xFF00) == 0xFF00;
    return (a & 0xFF00) == 0x200;
    }

static int on_chip_rom(int a)
    {
    if (mp_mc_mode) return 0;
    return ((a >= 0x20 && a < 0xFAF)
        || (cnf && in_block_b0(a)));
    }

static int on_chip_ram(int a)
    {
    return ((a >= 0x60 && a < 0x80)  /* block B2 */
        || (a & 0xFF00) == 0x300     /* block B1 */
        || (!cnf && in_block_b0(a)));
    }

static int get_address(void)
    {
    int i, mask, r, s, carry;

    if ((instruction & 0x80) == 0)
        r = ((dp << 7) & 0xFF80) | (instruction & 0x7F);
    else
        {
        /* handle updates to AR(ARP) and ARP */
        r = ar[arp];
        s = ar[0];
        switch ((instruction >> 4) & 7)
            {
            case 0:
                break;
            case 1:
                ar[arp]--;
                break;
            case 2:
                ar[arp]++;
                break;
            case 3:
                sprintf(toprint, "Bad addressing mode in instruction at %04X\n", pc);
                __write(toprint);
                break;
            case 4:
                carry = 0;
                for (i = 0; i < 16; i++)
                    {
                    mask = 1 << (15 - i);
                    carry = ((ar[arp] & mask) ? 1 : 0)
                        - ((s & mask) ? 1 : 0)
                        - carry;
                    ar[arp] = (ar[arp] & ~mask) + ((carry & 1) ? mask : 0);
                    carry = (carry >> 1) & 1;
                    }
                break;
            case 5:
                ar[arp] -= ar[0];
                break;
            case 6:
                ar[arp] += ar[0];
                break;
            case 7:
                carry = 0;
                for (i = 0; i < 16; i++)
                    {
                    mask = 1 << (15 - i);
                    carry += ((ar[arp] & mask) ? 1 : 0)
                        + ((s & mask) ? 1 : 0);
                    ar[arp] = (ar[arp] & ~mask) + ((carry & 1) ? mask : 0);
                    carry = (carry >> 1) & 1;
                    }
                break;
            }
        if (instruction & 8) { arb = arp; arp = (instruction & 7); }
        }
    operand_address = r;
    return r;
    }

static void sign_extend(long int *x)
    {
    if (sxm && (*x & 0x8000)) *x |= 0xFFFF0000L;
    else *x &= 0x0000FFFFL;
    }

static void fetch_operand(void)
    {
    int a;
 
    old_accum = accumulator;
    a = get_address();
    operand = read_data_memory(a);
    }

static void store_result(int x)
    {
    write_data_memory(get_address(), x);
    }

static int detect_overflow(long number_added)
    {
    if (!sign32(number_added))
        if (!sign32(old_accum) && sign32(accumulator)) return 1;
    if (sign32(number_added))
        if (sign32(old_accum) && !sign32(accumulator)) return 1;
    return 0;
    }

static void saturate_overflow(long number_added)
    {
    if (!sign32(number_added)) accumulator = ~SIGN_BIT;
    else accumulator = SIGN_BIT;
    }

static void add_operand(int c_in)
    {
    int i;
    long mask;

    old_accum = accumulator;
    for (i = 0, mask = SIGN_BIT; i < 32; i++, mask >>= 1)
        {
        if ((accumulator & operand) & mask) { c = 1; goto c_done; }
        if ((~accumulator & ~operand) & mask) { c = 0; goto c_done; }
        }
    c = c_in;
c_done:
    if (c_in) operand++;
    accumulator += operand;
    if (detect_overflow(operand))
        {
        ov = 1;
        if (ovm) saturate_overflow(operand);
        }
    }

static void sub_operand(int c_in)  /* I think this is right */
    {
    operand = -operand;
    add_operand(c_in);
    }

static void cb(int flag)  /* conditional branch */
    {
    branch_condition = flag;
    if (flag) branch_destination = read_program_memory(pc);
    else branch_destination = pc + 1;
    pc = branch_destination;
    get_address();
    }

static void cbs(int flag, int dest)  /* conditional branch, special destination */
    {
    if (flag) pc = dest;
    get_address();
    }

static void shift_operand(void)
    {
    operand <<= (instruction >> 8) & 15;
    }

static void long_immediate_with_shift(void)
    {
    operand = (long) read_program_memory(pc++);
    shift_operand();
    }

static long int shifted_p(void)
    {
    long int shp;

    switch (pm)
        {
        case 0: return p_register;
        case 1: return p_register << 1;
        case 2: return p_register << 4;
        case 3:
            shp = p_register >> 6;
            if (p_register & SIGN_BIT) shp |= 0xFC000000L;
            return shp;
        }
    }

static void repeat_instruction(void)
    {
    voidfunc g;
    int i, init_rptc;

    instruction = read_program_memory(pc++);
    i = lookup(instruction);
    if (i == -1)
        {
        sprintf(toprint, "Trying to repeat uknown opcode at address %04X\n", pc-1);
        __write(toprint);
        return;
        }
    repeating = 1;
    g = opcodes[i].rpt_prep;
    if (g != NULL) (*g) ();
    g = opcodes[i].f;
    init_rptc = rptc;
    while (rptc >= 0) { (*g) (); rptc--; }
    burn_cycles(opcodes[i].cycle_type, init_rptc + 1);
    repeating = 0;
    rptc = 0;
    }

static int c25_hardware_pop(void)
    {
    _sp = (_sp - 1) & 7;
    return _stack[_sp];
    }

static void c25_hardware_push(int d)
    {
    _stack[_sp] = d;
    _sp = (_sp + 1) & 7;
    }

static void check_dmov_okay(int addr, char *instruc_name)
    {
    /* For the instructions DMOV, LTD, and MACD, both addr and addr+1
    must be in on-chip ram for the data move to work. */
    if (on_chip_ram(addr) && on_chip_ram(addr + 1)) return;
    sprintf(toprint, "Instruction %s at %04X using non-internal "
        "data memory at address %04X\n", instruc_name, pc, addr);
    __write(toprint);
    }

/* ======================================================================= */
/*                        Instruction Cycle Times                          */
/* ======================================================================= */

static void handle_non_branch(int a,
    int pi_di, int pi_de, int pe_di, int pe_de, int pr_di, int pr_de)
    {
    int n;

    if (on_chip_ram(a) && on_chip_ram(operand_address)) n = pi_di;
    else if (on_chip_ram(a)) n = pi_de;
    else if (on_chip_rom(a) && on_chip_ram(operand_address)) n = pr_di;
    else if (on_chip_rom(a)) n = pr_de;
    else if (on_chip_ram(operand_address)) n = pe_di;
    else n = pe_de;
    cycles += n;
    tim -= n;
    if ((tim + n) > 0 && tim <= 0) c25_interrupt(TINT);
    }

static void handle_branch(int a,
    int pi_di_i, int pi_de_i, int pe_di_i, int pe_de_i,
        int pr_di_i, int pr_de_i,
    int pi_di_r, int pi_de_r, int pe_di_r, int pe_de_r,
        int pr_di_r, int pr_de_r,
    int pi_di_e, int pi_de_e, int pe_di_e, int pe_de_e,
        int pr_di_e, int pr_de_e)
    {
    if (on_chip_ram(branch_destination))
        {
        handle_non_branch(a,
            pi_di_i, pi_de_i, pe_di_i, pe_de_i, pr_di_i, pr_de_i);
        return;
        }
    if (on_chip_rom(branch_destination))
        {
        handle_non_branch(a,
            pi_di_r, pi_de_r, pe_di_r, pe_de_r, pr_di_r, pr_de_r);
        return;
        }
    handle_non_branch(a,
        pi_di_e, pi_de_e, pe_di_e, pe_de_e, pr_di_e, pr_de_e);
    }

static int get_pmem_wait_states(int addr)
    {
    if (on_chip_rom(addr)) return 0;
    if (pwaitf == NULL) return 0;
    return (*pwaitf) (addr);
    }

static int get_dmem_wait_states(int addr)
    {
    if (on_chip_ram(addr)) return 0;
    if (dwaitf == NULL) return 0;
    return (*dwaitf) (addr);
    }

static void burn_cycles(int cycle_type, int n)
    {
    int a, p, d, i;

    a = pc - 1;
    /* n is the RPTC+1, or zero if not repeating */
    p = get_pmem_wait_states(a);
    d = get_dmem_wait_states(operand_address);
    if (n == 0)
        switch (cycle_type)
            {
            case 0:  /* ABS, ... */
                handle_non_branch(a, 1, 1, 1+p, 1+p, 1, 1);
                return;
            case 1:  /* ADD, ADC ... */
                handle_non_branch(a, 1, 2+d, 1+p, 2+d+p, 1, 2+d);
                return;
            case 2:  /* ADLK */
                handle_non_branch(a, 2, 2, 2+2*p, 2+2*p, 2, 2);
                return;
            case 3:  /* B */
                if (!branch_condition)
                    {
                    handle_non_branch(a, 2, 2, 2+2*p, 2+2*p, 2, 2);
                    return;
                    }
                handle_branch(a,
                    2, 2, 2+2*p, 2+2*p, 2, 2,
                    3, 3, 3+2*p, 3+2*p, 3, 3,
                    3+p, 3+p, 3+3*p, 3+3*p, 3+p, 3+p);
                return;
            case 4:   /* Bacc */
                handle_branch(a,
                    2, 2, 2+2*p, 2+2*p, 2, 2,
                    3, 3, 3+2*p, 3+2*p, 3, 3,
                    3+p, 3+p, 3+2*p, 3+2*p, 3+p, 3+p);
                return;
            case 6:   /* Idle */
                handle_non_branch(a, 3, 3+2*p, 3, 3+2*p, 3, 3+2*p);
                return;
            case 7:   /* In */
                if (iwaitf == NULL) i = 0;
                else i = (*iwaitf) (port_addr);
                handle_non_branch(a, 2+i, 2+d+i, 2+p+i, 3+d+p+i, 2+i, 2+d+i);
                return;
            case 10:   /* Popd */
                handle_non_branch(a, 1, 1+d, 1+p, 2+d+p, 1, 1+d);
                return;
            case 9:   /* Tblr */
                branch_destination = pfc;
                handle_branch(a,
                    2, 2+d, 3+p, 3+d+p, 3, 3+d,
                    3, 3+d, 4+p, 4+d+p, 4, 4+d,
                    3+p, 3+d+p, 4+2*p, 4+d+2*p, 4+p, 4+d+p);
                return;
            case 5:   /* Tblw */
                branch_destination = pfc;
                handle_branch(a,
                    2, 3+d, 3+p, 4+d+p, 3, 4+d,
                    0, 0, 0, 0, 0, 0,
                    2+p, 3+d+p, 3+2*p, 4+d+2*p, 3+p, 4+d+p);
                return;
            case 8:   /* Mac */
                branch_destination = pfc;
                handle_branch(a,
                    3, 4+d, 4+2*p, 5+d+2*p, 4, 5+d,
                    4, 5+d, 4+2*p, 5+d+2*p, 4, 5+d,
                    4+p, 5+d+p, 4+3*p, 5+d+3*p, 4+p, 5+d+p);
                return;
            default:
                sprintf(toprint, "Not yet handling burn_cycles(%d, %d)\n",
                    cycle_type, repeating);
                __write(toprint);
            }
    else
        switch (cycle_type)
            {
            case 0:  /* ABS, ... */
                handle_non_branch(a, n, n, n+p, n+p, n, n);
                return;
            case 1:  /* ADD, ADC ... */
                handle_non_branch(a,
                    1+n, 1+n+n*d, n+p, 1+n+n*d+p, n, 1+n+n*d);
                return;
            case 2:  /* ADLK */
            case 3:  /* B */
            case 4:   /* Bacc */
            case 6:   /* Idle */
                sprintf(toprint, "Can't repeat instruction at address %04X\n", a);
                __write(toprint);
                return;
            case 7:   /* In */
                if (iwaitf == NULL) i = 0;
                else i = (*iwaitf) (port_addr);
                handle_non_branch(a,
                    1+n+n*i, n*(2+d+i), 1+n+p+n*i, 1+p+n*(2+d+i),
                    1+n+n*i, n*(2+d+i));
                return;
            case 10:   /* Popd */
                handle_non_branch(a,
                    n, n+n*d, n+p, 1+n+n*d+p, n, n+n*d);
                return;
            case 9:   /* Tblr */
                branch_destination = pfc;
                handle_branch(a,
                    1+n, 1+n+n*d, 2+n+p, 2+n+n*d+p, 2+n, 2+n+n*d,
                    2+n, 2+n+n*d, 3+n+p, 3+n+n*d+p, 3+n, 3+n+n*d,
                    2+n+n*p, 1+n*(2+d+p), 3+n+p+n*p, 2+p+n*(2+d+p),
                        3+n+n*p, 2+n*(2+d+p));
                return;
            case 5:   /* Tblw */
                branch_destination = pfc;
                handle_branch(a,
                    1+n, 2+n+n*d, 2+n+p, 3+n+n*d+p, 2+n, 3+n+n*d,
                    0, 0, 0, 0, 0, 0,
                    1+n+n*p, 1+n*(2+d+p), 2+n+p+n*p, 2+p+n*(2+d+p),
                        2+n+n*p, 2+n*(2+d+p));
                return;
            case 8:   /* Mac */
                branch_destination = pfc;
                handle_branch(a,
                    2+n, 2+2*n+n*d, 3+n+2*p, 3+2*n+n*d+2*p, 3+n, 3+2*n+n*d,
                    3+n, 3+2*n+n*d, 3+n+2*p, 3+2*n+n*d+2*p, 3+n, 3+2*n+n*d,
                    3+n+n*p, 3+n*(2+d+p), 3+n+2*p+n*p, 3+2*p+n*(2+d+p),
                        3+n+n*p, 3+n*(2+d+p));
                return;
            default:
                printf(toprint, "Not yet handling burn_cycles(%d, %d)\n",
                    cycle_type, repeating);
                __write(toprint);
            }
    }

/* ======================================================================= */
/*                       Lookup table, advance                             */
/* ======================================================================= */

static int lookup(int instruc)
    {
    int i;

    for (i = 0; i < NUM_OPCODES; i++)
        if ((instruc & opcode_masks[i].andmask) ==
                    opcode_masks[i].pattern) return i;
    return -1;
    }

char * disassemble(int instruc)
    {
    int i;

    if ((i = lookup(instruc)) != -1) return opcodes[i].opname;
    return "ILLEGAL";
    }

void advance(void)
    {
    int i;

    service_pending_interrupts();
    if (idle_mode)
        {
    	cycles++;
        tim--;
        if (tim == 0) c25_interrupt(TINT);
        return;
        }
    instruction = read_program_memory(pc++);
    i = lookup(instruction);
    if (i != -1)
        {
        (*opcodes[i].f) ();
        burn_cycles(opcodes[i].cycle_type, 0);
        }
    else __write("ILLEGAL OPCODE\n");
    steps_taken++;
    }

void c25_reset(void)
    {
    int i;

    pc = 0;
    _sp = 0;
    accumulator = 0;
    t_register = 0;
    p_register = 0;
    rptc = 0;
    for (i = 0; i < 8; i++) { _stack[i] = 0; ar[i] = 0; }
    arp = 0; ov = 0; ovm = 0; intm = 1; dp = 0; arb = 0;
    cnf = 0; tc = 0; sxm = 0; c = 0; hm = 0; fsm = 0; xf = 0;
    fo = 0; txm = 0; pm = 0;
    drr = 0; dxr = 0; tim = 0xFFFF; prd = 0xFFFF; imr = 0; greg = 0;
    mp_mc_mode = 1;
    repeating = 0;
    idle_mode = 0;
    steps_taken = 0;
    cycles = 0;
    }

void initialize_simulator(void)
    {
    int i, j;

    c25_reset();
    warn_progmem_writes = 1;
    for (i = 0; i < NUM_OPCODES; i++)
        {
        opcode_masks[i].andmask = 0;
        opcode_masks[i].pattern = 0;
        for (j = 0; j < 16; j++)
            switch (opcodes[i].pattern[15 - j])
                {
                case '0':
                    opcode_masks[i].andmask |= (1 << j);
                    break;
                case '1':
                    opcode_masks[i].andmask |= (1 << j);
                    opcode_masks[i].pattern |= (1 << j);
                    break;
                default:
                    break;
                }
        }
    }

/* ======================================================================= */
/*                                Interrupts                               */
/* ======================================================================= */

void c25_interrupt(enum int_type n)
    {
    switch (n)
        {    
        case RS:
            c25_reset();
            return;
        case INT0:
            pending_interrupts |= 1;
            return;
        case INT1:
            pending_interrupts |= 2;
            return;
        case INT2:
            pending_interrupts |= 4;
            return;
        case TINT:
            tim += prd;
            pending_interrupts |= 8;
            return;
        case RINT:
            pending_interrupts |= 0x10;
            return;
        case XINT:
            pending_interrupts |= 0x20;
            return;
        case TRAP:
            c25_hardware_push(pc);
            pc = 0x001E;
            intm = 1;
            idle_mode = 0;
            return;
        default:
            sprintf(toprint, "Illegal interrupt\n");
            __write(toprint);
            break;
        }
    }

static int try_interrupt_bit(int n, int addr)
    {
    if (read_bit(imr & pending_interrupts, n))
        {
        pending_interrupts &= ~(1 << n);
        c25_hardware_push(pc);
        pc = addr;
        intm = 1;
        idle_mode = 0;
        return 1;
        }
    return 0;
    }

static void service_pending_interrupts(void)
    {
    if (intm) return;
    if (try_interrupt_bit(0, 0x0002)) return;
    if (try_interrupt_bit(1, 0x0004)) return;
    if (try_interrupt_bit(2, 0x0006)) return;
    if (try_interrupt_bit(3, 0x0018)) return;
    if (try_interrupt_bit(4, 0x001A)) return;
    if (try_interrupt_bit(5, 0x001C)) return;
    }

/* ======================================================================= */
/*                              C25 instructions                           */
/* ======================================================================= */

/* Ancillary Functions */

static void Blkd_rpt_prep(void)
    {
    pfc = read_program_memory(pc++);
    }

static void Tblr_rpt_prep(void)
    {
    pfc = (int) accumulator;
    }

/* Real C25 Instructions */

static void Abs(void)
    {
    /* exceptional case for 0x80000000, depending on OVF flag */
    if (accumulator == 0x80000000L)
        {
        if (ovm) accumulator = ~SIGN_BIT;
        ov = 1;
        }
    else if (accumulator & SIGN_BIT) accumulator = -accumulator;
    }

static void Add(void)
    {
    fetch_operand();
    sign_extend(&operand);
    shift_operand();
    add_operand(0);
    }

static void Addc(void)
    {
    fetch_operand();
    add_operand(c);
    }

static void Addh(void)
    {
    int old_c;

    c = old_c;
    fetch_operand();
    operand = (operand << 16) & 0xFFFF0000L;
    add_operand(0);
    c |= old_c;
    }

static void Addk(void)
    {
    operand = instruction & 0xFF;
    add_operand(0);
    }

static void Adds(void)
    {
    fetch_operand();
    add_operand(0);
    }

static void Addt(void)
    {
    int shift;

    shift = t_register & 15;
    fetch_operand();
    operand <<= shift;
    add_operand(0);
    }

static void Adlk(void)
    {
    long_immediate_with_shift();
    add_operand(0);
    }

static void Adrk(void)
    {
    ar[arp] += instruction & 0xFF;
    }

static void And(void)
    {
    fetch_operand();
    accumulator += operand;
    }

static void Andk(void)
    {
    long_immediate_with_shift();
    accumulator &= operand;
    }

static void Apac(void)
    {
    operand = shifted_p();
    add_operand(0);
    }

static void B(void)
    {
    cb(1);
    }

static void Bacc(void)
    {
    cbs(1, (int) accumulator);
    }

static void Banz(void)
    {
    cb(ar[arp]);
    }

static void Bbnz(void)
    {
    cb(tc);
    }

static void Bbz(void)
    {
    cb(!tc);
    }

static void Bc(void)
    {
    cb(c);
    }

static void Bgez(void)
    {
    cb(accumulator >= 0);
    }

static void Bgz(void)
    {
    cb(accumulator > 0);
    }

static void Bioz(void)
    {
    cb(bio);
    }

static void Bit(void)
    {
    int shift;              

    shift = 15 - ((instruction >> 8) & 15);
    fetch_operand();
    tc = (int) ((operand >> shift) & 1);
    }

static void Bitt(void)
    {
    int shift;

    shift = 15 - (t_register & 15);
    fetch_operand();
    tc = (int) ((operand >> shift) & 1);
    }

static void Blez(void)
    {
    cb(accumulator <= 0);
    }

static void Blkd(void)
    {
    if (!repeating) pfc = read_program_memory(pc++);
    store_result(read_data_memory(pfc++));
    }

static void Blkp(void)
    {
    if (!repeating) pfc = read_program_memory(pc++);
    store_result(read_program_memory(pfc++));
    }

static void Blz(void)
    {
    cb(accumulator < 0);
    }

static void Bnc(void)
    {
    cb(!c);
    }

static void Bnv(void)
    {
    cb(!ov);
    ov = 0;
    }

static void Bnz(void)
    {
    cb(accumulator != 0);
    }

static void Bv(void)
    {
    cb(ov);
    ov = 0;
    }

static void Bz(void)
    {
    cb(accumulator == 0);
    }

static void Cala(void)
    {
    c25_hardware_push(pc);
    pc = (int) accumulator;
    branch_destination = pc;
    }

static void Call(void)
    {
    c25_hardware_push(pc + 1);
    pc = read_program_memory(pc);
    branch_destination = pc;
    Mar();
    }

static void Cmpl(void)
    {
    accumulator = ~accumulator;
    }

static void Cmpr(void)
    {
    unsigned u, v;

    u = ar[arp]; v = ar[0];
    switch (instruction & 3)
        {
        case 0:
            tc = (u == v);
            break;
        case 1:
            tc = (u < v);
            break;
        case 2:
            tc = (u > v);
            break;
        case 3:
            tc = (u != v);
            break;
        }
    }

static void Cnfd(void)
    {
    memblk *p;

    cnf = 0;
    /* if there is a PROG block at 0xFF??, make it DATA at 0x02?? */
    if ((p = search_for_block(1, 0xFF)) != NULL)
        {
        p->prog_data = 0;
        p->high_addr = 0x02;
        }
    }

static void Cnfp(void)
    {
    memblk *p;

    cnf = 1;
    /* if there is a DATA block at 0x02??, make it PROG at 0xFF?? */
    if ((p = search_for_block(0, 0x02)) != NULL)
        {
        p->prog_data = 1;
        p->high_addr = 0xFF;
        }
    }

static void Dint(void)
    {
    intm = 1;
    }

static void Dmov(void)
    {
    int a;

    a = get_address();
    check_dmov_okay(a, "DMOV");
    write_data_memory(a + 1, read_data_memory(a));
    }

static void Eint(void)
    {
    intm = 0;
    }

static void Fort(void)
    {
    fo = instruction & 1;
    }

static void Idle(void)
    {
    intm = 0;
    idle_mode = 1;
    }

static void In(void)
    {
    port_addr = (instruction >> 8) & 15;
    store_result(ports[port_addr]);
    }

static void Lac(void)
    {
    fetch_operand();
    sign_extend(&operand);
    shift_operand();
    accumulator = operand;
    }

static void Lack(void)
    {
    accumulator = instruction & 0xFF;
    }

static void Lact(void)
    {
    int shift;

    shift = t_register & 15;
    fetch_operand();
    sign_extend(&operand);
    operand <<= shift;
    accumulator = operand;
    }

static void Lalk(void)
    {
    long_immediate_with_shift();
    accumulator = operand;
    sign_extend(&accumulator);
    }

static void Lar(void)
    {
    fetch_operand();
    ar[(instruction >> 8) & 7] = (int) operand;
    }

static void Lark(void)
    {
    ar[(instruction >> 8) & 7] = instruction & 0xFF;
    }

static void Larp(void)
    {
    arb = arp;
    arp = instruction & 7;
    }

static void Ldp(void)
    {
    fetch_operand();
    dp = (int) (operand & 0x1FF);
    }

static void Ldpk(void)
    {
    dp = instruction & 0x1FF;
    }

static void Lph(void)
    {
    fetch_operand();
    p_register = (p_register & 0xFFFFL) |
        ((operand << 16) & 0xFFFF0000L);
    }

static void Lrlk(void)
    {
    ar[(instruction >> 8) & 7] = read_program_memory(pc++);
    }

static void Lst(void)
    {
    fetch_operand();
    arp = (int) ((operand >> 13) & 7);
    ov = read_bit(operand, 12);
    ovm = read_bit(operand, 11);
    intm = read_bit(operand, 9);
    dp = (int) (operand & 0x1FF);
    }

static void Lst1(void)
    {
    fetch_operand();
    arb = (int) ((operand >> 13) & 7);
    arp = arb;
    cnf = read_bit(operand, 12);
    tc = read_bit(operand, 11);
    sxm = read_bit(operand, 10);
    c = read_bit(operand, 9);
    hm = read_bit(operand, 6);
    fsm = read_bit(operand, 5);
    xf = read_bit(operand, 4);
    fo = read_bit(operand, 3);
    txm = read_bit(operand, 2);
    pm = (int) (operand & 3);
    }

static void Lt(void)
    {
    fetch_operand();
    t_register = (int) operand;
    }

static void Lta(void)
    {
    Lt();
    Apac();
    }

static void Ltd(void)
    {
    int a;

    a = get_address();
    check_dmov_okay(a, "LTD");
    t_register = read_data_memory(a);
    write_data_memory(a + 1, read_data_memory(a));
    Apac();
    }

static void Ltp(void)
    {
    Lt();
    Pac();
    }

static void Lts(void)
    {
    Lt();
    Spac();
    }

static void Mac(void)
    {
    if (!repeating) pfc = read_program_memory(pc++);
    Apac();
    Lt();
    p_register = t_register;
    p_register *= read_program_memory(pfc++);
    }

static void Macd(void)
    {
    int a;

    if (!repeating) pfc = read_program_memory(pc++);
    Apac();
    a = get_address();
    check_dmov_okay(a, "MACD");
    t_register = read_data_memory(a);
    p_register = t_register;
    p_register *= read_program_memory(pfc++);
    write_data_memory(a + 1, read_data_memory(a));
    return;
    }

static void Mar(void)
    {
    get_address();
    }

static void Mpy(void)
    {
    fetch_operand();
    p_register = operand * t_register;
    }

static void Mpya(void)
    {
    Apac();
    Mpy();
    }

static void Mpyk(void)
    {
    operand = instruction & 0x1FFF;
    if (instruction & 0x1000) operand |= 0xFFFFE000;
    p_register = operand * t_register;
    }

static void Mpys(void)
    {
    Spac();
    Mpy();
    }

static void Mpyu(void)
    {
    unsigned long t, d;

    fetch_operand();
    d = operand;
    t = t_register;
    p_register = d * t;
    }

static void Neg(void)
    {
    c = (accumulator == 0L);
    if (accumulator == SIGN_BIT)
        {
        if (ovm) accumulator = ~SIGN_BIT;
        ov = 1;
        }
    else accumulator = -accumulator;
    }

static void Nop(void)
    {
    /* fine just like this */
    }

static void Norm(void)
    {
    if (accumulator == 0) tc = 1;
    else
        {
        if (read_bit(accumulator, 31) == read_bit(accumulator, 30))
            {
            tc = 0;
            accumulator <<= 1;
            Mar();
            }
        else tc = 1;
        }
    }

static void Or(void)
    {
    fetch_operand();
    accumulator |= operand;
    }

static void Ork(void)
    {
    long_immediate_with_shift();
    accumulator |= operand;
    }

static void Out(void)
    {
    fetch_operand();
    port_addr = (instruction >> 8) & 15;
    ports[port_addr] = (int) operand;
    }

static void Pac(void)
    {
    accumulator = shifted_p();
    }

static void Pop(void)
    {
    accumulator = c25_hardware_pop();
    }

static void Popd(void)
    {
    store_result(c25_hardware_pop());
    }

static void Pshd(void)
    {
    fetch_operand();
    c25_hardware_push((int) operand);
    }

static void Push(void)
    {
    c25_hardware_push((int) accumulator);
    }

static void Rc(void)
    {
    c = 0;
    }

static void Ret(void)
    {
    pc = c25_hardware_pop();
    branch_destination = pc;
    }

static void Rfsm(void) { fsm = 0; }

static void Rhm(void) { hm = 0; }

static void Rol(void)
    {
    if (accumulator & SIGN_BIT)
        {
        accumulator = (accumulator << 1) + c;
        c = 1;
        }
    else
        {
        accumulator = (accumulator << 1) + c;
        c = 0;
        }
    }

static void Ror(void)
    {
    if (accumulator & 1)
        {
        accumulator = (accumulator >> 1) + (c ? SIGN_BIT : 0);
        c = 1;
        }
    else
        {
        accumulator = (accumulator >> 1) + (c ? SIGN_BIT : 0);
        c = 0;
        }
    }

static void Rovm(void)
    {
    ovm = 0;
    }

static void Rpt(void)
    {
    fetch_operand();
    rptc = (int) operand;
    repeat_instruction();
    }

static void Rptk(void)
    {
    rptc = (int) (instruction & 0xFF);
    repeat_instruction();
    }

static void Rsxm(void)
    {
    sxm = 0;
    }

static void Rtc(void)
    {
    tc = 0;
    }

static void Rtxm(void)
    {
    txm = 0;
    }

static void Rxf(void)
    {
    xf = 0;
    }

static void Sach(void)
    {
    operand = accumulator;
    operand <<= (instruction >> 8) & 7;
    store_result((int) (operand >> 16));
    }

static void Sacl(void)
    {
    operand = accumulator;
    operand <<= (instruction >> 8) & 7;
    store_result((int) operand);
    }

static void Sar(void)
    {
    int r;

    r = ar[(instruction >> 8) & 7];
    store_result(r);
    }

static void Sblk(void)
    {
    long_immediate_with_shift();
    sub_operand(0);
    }

static void Sbrk(void)
    {
    ar[arp] -= instruction & 0xFF;
    }

static void Sc(void) { c = 1; }

static void Sfl(void)
    {
    accumulator <<= 1;
    }

static void Sfr(void)
    {
    if (sxm && (accumulator & SIGN_BIT))
        accumulator = SIGN_BIT | (accumulator >> 1);
    else
        accumulator >>= 1;
    }

static void Sfsm(void)
    {
    fsm = 1;
    }

static void Shm(void)
    {
    hm = 1;
    }

static void Sovm(void)
    {
    ovm = 1;
    }

static void Spac(void)
    {
    operand = shifted_p();
    sub_operand(0);
    }

static void Sph(void)
    {
    operand = shifted_p();
    store_result((int) (operand >> 16));
    }

static void Spl(void)
    {
    operand = shifted_p();
    store_result((int) operand);
    }

static void Spm(void)
    {
    pm = instruction & 3;
    }

static void Sqra(void)
    {
    Apac();
    Lt();
    p_register = t_register;
    p_register *= p_register;
    }

static void Sqrs(void)
    {
    Spac();
    Lt();
    p_register = t_register;
    p_register *= p_register;
    }

static void Sst(void)
    {
    int st0;

    st0 = (arp << 13) & 0xE000;
    set_bit(st0, ov, 12);
    set_bit(st0, ovm, 11);
    set_bit(st0, intm, 9);
    st0 |= dp & 0x1FF;
    store_result(st0);
    }

static void Sst1(void)
    {
    int st1;

    st1 = (arb << 13) & 0xE000;
    set_bit(st1, cnf, 12);
    set_bit(st1, tc, 11);
    set_bit(st1, sxm, 10);
    set_bit(st1, c, 9);
    set_bit(st1, hm, 6);
    set_bit(st1, fsm, 5);
    set_bit(st1, xf, 4);
    set_bit(st1, fo, 3);
    set_bit(st1, txm, 2);
    st1 |= pm & 3;
    store_result(st1);
    }

static void Ssxm(void)
    {
    sxm = 1;
    }

static void Stc(void)
    {
    tc = 1;
    }

static void Stxm(void)
    {
    txm = 1;
    }

static void Sub(void)
    {
    fetch_operand();
    sign_extend(&operand);
    shift_operand();
    sub_operand(0);
    }

static void Subb(void)
    {
    fetch_operand();
    sub_operand(c);
    }

static void Subc(void)
    {
    long int alu_out;

    old_accum = accumulator;
    fetch_operand();
    shift_operand();
    alu_out = accumulator - operand;
    if (alu_out >= 0)
        {
        c = (alu_out & SIGN_BIT) ? 1 : 0;
        accumulator = (alu_out << 1) + 1;
        }
    else
        {
        c = (accumulator & SIGN_BIT) ? 1 : 0;
        accumulator = accumulator << 1;
        }
    if (detect_overflow(operand)) ov = 1;
    }

static void Subh(void)
    {
    int old_c;

    c = old_c;
    fetch_operand();
    operand = ((operand << 16) & 0xFFFF0000L);
    sub_operand(0);
    c |= old_c;
    }

static void Subk(void)
    {
    operand = instruction & 0xFF;
    sub_operand(0);
    }

static void Subs(void)
    {
    fetch_operand();
    sub_operand(0);
    }

static void Subt(void)
    {
    int shift;

    shift = t_register & 15;
    fetch_operand();
    operand <<= shift;
    sub_operand(0);
    }

static void Sxf(void)
    {
    xf = 1;
    }

static void Tblr(void)
    {
    if (!repeating) pfc = (int) accumulator;
    store_result(read_program_memory(pfc++));
    }

static void Tblw(void)
    {
    if (!repeating) pfc = (int) accumulator;
    fetch_operand();
    *program_memory_address(pfc++) = (int) operand;
    }

static void Trap(void)
    {
    c25_interrupt(TRAP);
    }

static void Xor(void)
    {
    fetch_operand();
    accumulator ^= operand;
    }

static void Xork(void)
    {
    long_immediate_with_shift();
    accumulator ^= operand;
    }

static void Zac(void)
    {
    accumulator = 0;
    }

static void Zalh(void)
    {
    fetch_operand();
    accumulator = operand << 16;
    }

static void Zalr(void)
    {
    fetch_operand();
    accumulator = (operand << 16) + 0x8000L;
    }

static void Zals(void)
    {
    fetch_operand();
    accumulator = operand & 0x0000FFFFL;
    }

/* ======================================================================= */
/*                               Loading Stuff                             */
/* ======================================================================= */

static int char_to_digit(char c)
    {
    if (c >= '0' && c <= '9') return c - '0';
    if (c >= 'A' && c <= 'F') return c + 10 - 'A';
    if (c >= 'a' && c <= 'f') return c + 10 - 'a';
    sprintf(toprint, "Wierd character in hex file!\n");
    __write(toprint);
    return 0;
    }

static int extract_byte(char *s, int *p)
    {
    int sum;

    sum = char_to_digit(s[(*p)++]) << 4;
    sum += char_to_digit(s[(*p)++]);
    return sum;
    }

static int extract_int(char *s, int *p)
    {
    int sum;

    sum = extract_byte(s, p) << 8;
    sum += extract_byte(s, p);
    return sum;
    }

void load_hex_files(char *word)
    {
    int a, i, j, bhi, blo, nbytes;
    char *p;
    memblk *q, *r;

    for (i = 0; i < (int) strlen(word); i++) word[i] = toupper(word[i]);

    sprintf(fname, "%s.HI", word);
    fhi = fopen(fname, "r");
    if (fhi == NULL)
        {
        sprintf("Can't read file %s\n", fname);
        return;
        }

    sprintf(fname, "%s.LO", word);
    flo = fopen(fname, "r");
    if (flo == NULL)
        {
        sprintf(toprint, "Can't read file %s\n", fname);
        __write(toprint);
        return;
        }

    if (strstr(word, ".HEX") != NULL) goto SixteenBits;
    if ((p = strchr(word, '.')) != NULL) *p = '\0';

    /* if there is already memory in use, free() that all up */
    q = first_memblk;
    while (q != NULL)
        {
        r = q->next;
        free(q);
        q = r;
        }
    first_memblk = NULL;

    warn_progmem_writes = 0;
    while (!feof(fhi))
        {
        fgets(fhiline, 80, fhi);
        fgets(floline, 80, flo);
        j = 1;
        nbytes = extract_byte(fhiline, &j);
        a = extract_int(fhiline, &j);
        j += 2; /* filler */
        for (i = 0; i < nbytes; i++)
            {
            bhi = extract_byte(fhiline, &j);
            j -= 2;
            blo = extract_byte(floline, &j);
            write_program_memory(a++, ((bhi & 0xFF) << 8) | (blo & 0xFF));
            }
        }
    warn_progmem_writes = 1;
    c25_reset();
    return;

SixteenBits:
    fhi = fopen(word, "r");
    if (fhi == NULL)
        {
        sprintf(toprint, "Can't read file %s\n", word);
        __write(toprint);
        return;
        }

    /* if there is already memory in use, free() that all up */
    q = first_memblk;
    while (q != NULL)
        {
        r = q->next;
        free(q);
        q = r;
        }
    first_memblk = NULL;

    warn_progmem_writes = 0;
    while (!feof(fhi))
        {
        fgets(fhiline, 80, fhi);
        j = 1;
        nbytes = extract_byte(fhiline, &j);
        a = extract_int(fhiline, &j);
        extract_byte(fhiline, &j);  /* filler */
        for (i = 0; i < nbytes / 2; i++)
            write_program_memory(a++, extract_int(fhiline, &j));
        }
    warn_progmem_writes = 1;
    c25_reset();
    }

/* ======================================================================= */
/*                                   Reports                               */
/* ======================================================================= */

static void print_binary(char *name, int x, int n)
    {
    int mask = 1 << (n - 1);

    __write(name);
    __write(":");
    while (mask)
        {
        if (x & mask) __write("1");
        else __write("0");
        mask >>= 1;
        }
    sprintf(toprint, "(%d) ", x);
    __write(toprint);
    }

static void print_bit(char *name, int x)
    {
    __write(name);
    __write(":");
    if (x) __write("1 ");
    else __write("0 ");
    }

void short_report(void)
    {
    int i;

    __write("PC:");
    phex(pc); 
    sprintf(toprint,
        "  Instructions executed: %ld   Processor cycles: %ld\n",
        steps_taken, cycles);
    __write(toprint);
    i = read_program_memory(pc);
    __write("Instruction: ");
    phex(i); 
    __write("   ");
    if ((i = lookup(i)) != -1) __write(opcodes[i].opname);
    else __write("ILLEGAL OPCODE");
    if (idle_mode) __write("   (idling)");
    __write("\n");
    }

void long_report(void)
    {
    int i;

    print_matching_symbols(pc);
    short_report();
    __write("ACC:");
    plhex(accumulator); 
    print_binary("ARP", arp, 3);
    __write("\n");

    __write("ARs: ");
    for (i = 0; i < 8; i++) phex(ar[i]);
    __write("\n");

    __write("Hardware stack: ");
    for (i = _sp - 1; i >= 0; i--) phex(_stack[i]);
    if (_sp == 0) __write("empty");
    __write("\n");

    /* ST0 */
    print_bit("OV", ov);
    print_bit("OVM", ovm);
    print_bit("INTM", intm);
    print_binary("DP", dp, 9);
    __write("\n");
    /* ST1 */
    print_binary("ARB", arb, 3);
    print_bit("CNF", cnf);
    print_bit("TC", tc);
    print_bit("SXM", sxm);
    print_bit("C", c);
    print_bit("HM", hm);
    print_bit("FSM", fsm);
    print_bit("XF", xf);
    print_bit("FO", fo);
    print_bit("TXM", txm);
    print_binary("PM", pm, 2);
    __write("\n");
    __write("T:");
    phex(t_register);
    __write("P:");
    plhex(p_register);
    __write("\n");

    __write("DRR:");
    phex(drr);
    __write("DXR:");
    phex(dxr);
    __write("TIM:");
    phex(tim);
    __write("PRD:");
    phex(prd);
    __write("IMR:");
    phex(imr);
    __write("GREG:");
    phex(greg);
    __write("\n");
    }

/* ======================================================================= */
/*                                 Breakpoints                             */
/* ======================================================================= */

static int find_breakpoint(int d)
    {
    int i;

    for (i = 0; i < number_breakpoints; i++)
        if (breakpoint_list[i] == d) return i;
    return -1;
    }

void add_breakpoint(int a)
    {
    if (find_breakpoint(a) != -1) return;
    if (number_breakpoints == MAX_NUM_BRKPTS)
        {
        __write("No more room for breakpoints!\n");
        return;
        }
    breakpoint_list[number_breakpoints++] = a;
    }

void delete_breakpoint(int a)
    {
    a = find_breakpoint(a);
    if (a == -1) return;
    breakpoint_list[a] = breakpoint_list[--number_breakpoints];
    }

void clear_all_breakpoints(void)
    {
    number_breakpoints = 0;
    }

int go_til_breakpoint(long int n)
    {
    int p;
    unsigned long int limit;

    limit = steps_taken + n;
    advance();
    while ((p = find_breakpoint(pc)) == -1 &&
                        !kbhit() && (steps_taken < limit || n == -1))
        advance();
    if (kbhit()) return getch();
    if (p != -1) return -1;
    return -2;
    }
