OpenCores
URL https://opencores.org/ocsvn/light52/light52/trunk

Subversion Repositories light52

[/] [light52/] [trunk/] [tools/] [b51/] [src/] [b51_cpu.c] - Rev 4

Compare with Previous | Blame | View Log

 
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
 
#include "b51_cpu.h"
#include "b51_mcu.h"
#include "b51_log.h"
#include "util/ihex.h"
 
 
/*-- Local data types & macros -----------------------------------------------*/
 
typedef enum cpu_op_e {
    add, addc, subb,
    alu_mul, alu_div,
    da,
    rrc, rlc, rr, rl,
    setb_c, clr_c, anl_c, orl_c,
    cjne
} cpu_op_t;
 
/** Cycle count information for an opcode. */
typedef struct {
    int min;    /**< Minimum number of cycles. */
    int max;    /**< Maximum number of cycles. */
} cycle_count_t;
 
/** Cycle count table for light52 core. */
cycle_count_t cycle_count[256] = {
    { 2, 2}, { 5, 5}, { 6, 6}, { 3, 3}, { 3, 3}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 7, 8}, { 7, 7}, { 8, 8}, { 3, 3}, { 3, 3}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 6, 7}, { 5, 5}, { 7, 7}, { 3, 3}, { 4, 4}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 6, 7}, { 7, 7}, { 7, 7}, { 3, 3}, { 4, 4}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 3, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 4, 4}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 3, 5}, { 7, 7}, { 5, 5}, { 5, 5}, { 4, 4}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 3, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 4, 4}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 3, 5}, { 7, 7}, { 5, 5}, { 4, 4}, { 4, 4}, { 5, 5}, { 5, 5}, { 5, 5},
    { 4, 4}, { 4, 4}, { 4, 4}, { 4, 4}, { 4, 4}, { 4, 4}, { 4, 4}, { 4, 4},
    { 5, 5}, { 5, 5}, { 5, 5}, { 4, 4}, {10,10}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 5, 5}, { 7, 7}, { 5, 5}, { 4, 4}, { 4, 4}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 5, 5}, { 5, 5}, { 5, 5}, { 3, 3}, { 3, 3}, { 2, 2}, { 6, 6}, { 6, 6},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 5, 5}, { 7, 7}, { 5, 5}, { 3, 3}, { 5, 6}, { 6, 7}, { 8, 9}, { 8, 9},
    { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 3, 3}, { 3, 3}, { 6, 6}, { 7, 7}, { 7, 7},
    { 6, 6}, { 6, 6}, { 6, 6}, { 6, 6}, { 6, 6}, { 6, 6}, { 6, 6}, { 6, 6},
    { 5, 5}, { 7, 7}, { 5, 5}, { 3, 3}, { 2, 2}, { 7, 8}, { 2, 2}, { 2, 2},
    { 7, 8}, { 7, 8}, { 7, 8}, { 7, 8}, { 7, 8}, { 7, 8}, { 7, 8}, { 7, 8},
    { 3, 3}, { 5, 5}, { 6, 6}, { 6, 6}, { 3, 3}, { 5, 5}, { 7, 7}, { 7, 7},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    { 3, 3}, { 7, 7}, { 5, 5}, { 5, 5}, { 3, 3}, { 4, 4}, { 5, 5}, { 5, 5},
    { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5}, { 5, 5},
    };
 
 
 
 
/*-- Local function prototypes -----------------------------------------------*/
 
 
static uint8_t cpu_fetch(cpu51_t *cpu);
static uint16_t cpu_fetch16(cpu51_t *cpu);
static uint16_t cpu_fetch11(cpu51_t *cpu, uint8_t opcode);
static uint8_t cpu_xcode_read(cpu51_t *cpu, uint16_t addr);
static uint8_t cpu_xdata_read(cpu51_t *cpu, uint16_t addr);
static void cpu_xdata_write(cpu51_t *cpu, uint16_t addr, uint8_t value);
static void cpu_set_rn(cpu51_t *cpu, uint8_t n, uint8_t value);
static void cpu_set_a(cpu51_t *cpu, uint8_t value);
static uint8_t cpu_update_flags(cpu51_t *cpu, uint8_t x, uint8_t y, cpu_op_t op);
static uint8_t cpu_compute_ac(cpu51_t *cpu, uint8_t x, uint8_t y, cpu_op_t op);
static void cpu_set_dir(cpu51_t *cpu, uint8_t dir, uint8_t value);
static void cpu_set_idata(cpu51_t *cpu, uint8_t dir, uint8_t value);
static uint8_t cpu_get_dir(cpu51_t *cpu, uint8_t dir);
static uint8_t cpu_get_idata(cpu51_t *cpu, uint8_t dir);
static bool cpu_rel_jump(cpu51_t *cpu, uint8_t rel);
static void cpu_set_sfr(cpu51_t *cpu, uint8_t dir, uint8_t value);
static uint8_t cpu_get_sfr(cpu51_t *cpu, uint8_t dir);
static uint8_t cpu_get_bit_address(cpu51_t *cpu, uint8_t bit);
static uint8_t cpu_get_bit(cpu51_t *cpu, uint8_t bit);
static void cpu_set_bit(cpu51_t *cpu, uint8_t bit, uint8_t value);
 
static bool cpu_exec_rn(cpu51_t *cpu, uint8_t opcode);
static bool cpu_exec_upper_half(cpu51_t *cpu, uint8_t opcode);
 
 
/*-- Public functions --------------------------------------------------------*/
 
extern uint16_t cpu_load_code(cpu51_t *cpu, const char *hex_filename){
    FILE *fp;
    IHexRecord irec;
    uint16_t i, target, bytes_read=0;
 
    fp = fopen(hex_filename, "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 0;
    }
 
    while (Read_IHexRecord(&irec, fp) == IHEX_OK) {
        /* Debug: print read record */
        #if 0
        Print_IHexRecord(&irec);
        printf("\n");
        #endif
        /* Move data from record to XCODE space, no questions asked */
        /* FIXME XCODE size hardcoded here */
        target = irec.address;
        for(i=0; i < irec.dataLen && (target+i)<65536; i++){
            cpu->mcu.xcode[target + i] = irec.data[i];
        }
        bytes_read += irec.dataLen;
    }
 
    fclose(fp);
 
    if(bytes_read > 0){
        printf("Read %d code bytes from '%s'\n", bytes_read, hex_filename);
    }
 
    return bytes_read;
}
 
extern void cpu_init(cpu51_t *cpu){
    /* Not much to init in the CPU so far */
    cpu->breakpoint = 0xffff; /* FIXME implementation of BP is flimsy */
    cpu->log.executed_instructions = 0;
    /* Set the core implementation options to their default value */
    cpu->options.bcd = false;
    /* now init the MCU model -- peripherals */
    mcu_init(&cpu->mcu);
}
 
extern void cpu_reset(cpu51_t *cpu){
    /* Give CPU registers their reset value, if any */
    cpu->sfr.dph = 0x00;
    cpu->sfr.dpl = 0x00;
    cpu->sfr.sp = 0x07;
    cpu->sfr.psw = 0x00;
    cpu->pc = 0x0000;
    cpu->cycles = 0;
    /* FIXME reset interrupt level */
    /* Now reset the MCU model -- peripherals */
    mcu_reset(&cpu->mcu);
}
 
extern bool cpu_add_breakpoint(cpu51_t *cpu, uint16_t address){
    cpu->breakpoint = address;
    return true;
}
 
extern uint32_t cpu_exec(cpu51_t *cpu, uint32_t num_inst){
    uint8_t opcode;
    uint32_t i;
    bool ok;
    uint32_t cycles;
 
    for(i=0;i<num_inst;i++){
        log_baseline(&(cpu->log), cpu->pc, cpu->sfr.sp, cpu->a, cpu->sfr.psw);
 
        if(cpu->pc == cpu->breakpoint){
            printf("BREAKPOINT hit at %04Xh\n", cpu->breakpoint);
            return 2;
        }
 
        opcode = cpu_fetch(cpu);
        cpu->max_cycle_count = false;
        cpu->implemented_as_nop = false;
 
        if((opcode & 0x08)!=0){
            /* bottom half of decoding table */
            ok = cpu_exec_rn(cpu, opcode);
        }
        else{
            /* top half of decoding table */
            ok = cpu_exec_upper_half(cpu, opcode);
        }
 
        /* Update cycle counter... */
        if(cpu->implemented_as_nop){
            /* Instruction is not implemented as per command line parameters;
               it was executed as NOP so its cycle count is that of NOP.*/
            cycles = cycle_count[0].min;
        }
        else{
            if(cpu->max_cycle_count){
                cycles = cycle_count[opcode].max;
            }
            else{
                cycles = cycle_count[opcode].min;
            }
        }
        cpu->cycles += cycles;
 
        mcu_update(&cpu->mcu, cycles);
        log_status(&(cpu->log), cpu->sfr.sp, cpu->a, cpu->sfr.psw);
 
        cpu->log.executed_instructions++;
 
        /* Break execution on any kind of trouble */
        /* FIXME handle execution faults */
        if(!ok) {
            return 1;
            break;
        }
    }
 
    return 0;
}
 
/*-- Local functions ---------------------------------------------------------*/
 
/**
    Get the byte pointed to by PC, increment PC.
 
    This function and cpu_xcode_read encapsulate code space addressing.
 
    @arg CPU object.
    @return Opcode at PC.
    @sideeffect CPU state updated accordingly.
*/
static uint8_t cpu_fetch(cpu51_t *cpu){
    return cpu_xcode_read(cpu, cpu->pc++);
}
 
static uint16_t cpu_fetch16(cpu51_t *cpu){
    uint8_t hi, lo;
    uint16_t word;
 
    hi = cpu_fetch(cpu);
    lo = cpu_fetch(cpu);
    word = ((uint16_t)hi << 8) + (uint16_t)lo;
    return word;
}
 
static uint16_t cpu_fetch11(cpu51_t *cpu, uint8_t opcode){
    uint8_t hi, lo, tmp;
    uint16_t word;
 
    lo = cpu_fetch(cpu);
    hi = (opcode >> 5) & 0x07;
    tmp = (cpu->pc >> 8) & 0xf8;
    hi |= tmp;
    word = ((uint16_t)hi << 8) + (uint16_t)lo;
    return word;
}
 
 
static uint8_t cpu_xcode_read(cpu51_t *cpu, uint16_t addr){
    return cpu->mcu.xcode[addr];
}
 
 
static uint8_t cpu_xdata_read(cpu51_t *cpu, uint16_t addr){
    return cpu->mcu.xdata[addr];
}
 
static void cpu_xdata_write(cpu51_t *cpu, uint16_t addr, uint8_t value){
    /*if(cpu->mcu.xdata[addr]!=value){*/
        log_xdata(&(cpu->log), addr, value);
    /*}*/
    cpu->mcu.xdata[addr] = value;
}
 
 
static uint8_t cpu_get_rn(cpu51_t *cpu, uint8_t n){
    uint8_t idata_address;
 
    idata_address = (cpu->sfr.psw & 0x18) + (n & 0x07);
    return cpu->idata[idata_address];
}
 
static void cpu_set_rn(cpu51_t *cpu, uint8_t n, uint8_t value){
    uint8_t idata_address;
 
    idata_address = (cpu->sfr.psw & 0x18) + (n & 0x07);
    cpu_set_idata(cpu, idata_address, value);
}
 
static void cpu_set_a(cpu51_t *cpu, uint8_t value){
    uint8_t p, t, i;
 
    cpu->a = value;
 
    /* Update P flag */
    t = cpu->a;
    p = 0;
    for(i=0;i<8;i++){
        p = p ^ (t & 0x01);
        t = t >> 1;
    }
    cpu->sfr.psw &= 0x0fe;
    cpu->sfr.psw |= (p & 0x01);
}
 
 
static uint8_t set_bit(uint8_t target, uint8_t index, uint8_t value){
    index &= 0x07;
    target &= ~(1 << index);
    value &= 0x01;
    target |= (value << index);
    return target;
}
 
static uint8_t cpu_compute_ac(cpu51_t *cpu, uint8_t x, uint8_t y, cpu_op_t op){
 
  if(op==subb){
    x = x & 0x0f;
    y = y & 0x0f;
    if((cpu->sfr.psw)&0x80){
      y++;
    }
    return (x<y)?1:0;
  }
  else{
    x = x & 0x0f;
    y = y & 0x0f;
    if((cpu->sfr.psw)&0x80){
      y++;
    }
    return ((x+y)>0x0f)?1:0;
  }
}
 
static uint8_t cpu_update_flags(cpu51_t *cpu, uint8_t s, uint8_t d, cpu_op_t op){
    uint16_t res, x, y, res_half, rem;
    uint16_t cy=0, ov=0, ac=0;
 
    x = (uint16_t)s;
    y = (uint16_t)d;
 
    switch(op){
    case add:
        res = x + y;
        res_half = (x & 0x0f) + (y & 0x0f);
        cy = (res & 0x0100)? 1 : 0;
        ac = (res_half & 0x0010)? 1 : 0;
        if((x < 0x80) && (y < 0x80)){
            ov = (res & 0x0080)? 1 : 0;
        }
        else if((x >= 0x80) && (y >= 0x80)){
            ov = (~(res & 0x0080))? 1 : 0;
        }
        else{
            ov = 0;
        }
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 6, ac);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 2, ov);
        break;
    case subb:
        ac = cpu_compute_ac(cpu, x, y, subb);
        y ^= 0xffff;
        y += 1;
        res = x + y;
        if(cpu->sfr.psw & 0x80){
            res--;
        }
        cy = (res & 0x0100)? 1 : 0;
 
        if((x < 0x80) && (y >= 0x80)){
            /* positive - negative = positive, otherwise OV */
            ov = (res & 0x0080)? 1 : 0;
        }
        else if((x >= 0x80) && (y < 0x80)){
            /* negative - positive = negative, otherwise OV */
            ov = (~(res & 0x0080))? 1 : 0;
        }
        else{
            ov = 0;
        }
 
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 6, ac);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 2, ov);
        break;
    case addc:
        res = x + y;
        res_half = (x & 0x0f) + (y & 0x0f);
        if(cpu->sfr.psw & 0x80){
            res++;
            res_half++;
        }
        cy = (res & 0x0100)? 1 : 0;
        ac = (res_half & 0x0010)? 1 : 0;
        if((x < 0x80) && (y < 0x80)){
            ov = (res & 0x0080)? 1 : 0;
        }
        else if((x >= 0x80) && (y >= 0x80)){
            ov = (~(res & 0x0080))? 1 : 0;
        }
        else{
            ov = 0;
        }
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 6, ac);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 2, ov);
        break;
    case alu_mul:
        res = x * y;
        ov = (res & 0xff00)? 1 : 0;
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, 0);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 2, ov);
        /* special case: update B right here */
        cpu->sfr.b = (res>>8)&0xff;
        res = res & 0xff;
        break;
    case alu_div:
        if(y != 0){
            res = x / y;
            rem = x % y;
            cy = 0;
            ov = 0;
            /* special case: update B right here */
            cpu->sfr.b = rem;
        }
        else{
            ov = 1;
            cy = 0;
            /* Quotient and remainder are undefined in Intel specs; we'll use
               light52 actual values for consistency of the SW/HW simulations */
            res = 0xff;
            rem = 0x00;
        }
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 2, ov);
        break;
    case da:
        x = s & 0x0f;
        y = s;
        if((x > 9) || ((cpu->sfr.psw&0x40)!=0)){
            y += 0x06;
        }
        x = (y >> 4) & 0x1f;
        if((x > 9) || ((cpu->sfr.psw&0x80)!=0)){
            y += 0x60;
        }
        res = y & 0x0ff;
        /* DA can SET C but can't clear it if it's set */
        if(y > 0x0ff){
            cpu->sfr.psw |= 0x80;
        }
        break;
    case rlc:
        res = (s << 1) | (cpu->sfr.psw >> 7);
        cy = (s >> 7) & 0x01;
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        break;
    case rl:
        res = (s << 1) | ((s >> 7) & 0x01);
        break;
    case rrc:
        cy = (s & 0x01)? 1 : 0;
        res = ((s >> 1) & 0x7f) | (cpu->sfr.psw & 0x80);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        break;
    case rr:
        res = ((s >> 1) & 0x7f) | ((s << 7) & 0x80);
        break;
    case setb_c:
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, 1);
        res = 0; /* unused ret value */
        break;
    case clr_c:
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, 0);
        res = 0; /* unused ret value */
        break;
    case anl_c:
        s = (cpu->sfr.psw >> 7);
        cy = s & d;
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        break;
    case orl_c:
        s = (cpu->sfr.psw >> 7);
        cy = s | d;
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, cy);
        break;
    case cjne:
        if(x < y){
            cpu->sfr.psw |= 0x80;
        }
        else{
            cpu->sfr.psw &= ~0x80;
        }
        res = 0;
        break;
    }
 
    return res;
}
 
static void cpu_set_sfr(cpu51_t *cpu, uint8_t dir, uint8_t value){
    uint16_t undefined_sfr=0;
 
    if(dir < 0x80) return;
 
    switch(dir){
    case 0xe0:  /* ACC */
        cpu_set_a(cpu, value);
        break;
    case 0xf0:  /* B */
        cpu->sfr.b = value;
        break;
    case 0x83:  /* DPH */
        cpu->sfr.dph = value;
        break;
    case 0x82:  /* DPL */
        cpu->sfr.dpl = value;
        break;
    case 0xa8:  /* IE */
        cpu->sfr.ie = value;
        break;
    case 0xb8:  /* IP */
        cpu->sfr.ip = value;
        break;
    case 0x0d0: /* PSW */
        /* The P flag can't be overwritten */
        cpu->sfr.psw = (value & 0xfe) | (cpu->sfr.psw & 0x01);
        break;
    case 0x081: /* SP */
        cpu->sfr.sp = value;
        break;
    default:
        undefined_sfr = mcu_set_sfr(&cpu->mcu, dir, value);
        if(undefined_sfr){
            /* TODO unimplemented SFR addr message should be optional */
            printf("(%04X) UNIMPLEMENTED SFR %02X\n", cpu->log.pc, dir);
        }
    }
    log_sfr(&(cpu->log), dir, value);
}
 
static uint8_t cpu_get_sfr(cpu51_t *cpu, uint8_t dir){
    uint16_t sfr_value;
 
    /* TODO this is a bug and some fault flag should be raised */
    if(dir < 0x80) return 0;
 
    switch(dir){
    case 0xe0:  /* ACC */
        return cpu->a;
        break;
    case 0xf0:  /* B */
        return cpu->sfr.b;
        break;
    case 0x83:  /* DPH */
        return cpu->sfr.dph;
        break;
    case 0x82:  /* DPL */
        return cpu->sfr.dpl;
        break;
    case 0xa8:  /* IE */
        return cpu->sfr.ie;
        break;
    case 0xb8:  /* IP */
        return cpu->sfr.ip;
        break;
    case 0x0d0: /* PSW */
        return cpu->sfr.psw;
        break;
    case 0x081: /* SP */
        return cpu->sfr.sp;
        break;
    default:
        sfr_value = mcu_get_sfr(&cpu->mcu, dir);
        if(sfr_value==0xffff){
            /* TODO unimplemented SFR addr message should be optional */
            printf("(%04X) UNIMPLEMENTED SFR %02X\n", cpu->log.pc, dir);
        }
        else{
            return (uint8_t)sfr_value;
        }
    }
 
    /* Control will never actually reach here */
    return 0;
}
 
static void cpu_set_idata(cpu51_t *cpu, uint8_t dir, uint8_t value){
    /* FIXME check bounds */
    cpu->idata[dir] = value;
    log_idata(&(cpu->log), dir, value);
}
 
static void cpu_set_dir(cpu51_t *cpu, uint8_t dir, uint8_t value){
    /* Direct addressing mode is special; can access both idata and sfr */
    if(dir > 0x07f){
        /* In direct addressing mode, 0x80 to 0xff is actually an SFR address */
        cpu_set_sfr(cpu, dir, value);
    }
    else{
        /* From 0x00 to 0x7f, we're addressing regular idata RAM */
        cpu->idata[dir] = value;
        log_idata(&(cpu->log), dir, value);
    }
}
 
static uint8_t cpu_get_dir(cpu51_t *cpu, uint8_t dir){
    /* Direct addressing mode is special; can access both idata and sfr */
    if(dir > 0x07f){
        /* In direct addressing mode, 0x80 to 0xff is actually an SFR address */
        return cpu_get_sfr(cpu, dir);
    }
    else{
        /* From 0x00 to 0x7f, we're addressing regular idata RAM */
        return cpu->idata[dir];
    }
}
 
static uint8_t cpu_get_idata(cpu51_t *cpu, uint8_t dir){
    /* FIXME check bounds */
    return cpu->idata[dir];
}
 
 
static bool cpu_rel_jump(cpu51_t *cpu, uint8_t rel){
    int16_t target, offset;
 
    target = (int16_t)cpu->pc;
    offset = (int16_t)((int8_t)rel);
    target += offset;
    cpu->pc = (uint16_t)target;
    return log_jump(&(cpu->log), cpu->pc);
}
 
 
static uint8_t cpu_get_bit_address(cpu51_t *cpu, uint8_t bit){
    uint8_t addr;
 
    if(bit > 0x7f){
        addr = bit & 0xf8;
    }
    else{
        addr = 0x20 + (bit >> 3);
    }
 
    return addr;
}
 
 
static uint8_t cpu_get_bit(cpu51_t *cpu, uint8_t bit){
    uint8_t dir, res, value;
 
    dir = cpu_get_bit_address(cpu, bit);
    res = cpu_get_dir(cpu, dir);
    value = (res & (1 << (bit & 0x07)))? 1 : 0;
 
    return value;
}
 
 
static void cpu_set_bit(cpu51_t *cpu, uint8_t bit, uint8_t value){
    uint8_t dir, res;
 
    dir = cpu_get_bit_address(cpu, bit);
    res = cpu_get_dir(cpu, dir);
    res = set_bit(res, bit, value);
    cpu_set_dir(cpu, dir, res);
}
 
 
static bool cpu_exec_upper_half(cpu51_t *cpu, uint8_t opcode){
    uint8_t imm, dir, dir2, bit, res, rel, val;
    uint8_t hi, lo;
    uint16_t address;
    bool ok = true, endless_loop = false;
 
 
    switch(opcode){
    /*-- ROW 0 ---------------------------------------------------------------*/
    case 0x00:  /* NOP */
        break;
    case 0x10: /* Jcc bit, rel */
    case 0x20:
    case 0x30:
        bit = cpu_fetch(cpu);
        rel = cpu_fetch(cpu);
        dir = cpu_get_bit_address(cpu, bit);
        res = cpu_get_dir(cpu, dir);
        switch(opcode){
        case 0x10:  /* JBC bit, rel */
            val = res & (1 << (bit & 0x07));
            res &= ~(1 << (bit & 0x07));
            cpu_set_dir(cpu, dir, res);
            if(val){
                cpu_rel_jump(cpu, rel);
                cpu->max_cycle_count = true;
            }
            break;
        case 0x20:  /* JB bit, rel */
            if(res & (1 << (bit & 0x07))){
                cpu_rel_jump(cpu, rel);
                cpu->max_cycle_count = true;
            }
            break;
        case 0x30:  /* JC bit, rel */
            if((res & (1 << (bit & 0x07))) ==0){
                cpu_rel_jump(cpu, rel);
                cpu->max_cycle_count = true;
            }
            break;
        }
        break;
    case 0x40:  /* JC rel */
        rel = cpu_fetch(cpu);
        if(cpu->sfr.psw & 0x80){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0x50:  /* JNC rel */
        rel = cpu_fetch(cpu);
        if(!(cpu->sfr.psw & 0x80)){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0x60:  /* JZ rel */
        rel = cpu_fetch(cpu);
        if(cpu->a == 0){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0x70:  /* JNZ rel */
        rel = cpu_fetch(cpu);
        if(cpu->a != 0){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0x80:  /* SJMP rel */
        rel = cpu_fetch(cpu);
        endless_loop = cpu_rel_jump(cpu, rel);
        break;
    case 0x90:  /* MOV DPTR, #addr16 */
        hi = cpu_fetch(cpu);
        lo = cpu_fetch(cpu);
        cpu->sfr.dph = hi;
        cpu->sfr.dpl = lo;
        log_sfr(&(cpu->log), 0x83, hi);
        log_sfr(&(cpu->log), 0x82, lo);
        break;
    case 0xa0:  /* ORL C, /bit */
        bit = cpu_fetch(cpu);
        res = 1 - cpu_get_bit(cpu, bit);
        cpu->sfr.psw  |= (res << 7);
        break;
    case 0xb0:  /* ANL C, /bit */
        bit = cpu_fetch(cpu);
        res = 1 - cpu_get_bit(cpu, bit);
        cpu->sfr.psw  &= (res << 7);
        break;
    case 0xc0:  /* PUSH dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        cpu->sfr.sp++;
        cpu_set_idata(cpu, cpu->sfr.sp, res);
        break;
    case 0xd0:  /* POP dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_idata(cpu, cpu->sfr.sp--);
        cpu_set_dir(cpu, dir, res);
        break;
    case 0xe0:  /* MOVX A, @DPTR */
        address = (((uint16_t)cpu->sfr.dph) << 8) + (uint16_t)cpu->sfr.dpl;
        res = cpu_xdata_read(cpu, address);
        cpu_set_a(cpu, res);
        break;
    case 0xf0:  /* MOVX @DPTR, A */
        address = (((uint16_t)cpu->sfr.dph) << 8) + (uint16_t)cpu->sfr.dpl;
        cpu_xdata_write(cpu, address, cpu->a);
        break;
    /*-- ROW 1 ---------------------------------------------------------------*/
    case 0x01: case 0x21: case 0x41: case 0x61: /* AJMP addr11 */
    case 0x81: case 0xa1: case 0xc1: case 0xe1:
        address = cpu_fetch11(cpu, opcode);
        cpu->pc = address;
        endless_loop = log_jump(&(cpu->log), address);
        break;
    case 0x11: case 0x31: case 0x51: case 0x71: /* ACALL addr11 */
    case 0x91: case 0xb1: case 0xd1: case 0xf1:
        address = cpu_fetch11(cpu, opcode);
        hi = (uint8_t)(cpu->pc >> 8);
        lo = (uint8_t)(cpu->pc >> 0);
        cpu->sfr.sp++;
        cpu_set_idata(cpu, cpu->sfr.sp, lo);
        cpu->sfr.sp++;
        cpu_set_idata(cpu, cpu->sfr.sp, hi);
        cpu->pc = address;
        endless_loop = log_jump(&(cpu->log), address);
        break;
    /*-- ROW 2 ---------------------------------------------------------------*/
    case 0x02:  /* LJMP addr16 */
        address = cpu_fetch16(cpu);
        cpu->pc = address;
        endless_loop = log_jump(&(cpu->log), address);
        break;
    case 0x12:  /* LCALL addr16 */
        address = cpu_fetch16(cpu);
        hi = (uint8_t)(cpu->pc >> 8);
        lo = (uint8_t)(cpu->pc >> 0);
        cpu->sfr.sp++;
        cpu_set_idata(cpu, cpu->sfr.sp, lo);
        cpu->sfr.sp++;
        cpu_set_idata(cpu, cpu->sfr.sp, hi);
        cpu->pc = address;
        endless_loop = log_jump(&(cpu->log), address);
        break;
    case 0x22:  /* RET */
        hi = cpu_get_idata(cpu, cpu->sfr.sp--);
        lo = cpu_get_idata(cpu, cpu->sfr.sp--);
        address = ((uint16_t)hi << 8) + (uint16_t)lo;
        cpu->pc = address;
        endless_loop = log_jump(&(cpu->log), address);
        break;
    case 0x32:  /* RET */
        /* FIXME interrupts unimplemented / RETI */
        hi = cpu_get_idata(cpu, cpu->sfr.sp--);
        lo = cpu_get_idata(cpu, cpu->sfr.sp--);
        address = ((uint16_t)hi << 8) + (uint16_t)lo;
        cpu->pc = address;
        endless_loop = log_jump(&(cpu->log), address);
        break;
    case 0x42:  /* ORL dir, A */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res | cpu->a;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x52:  /* ANL dir, A */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res & cpu->a;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x62:  /* XRL dir, A */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res ^ cpu->a;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x72:  /* ORL C, bit */
        bit = cpu_fetch(cpu);
        res = cpu_get_bit(cpu, bit);
        cpu->sfr.psw  |= (res << 7);
        break;
    case 0x82:  /* ANL C, bit */
        bit = cpu_fetch(cpu);
        res = cpu_get_bit(cpu, bit);
        cpu->sfr.psw &= (res << 7);
        break;
    case 0x92:  /* MOV bit, C */
        bit = cpu_fetch(cpu);
        cpu_set_bit(cpu, bit, (cpu->sfr.psw >> 7));
        break;
    case 0xa2:  /* MOV C, bit */
        bit = cpu_fetch(cpu);
        res = cpu_get_bit(cpu, bit);
        cpu->sfr.psw = set_bit(cpu->sfr.psw, 7, res);
        break;
    case 0xb2:  /* CPL bit */
        bit = cpu_fetch(cpu);
        dir = cpu_get_bit_address(cpu, bit);
        res = cpu_get_dir(cpu, dir);
        res ^= (1 << (bit & 0x07));
        cpu_set_dir(cpu, dir, res);
        break;
    case 0xc2:  /* CLR bit */
        bit = cpu_fetch(cpu);
        dir = cpu_get_bit_address(cpu, bit);
        res = cpu_get_dir(cpu, dir);
        res &= ~(1 << (bit & 0x07));
        cpu_set_dir(cpu, dir, res);
        break;
    case 0xd2:  /* SETB bit */
        bit = cpu_fetch(cpu);
        dir = cpu_get_bit_address(cpu, bit);
        res = cpu_get_dir(cpu, dir);
        res |= (1 << (bit & 0x07));
        cpu_set_dir(cpu, dir, res);
        break;
    case 0xe2:  /* MOVX A, @R0 */
        /* TODO MOVX @Ri upper address byte hardcoded to zero */
        address = ((uint16_t)cpu_get_rn(cpu, 0)) & 0x00ff;
        res = cpu_xdata_read(cpu, address);
        cpu_set_a(cpu, res);
        break;
    case 0xf2:  /* MOVX @R0, A */
        /* TODO MOVX @Ri upper address byte hardcoded to zero */
        address = ((uint16_t)cpu_get_rn(cpu, 0)) & 0x00ff;
        cpu_xdata_write(cpu, address, cpu->a);
        break;
    /*-- ROW 3 ---------------------------------------------------------------*/
    case 0x03:  /* RR A */
        val = cpu_update_flags(cpu, cpu->a, 0, rr);
        cpu_set_a(cpu, val);
        break;
    case 0x13:  /* RRC A */
        val = cpu_update_flags(cpu, cpu->a, 0, rrc);
        cpu_set_a(cpu, val);
        break;
    case 0x23:  /* RL A */
        val = cpu_update_flags(cpu, cpu->a, 0, rl);
        cpu_set_a(cpu, val);
        break;
    case 0x33:  /* RLC A */
        val = cpu_update_flags(cpu, cpu->a, 0, rlc);
        cpu_set_a(cpu, val);
        break;
    case 0x43:  /* ORL dir, #data */
        dir = cpu_fetch(cpu);
        imm = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res | imm;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x53:  /* ANL dir, #data */
        dir = cpu_fetch(cpu);
        imm = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res & imm;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x63:  /* XRL dir, #data */
        dir = cpu_fetch(cpu);
        imm = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res ^ imm;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x73:  /* JMP @A + DPTR */
        address = (((uint16_t)cpu->sfr.dph) << 8) + (uint16_t)cpu->sfr.dpl;
        address += (uint16_t)(cpu->a);
        cpu->pc = address;
        endless_loop = log_jump(&(cpu->log), address);
        break;
    case 0x83:  /* MOVC A, @A + PC */
        /* Base is PC of NEXT instruction == cpu->pc */
        address = cpu->pc + (uint16_t)(cpu->a);
        res = cpu_xcode_read(cpu, address);
        cpu_set_a(cpu, res);
        break;
    case 0x93:  /* MOVC A, @A + DPTR */
        address = (((uint16_t)cpu->sfr.dph) << 8) + (uint16_t)cpu->sfr.dpl;
        address += (uint16_t)(cpu->a);
        res = cpu_xcode_read(cpu, address);
        cpu_set_a(cpu, res);
        break;
    case 0xa3:  /* INC DPTR */
        address = (((uint16_t)cpu->sfr.dph) << 8) + (uint16_t)cpu->sfr.dpl;
        address++;
        cpu->sfr.dph = (uint8_t)(address >> 8);
        cpu->sfr.dpl = (uint8_t)(address >> 0);
        log_reg16(&(cpu->log), "DPTR", address);
        break;
    case 0xb3:  /* CPL C */
        cpu->sfr.psw ^= 0x80;
        break;
    case 0xc3:  /* CLR C */
        cpu->sfr.psw &= ~0x80;
        break;
    case 0xd3:  /* SETB C */
        cpu->sfr.psw |= 0x80;
        break;
    case 0xe3:  /* MOVX A, @R0 */
        /* TODO MOVX @Ri upper address byte hardcoded to zero */
        address = ((uint16_t)cpu_get_rn(cpu, 1)) & 0x00ff;
        res = cpu_xdata_read(cpu, address);
        cpu_set_a(cpu, res);
        break;
    case 0xf3:  /* MOVX @R0, A */
        /* TODO MOVX @Ri upper address byte hardcoded to zero */
        address = ((uint16_t)cpu_get_rn(cpu, 1)) & 0x00ff;
        cpu_xdata_write(cpu, address, cpu->a);
        break;
    /*-- ROW 4 ---------------------------------------------------------------*/
    case 0x04:  /* INC a */
        cpu_set_a(cpu, cpu->a + 1);
        break;
    case 0x14:  /* DEC a */
        cpu_set_a(cpu, cpu->a - 1);
        break;
    case 0x24:  /* ADD A, #imm */
        imm = cpu_fetch(cpu);
        res = cpu_update_flags(cpu, cpu->a, imm, add);
        cpu_set_a(cpu, res);
        break;
    case 0x34:  /* ADDC A, #imm */
        imm = cpu_fetch(cpu);
        res = cpu_update_flags(cpu, cpu->a, imm, addc);
        cpu_set_a(cpu, res);
        break;
    case 0x44:  /* ORL A, #imm */
        imm = cpu_fetch(cpu);
        res = cpu->a | imm;
        cpu_set_a(cpu, res);
        break;
    case 0x54:  /* ANL A, #imm */
        imm = cpu_fetch(cpu);
        res = cpu->a & imm;
        cpu_set_a(cpu, res);
        break;
    case 0x64:  /* XRL A, #imm */
        imm = cpu_fetch(cpu);
        res = cpu->a ^ imm;
        cpu_set_a(cpu, res);
        break;
    case 0x74:  /* MOV A, #data */
        imm = cpu_fetch(cpu);
        cpu_set_a(cpu, imm);
        break;
    case 0x84:  /* DIV AB */
        res = cpu_update_flags(cpu, cpu->a, cpu->sfr.b, alu_div);
        cpu_set_a(cpu, res);
        break;
    case 0x94:  /* SUBB A, #data */
        imm = cpu_fetch(cpu);
        res = cpu_update_flags(cpu, cpu->a, imm, subb);
        cpu_set_a(cpu, res);
        break;
    case 0xa4:  /* MUL AB */
        res = cpu_update_flags(cpu, cpu->a, cpu->sfr.b, alu_mul);
        cpu_set_a(cpu, res);
        break;
    case 0xb4:  /* CJNE A, #data, rel */
        imm = cpu_fetch(cpu);
        rel = cpu_fetch(cpu);
        cpu_update_flags(cpu, cpu->a, imm, cjne);
        if(imm != cpu->a){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0xc4:  /* SWAP A */
        res = (cpu->a >> 4) & 0x0f;
        res = res | ((cpu->a << 4) & 0x0f0);
        cpu_set_a(cpu, res);
        break;
    case 0xd4:  /* DA A */
        if(cpu->options.bcd){
            val = cpu_update_flags(cpu, cpu->a, 0, da);
            cpu_set_a(cpu, val);
        }
        else{
            /* DA unimplemented, execute as NOP */
            cpu->implemented_as_nop = true;
        }
        break;
    case 0xe4:  /* CLR A */
        cpu_set_a(cpu, 0);
        break;
    case 0xf4:  /* CPL A */
        cpu_set_a(cpu, ~(cpu->a));
        break;
    /*-- ROW 5 ---------------------------------------------------------------*/
    case 0x05:  /* INC dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res + 1;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x15:  /* DEC dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res - 1;
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x25:  /* ADD A, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = cpu_update_flags(cpu, cpu->a, res, add);
        cpu_set_a(cpu, res);
        break;
    case 0x35:  /* ADDC A, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = cpu_update_flags(cpu, cpu->a, res, addc);
        cpu_set_a(cpu, res);
        break;
    case 0x45:  /* ORL A, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res | cpu->a;
        cpu_set_a(cpu, res);
        break;
    case 0x55:  /* ANL A, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res & cpu->a;
        cpu_set_a(cpu, res);
        break;
    case 0x65:  /* XRL A, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res ^ cpu->a;
        cpu_set_a(cpu, res);
        break;
    case 0x75:  /* MOV dir, #imm */
        dir = cpu_fetch(cpu);
        imm = cpu_fetch(cpu);
        cpu_set_dir(cpu, dir, imm);
        break;
    case 0x85:  /* MOV dir1, dir2 */
        dir = cpu_fetch(cpu);
        dir2 = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        cpu_set_dir(cpu, dir2, res);
        break;
    case 0x95:  /* SUBB A, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = cpu_update_flags(cpu, cpu->a, res, subb);
        cpu_set_a(cpu, res);
        break;
    case 0xa5:  /* RESERVED A5h opcode : implemented as NOP */
        cpu->implemented_as_nop = true;
        break;
    case 0xb5:  /* CJNE A, dir, rel */
        dir = cpu_fetch(cpu);
        rel = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        cpu_update_flags(cpu, cpu->a, res, cjne);
        if(res != cpu->a){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0xc5:  /* XCH A,dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        cpu_set_dir(cpu, dir, cpu->a);
        cpu_set_a(cpu, res);
        break;
    case 0xd5:  /* DJNZ dir, rel */
        dir = cpu_fetch(cpu);
        rel = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        res = res - 1;
        cpu_set_dir(cpu, dir, res);
        if(res != 0){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0xe5:  /* MOV a, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        cpu_set_a(cpu, res);
        break;
    case 0xf5:  /* MOV dir, a */
        dir = cpu_fetch(cpu);
        cpu_set_dir(cpu, dir, cpu->a);
        break;
    /*-- ROW 6 & ROW 7 -------------------------------------------------------*/
    /*  IMPORTANT:
        All the cases in the x6 & x7 groups are PAIRED. The 'case fallthrough'
        is intentional.
        TODO label fallthrough case for lint tool.
    */
    case 0x06:  /* INC @Ri */
    case 0x07:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        cpu_set_idata(cpu, dir, res + 1);
        break;
    case 0x16:  /* DEC @Ri */
    case 0x17:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        cpu_set_idata(cpu, dir, res - 1);
        break;
    case 0x26:  /* ADD A, @Ri */
    case 0x27:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        res = cpu_update_flags(cpu, cpu->a, res, add);
        cpu_set_a(cpu, res);
        break;
    case 0x36:  /* ADDC A, @Ri */
    case 0x37:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        res = cpu_update_flags(cpu, cpu->a, res, addc);
        cpu_set_a(cpu, res);
        break;
    case 0x46:  /* ORL A, @Ri */
    case 0x47:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        res = res | cpu->a;
        cpu_set_a(cpu, res);
        break;
    case 0x56:  /* ANL A, @Ri */
    case 0x57:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        res = res & cpu->a;
        cpu_set_a(cpu, res);
        break;
    case 0x66:  /* XRL A, @Ri */
    case 0x67:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        res = res ^ cpu->a;
        cpu_set_a(cpu, res);
        break;
    case 0x76:  /* MOV @Ri, imm */
    case 0x77:
        imm = cpu_fetch(cpu);
        dir = cpu_get_rn(cpu, opcode & 0x01);
        cpu_set_idata(cpu, dir, imm);
        break;
    case 0x86:  /* MOV dir, @Ri */
    case 0x87:
        dir = cpu_fetch(cpu);
        imm = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, imm);
        cpu_set_dir(cpu, dir, res);
        break;
    case 0x96:  /* SUBB A, @Ri */
    case 0x97:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        res = cpu_update_flags(cpu, cpu->a, res, subb);
        cpu_set_a(cpu, res);
        break;
    case 0xa6:  /* MOV @Ri, dir */
    case 0xa7:
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        dir = cpu_get_rn(cpu, opcode & 0x01);
        cpu_set_idata(cpu, dir, res);
        break;
    case 0xb6:  /* CJNE @Ri, #imm, rel */
    case 0xb7:
        imm = cpu_fetch(cpu);
        rel = cpu_fetch(cpu);
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        cpu_update_flags(cpu, res, imm, cjne);
        if(imm != res){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0xc6:  /* XCH A,@Ri */
    case 0xc7:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        cpu_set_idata(cpu, dir, cpu->a);
        cpu_set_a(cpu, res);
        break;
    case 0xd6:  /* XCHD A,@Ri */
    case 0xd7:
        if(cpu->options.bcd){
            dir = cpu_get_rn(cpu, opcode & 0x01);
            res = cpu_get_idata(cpu, dir);
            imm = (res & 0x0f0) | (cpu->a & 0x00f);
            cpu_set_a(cpu, ((cpu->a & 0x0f0) | (res & 0x00f)));
            cpu_set_idata(cpu, dir, imm);
        }
        else{
            /* Implemented as NOP */
            cpu->implemented_as_nop = true;
        }
        break;
    case 0xe6:  /* MOV A, @Ri */
    case 0xe7:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        res = cpu_get_idata(cpu, dir);
        cpu_set_a(cpu, res);
        break;
    case 0xf6:  /* MOV @Ri, A */
    case 0xf7:
        dir = cpu_get_rn(cpu, opcode & 0x01);
        cpu_set_idata(cpu, dir, cpu->a);
        break;
    default:    /* unimplemented opcode */
        log_unimplemented(&(cpu->log), opcode);
        ok = false;
    }
 
    if(endless_loop){
        return false;
    }
    else{
        return ok;
    }
}
 
static bool cpu_exec_rn(cpu51_t *cpu, uint8_t opcode){
    uint8_t operation, rn, n, res, dir, imm, rel;
    bool ok = true;
    bool endless_loop = false;
 
    operation = (opcode >> 4) & 0x0f;
    n = opcode & 0x07;
    rn = cpu_get_rn(cpu, n);
 
    switch(operation){
    case 0x00:  /* INC Rn */
        cpu_set_rn(cpu, n, rn+1);
        break;
    case 0x01:  /* DEC Rn */
        cpu_set_rn(cpu, n, rn-1);
        break;
    case 0x02:  /* ADD A,Rn */
        res = cpu_update_flags(cpu, cpu->a, rn, add);
        cpu_set_a(cpu, res);
        break;
    case 0x03:  /* ADDC A,Rn */
        res = cpu_update_flags(cpu, cpu->a, rn, addc);
        cpu_set_a(cpu, res);
        break;
    case 0x04:  /* ORL A, Rn */
        cpu_set_a(cpu, cpu->a | rn);
        break;
    case 0x05:  /* ANL A, Rn */
        cpu_set_a(cpu, cpu->a & rn);
        break;
    case 0x06:  /* XRL A, Rn */
        cpu_set_a(cpu, cpu->a ^ rn);
        break;
    case 0x07:  /* MOV Rn, #data */
        imm = cpu_fetch(cpu);
        cpu_set_rn(cpu, n, imm);
        break;
    case 0x08:  /* MOV dir, Rn */
        dir = cpu_fetch(cpu);
        cpu_set_dir(cpu, dir, rn);
        break;
    case 0x09:  /* SUBB A, Rn */
        res = cpu_update_flags(cpu, cpu->a, rn, subb);
        cpu_set_a(cpu, res);
        break;
    case 0x0a:  /* MOV Rn, dir */
        dir = cpu_fetch(cpu);
        res = cpu_get_dir(cpu, dir);
        cpu_set_rn(cpu, n, res);
        break;
    case 0x0b:  /* CJNE Rn, #data, rel */
        imm = cpu_fetch(cpu);
        rel = cpu_fetch(cpu);
        cpu_update_flags(cpu, rn, imm, cjne);
        if(imm != rn){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0x0c:  /* XCH A, Rn */
        res = cpu->a;
        cpu_set_a(cpu, rn);
        cpu_set_rn(cpu, n, res);
        break;
    case 0x0d:  /* DJNZ Rn, rel */
        rel = cpu_fetch(cpu);
        res = rn - 1;
        cpu_set_rn(cpu, n, res);
        if(res != 0){
            cpu_rel_jump(cpu, rel);
            cpu->max_cycle_count = true;
        }
        break;
    case 0x0e:  /* MOV A, Rn */
        cpu_set_a(cpu, rn);
        break;
    case 0x0f:  /* MOV Rn, A */
        cpu_set_rn(cpu, n, cpu->a);
        break;
    }
 
    if(endless_loop){
        return false;
    }
    else{
        return ok;
    }
}
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.