mirror of
https://github.com/pruten/shoebill.git
synced 2024-11-17 15:07:18 +00:00
adc2e16ffd
It compiles, but I haven't even tested it once. *fingers crossed*
3033 lines
86 KiB
C
3033 lines
86 KiB
C
/*
|
|
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "../core/shoebill.h"
|
|
#include "../core/mc68851.h"
|
|
|
|
global_shoebill_context_t shoe;
|
|
|
|
static _Bool _cc_t() {return 1;}
|
|
static _Bool _cc_f() {return 0;}
|
|
static _Bool _cc_hi() {return !sr_c() && !sr_z();}
|
|
static _Bool _cc_ls() {return sr_c() || sr_z();}
|
|
static _Bool _cc_cc() {return !sr_c();}
|
|
static _Bool _cc_cs() {return sr_c();}
|
|
static _Bool _cc_ne() {return !sr_z();}
|
|
static _Bool _cc_eq() {return sr_z();}
|
|
static _Bool _cc_vc() {return !sr_v();}
|
|
static _Bool _cc_vs() {return sr_v();}
|
|
static _Bool _cc_pl() {return !sr_n();}
|
|
static _Bool _cc_mi() {return sr_n();}
|
|
static _Bool _cc_ge() {return (sr_n() && sr_v()) || (!sr_n() && !sr_v());}
|
|
static _Bool _cc_lt() {return (sr_n() && !sr_v()) || (!sr_n() && sr_v());}
|
|
static _Bool _cc_gt() {return (sr_n() && sr_v() && !sr_z()) || (!sr_n() && !sr_v() && !sr_z());}
|
|
static _Bool _cc_le() {return sr_z() || (sr_n() && !sr_v()) || (!sr_n() && sr_v());}
|
|
typedef _Bool (*_cc_func)();
|
|
static const _cc_func evaluate_cc[16] = {
|
|
_cc_t, _cc_f, _cc_hi, _cc_ls, _cc_cc, _cc_cs, _cc_ne, _cc_eq,
|
|
_cc_vc, _cc_vs, _cc_pl, _cc_mi, _cc_ge, _cc_lt, _cc_gt, _cc_le
|
|
};
|
|
|
|
|
|
#define nextword() ({const uint16_t w=lget(shoe.pc,2); if (shoe.abort) {return;}; shoe.pc+=2; w;})
|
|
#define nextlong() ({const uint32_t L=lget(shoe.pc,4); if (shoe.abort) {return;}; shoe.pc+=4; L;})
|
|
#define verify_supervisor() {if (!sr_s()) {throw_privilege_violation(); return;}}
|
|
|
|
|
|
static void inst_callm(void) {
|
|
assert(!"callm: error, not implemented\n");
|
|
}
|
|
|
|
static void inst_chk2_cmp2 (void) {
|
|
assert(!"chk2_cmp2: error: not implemented\n");
|
|
}
|
|
|
|
static void inst_illegal (void) {
|
|
throw_illegal_instruction();
|
|
}
|
|
|
|
static void inst_move16 (void) {
|
|
assert(!"move16: SHOULD NOT BE CALLED\n"); // shouldn't be called by 68020 decoder
|
|
}
|
|
|
|
static void inst_rtm (void) {
|
|
assert(!"rtm: error: not implemented\n");
|
|
}
|
|
|
|
static void inst_tas (void) {
|
|
~decompose(shoe.op, 0100 1010 11 MMMMMM);
|
|
|
|
call_ea_read(M, 1);
|
|
const uint8_t byte = shoe.dat;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(byte == 0);
|
|
set_sr_n(byte >> 7);
|
|
|
|
shoe.dat = byte | 0x80;
|
|
|
|
call_ea_write(M, 1);
|
|
}
|
|
|
|
static void inst_trapcc (void) {
|
|
~decompose(shoe.op, 0101 cccc 11111 xyz);
|
|
|
|
// (xyz) == (100) -> sz=0
|
|
// (xyz) == (010) -> sz=2
|
|
// (xyz) == (011) -> sz=4
|
|
const uint32_t sz = y << (z+1); // too clever
|
|
const uint32_t next_pc = shoe.pc + sz;
|
|
|
|
if (evaluate_cc[c]())
|
|
throw_frame_two(shoe.sr, next_pc, 7, shoe.orig_pc);
|
|
else
|
|
shoe.pc = next_pc;
|
|
}
|
|
|
|
static void inst_trapv (void) {
|
|
if (sr_v())
|
|
throw_frame_two(shoe.sr, shoe.pc, 7, shoe.orig_pc);
|
|
}
|
|
|
|
static void inst_asx_reg (void) {
|
|
~decompose(shoe.op, 1110 ccc d ss i 00 rrr);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
uint8_t count, lastb;
|
|
if (i)
|
|
count = shoe.d[c] % 64;
|
|
else
|
|
count = (c==0)?8:c;
|
|
|
|
if (d) { // left shift
|
|
uint32_t dat = get_d(r, sz);
|
|
const uint8_t origmib = mib(dat, sz);
|
|
uint8_t i=0, deltamib=0, lastb=0;
|
|
for (; i<count; i++) {
|
|
lastb = mib(dat, sz);
|
|
dat <<= 1;
|
|
if ( mib(dat, sz) != origmib )
|
|
deltamib = 1;
|
|
}
|
|
set_d(r, dat, sz);
|
|
if (count) {
|
|
set_sr_x(lastb);
|
|
}
|
|
set_sr_n(mib(dat, sz));
|
|
set_sr_z(get_d(r, sz) == 0);
|
|
set_sr_v(deltamib);
|
|
set_sr_c(lastb);
|
|
return ;
|
|
}
|
|
else { // right shift
|
|
uint32_t dat = get_d(r, sz);
|
|
const uint32_t tmask = (mib(dat,sz)) << (sz*8-1); // 0 if mib==0, 0x80, 0x8000, or 0x80000000 if mib==1
|
|
|
|
//slog("asr: dat=%u sz=%u count=%u", dat, sz, count);
|
|
|
|
uint8_t i=0, lastb=0;
|
|
for (; i < count; i++) {
|
|
lastb = dat & 1;
|
|
dat >>= 1;
|
|
dat |= tmask;
|
|
}
|
|
set_d(r, dat, sz);
|
|
if (count) {
|
|
set_sr_x(lastb);
|
|
}
|
|
set_sr_n(mib(dat, sz));
|
|
set_sr_z(get_d(r, sz) == 0);
|
|
set_sr_v(0);
|
|
set_sr_c(lastb);
|
|
|
|
//slog(" result=%u\n", get_d(r, sz));
|
|
}
|
|
}
|
|
|
|
static void inst_asx_mem (void) {
|
|
~decompose(shoe.op, 1110 000d 11 MMMMMM);
|
|
|
|
call_ea_read(M, 2);
|
|
const uint16_t dat = shoe.dat;
|
|
if (d) { // left shift <ea> 1 bit
|
|
|
|
const uint16_t R = dat << 1;
|
|
const uint16_t lost = (dat >> 15);
|
|
|
|
shoe.dat = R;
|
|
call_ea_write(M, 2);
|
|
|
|
set_sr_x(lost);
|
|
set_sr_c(lost);
|
|
set_sr_v((R >> 15) != (dat >> 15)); // set if the MSB changes (at anytime during the shift operation)
|
|
set_sr_n(R>>15);
|
|
set_sr_z(R==0);
|
|
}
|
|
else { // right shift <ea> 1 bit
|
|
const uint16_t R = (uint16_t)(((int16_t)dat) >> 1);
|
|
const uint16_t lost = dat & 1;
|
|
|
|
slog("asr_mem: shifted 0x%04x to 0x%04x lost=%u\n", dat, R, lost);
|
|
|
|
shoe.dat = R;
|
|
call_ea_write(M, 2);
|
|
|
|
set_sr_x(lost);
|
|
set_sr_c(lost);
|
|
set_sr_v(0);
|
|
set_sr_n(R>>15);
|
|
set_sr_z(R==0);
|
|
}
|
|
}
|
|
|
|
// GCC returns garbage when you shift a value by >= its size: ((uint32_t)123)<<32) == garbage
|
|
// So we'll use macros for risky shifts
|
|
|
|
#define shiftl(_v, _b) ((uint32_t) (((uint64_t)(_v)) << (_b)))
|
|
#define shiftr(_v, _b) ((uint32_t) (((uint64_t)(_v)) >> (_b)))
|
|
|
|
static void inst_lsx_reg (void) {
|
|
~decompose(shoe.op, 1110 ccc d ss i 01 rrr);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
uint8_t count, lastb;
|
|
if (i)
|
|
count = shoe.d[c] % 64;
|
|
else
|
|
count = ((c==0)<<3) | c;
|
|
|
|
const uint32_t dat = get_d(r, sz);
|
|
|
|
if (d) { // left shift
|
|
const uint32_t shifted = shiftl(dat, count);
|
|
lastb = mib(shiftl(dat, count-1), sz);
|
|
set_d(r, shifted, sz);
|
|
} else { // right shift
|
|
const uint32_t shifted = shiftr(dat, count);
|
|
lastb = shiftr(dat, count-1) & 1;
|
|
set_d(r, shifted, sz);
|
|
}
|
|
|
|
// set control codes
|
|
if (count) {
|
|
set_sr_x(lastb);
|
|
}
|
|
set_sr_n(mib(shoe.d[r], sz));
|
|
set_sr_z(chop(shoe.d[r], sz) == 0);
|
|
set_sr_v(0);
|
|
set_sr_c(lastb && (count != 0));
|
|
}
|
|
|
|
static void inst_lsx_mem (void) {
|
|
~decompose(shoe.op, 1110 001d 11 MMMMMM);
|
|
|
|
call_ea_read(M, 2);
|
|
if (d) { // left shift <ea> 1 bit
|
|
const uint16_t R = shoe.dat << 1;
|
|
const uint8_t lost = (shoe.dat >> 15);
|
|
|
|
shoe.dat = R;
|
|
call_ea_write(M, 2);
|
|
|
|
set_sr_x(lost);
|
|
set_sr_c(lost);
|
|
set_sr_v(0);
|
|
set_sr_n(R>>15);
|
|
set_sr_z(R==0);
|
|
}
|
|
else { // right shift <ea> 1 bit
|
|
const uint16_t R = shoe.dat>>1;
|
|
const uint8_t lost = shoe.dat & 1;
|
|
|
|
shoe.dat = R;
|
|
call_ea_write(M, 2);
|
|
|
|
set_sr_x(lost);
|
|
set_sr_c(lost);
|
|
set_sr_v(0);
|
|
set_sr_n(R>>15);
|
|
set_sr_z(R==0);
|
|
}
|
|
}
|
|
|
|
// FIXME: rox_reg and roxx_reg can be made O(1)
|
|
|
|
static void inst_roxx_reg (void) {
|
|
~decompose(shoe.op, 1110 ccc d ss i 10 rrr);
|
|
|
|
const uint8_t sz = 1<<s;
|
|
uint32_t data = get_d(r, sz);
|
|
const uint32_t hi_bit = (sz * 8) - 1;
|
|
const uint32_t hi_mask = 1 << hi_bit;
|
|
|
|
uint32_t carry = 0;
|
|
uint32_t extend = sr_x();
|
|
uint8_t count = i ? (shoe.d[c] & 63) : ( c ? c : 8) ;
|
|
uint32_t j;
|
|
|
|
if (!d) { // right
|
|
for (j=0; j<count; j++) {
|
|
carry = data & 1;
|
|
data >>= 1;
|
|
data |= (extend << hi_bit);
|
|
extend = carry;
|
|
}
|
|
}
|
|
else { // left
|
|
for (j=0; j<count; j++) {
|
|
carry = (data & hi_mask) != 0;
|
|
data <<= 1;
|
|
data |= extend;
|
|
extend = carry;
|
|
}
|
|
}
|
|
|
|
set_d(r, data, sz);
|
|
|
|
set_sr_x(extend);
|
|
set_sr_v(0);
|
|
set_sr_c(carry);
|
|
set_sr_n(mib(data, sz));
|
|
set_sr_z(data == 0);
|
|
}
|
|
|
|
static void inst_roxx_mem (void) {
|
|
assert(!"roxx_mem: unimplemented\n");
|
|
}
|
|
|
|
static void inst_rox_reg (void) {
|
|
~decompose(shoe.op, 1110 ccc d ss i 11 rrr);
|
|
|
|
const uint8_t sz = 1<<s;
|
|
uint32_t data = get_d(r, sz);
|
|
const uint32_t hi_bit = (sz * 8) - 1;
|
|
const uint32_t hi_mask = 1 << hi_bit;
|
|
|
|
uint32_t carry = 0;
|
|
uint8_t count = i ? (shoe.d[c] & 63) : ( c ? c : 8) ;
|
|
uint32_t j;
|
|
|
|
// slog("rox_reg: count = %u\n", count);
|
|
|
|
if (!d) { // right
|
|
for (j=0; j<count; j++) {
|
|
carry = data & 1;
|
|
data >>= 1;
|
|
data |= (carry << hi_bit);
|
|
}
|
|
}
|
|
else { // left
|
|
for (j=0; j<count; j++) {
|
|
carry = (data & hi_mask) != 0;
|
|
data <<= 1;
|
|
data |= carry;
|
|
}
|
|
}
|
|
|
|
set_d(r, data, sz);
|
|
|
|
set_sr_v(0);
|
|
set_sr_c(carry);
|
|
set_sr_n(mib(data, sz));
|
|
set_sr_z(data == 0);
|
|
}
|
|
|
|
static void inst_rox_mem (void) {
|
|
assert(!"rox_mem: unimplemented\n");
|
|
}
|
|
|
|
static void inst_sbcd (void) {
|
|
assert(!"Hey! inst_sbcd isn't implemented!\n");
|
|
}
|
|
|
|
static void inst_pack (void) {
|
|
assert(!"Hey! inst_pack isn't implemented!\n");
|
|
}
|
|
|
|
static void inst_unpk (void) {
|
|
assert(!"Hey! inst_unpk isn't implemented!\n");
|
|
}
|
|
|
|
static void inst_divu (void) {
|
|
~decompose(shoe.op, 1000 rrr 011 MMMMMM);
|
|
|
|
// uncommitted_ea_read_pc is uninitialized for EA modes that don't change the PC,
|
|
// but we might need it if we need to throw a divide-by-zero exception,
|
|
// so initialize it here, just in case.
|
|
shoe.uncommitted_ea_read_pc = shoe.pc;
|
|
|
|
call_ea_read(M, 2);
|
|
|
|
const uint32_t dividend = shoe.d[r];
|
|
const uint16_t divisor = (uint16_t)shoe.dat;
|
|
|
|
if (divisor == 0) {
|
|
throw_frame_two(shoe.orig_sr, shoe.uncommitted_ea_read_pc, 5, shoe.orig_pc);
|
|
return ;
|
|
}
|
|
|
|
call_ea_read_commit(M, 2);
|
|
|
|
const uint32_t quotient_32 = dividend / divisor;
|
|
const uint32_t remainder_32 = dividend % divisor;
|
|
|
|
shoe.d[r] = (remainder_32 << 16) | (quotient_32 & 0xffff);
|
|
|
|
set_sr_c(0);
|
|
set_sr_n((quotient_32>>15)&1);
|
|
set_sr_z((quotient_32 & 0xffff) == 0);
|
|
set_sr_v(quotient_32 >> 16);
|
|
}
|
|
|
|
static void inst_divs (void) {
|
|
~decompose(shoe.op, 1000 rrr 111 MMMMMM);
|
|
|
|
// uncommitted_ea_read_pc is uninitialized for EA modes that don't change the PC,
|
|
// but we might need it if we need to throw a divide-by-zero exception,
|
|
// so initialize it here, just in case.
|
|
shoe.uncommitted_ea_read_pc = shoe.pc;
|
|
|
|
call_ea_read(M, 2);
|
|
|
|
const int32_t dividend = (int32_t)shoe.d[r];
|
|
const uint16_t u_divisor = (uint16_t)shoe.dat;
|
|
const int16_t s_divisor = (int16_t)shoe.dat;
|
|
|
|
if (s_divisor == 0) {
|
|
throw_frame_two(shoe.orig_sr, shoe.uncommitted_ea_read_pc, 5, shoe.orig_pc);
|
|
return ;
|
|
}
|
|
|
|
call_ea_read_commit(M, 2);
|
|
|
|
const int32_t s_quotient_32 = dividend / s_divisor;
|
|
const int32_t s_remainder_32 = dividend % s_divisor;
|
|
|
|
const uint32_t u_quotient_32 = (uint32_t)s_quotient_32;
|
|
const uint32_t u_remainder_32 = (uint32_t)s_remainder_32;
|
|
|
|
shoe.d[r] = (u_remainder_32 << 16) | (u_quotient_32 & 0xffff);
|
|
|
|
set_sr_c(0);
|
|
set_sr_n((u_quotient_32>>15)&1);
|
|
set_sr_z((u_quotient_32 & 0xffff) == 0);
|
|
set_sr_v(u_quotient_32 >> 16);
|
|
}
|
|
|
|
static void inst_bkpt (void) {
|
|
assert(!"Hey! inst_bkpt isn't implemented!\n");
|
|
}
|
|
|
|
static void inst_swap (void) {
|
|
~decompose(shoe.op, 0100 1000 0100 0 rrr);
|
|
shoe.d[r] = (shoe.d[r]>>16) | (shoe.d[r]<<16);
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(shoe.d[r]==0);
|
|
set_sr_n(mib(shoe.d[r], 4));
|
|
}
|
|
|
|
static void inst_abcd (void) {
|
|
~decompose(shoe.op, 1100 xxx 10000 m yyy);
|
|
uint8_t packed_x, packed_y;
|
|
const uint8_t extend = sr_x() ? 1 : 0;
|
|
|
|
// slog("abcd: pc=0x%08x extend=%u m=%u x=%u y=%u\n", shoe.orig_pc, extend, m, x, y);
|
|
|
|
if (m) {
|
|
// predecrement mem to predecrement mem
|
|
// FIXME: these addresses aren't predecremented (check whether a7 is incremented 2 bytes)
|
|
assert(!"acbd is broken");
|
|
packed_x = lget(shoe.a[x], 1);
|
|
if (shoe.abort) return ;
|
|
packed_y = lget(shoe.a[y], 1);
|
|
if (shoe.abort) return ;
|
|
}
|
|
else {
|
|
packed_x = shoe.d[x] & 0xff;
|
|
packed_y = shoe.d[y] & 0xff;
|
|
}
|
|
|
|
if (((packed_x & 0xF) > 9) || (((packed_x>>4) & 0xF) > 9) || ((packed_y & 0xF) > 9) || (((packed_y>>4) & 0xF) > 9))
|
|
assert(!"abcd: badly packed byte");
|
|
|
|
const uint8_t unpacked_x = (packed_x & 0xf) + ((packed_x >> 4) * 10);
|
|
const uint8_t unpacked_y = (packed_y & 0xf) + ((packed_y >> 4) * 10);
|
|
const uint8_t sum = unpacked_x + unpacked_y + extend;
|
|
const uint8_t unpacked_sum = sum % 100;
|
|
const uint8_t carry = (sum >= 100);
|
|
const uint8_t packed_sum = ((unpacked_sum / 10) << 4) | (unpacked_sum % 10);
|
|
|
|
// slog("abcd: packed_x = 0x%02x(%u) packed_y = 0x%02x(%u) sum=0x%02x(%u) carry=%u\n", packed_x, unpacked_x, packed_y, unpacked_y, packed_sum, unpacked_sum, carry);
|
|
|
|
if (unpacked_sum)
|
|
set_sr_z(0);
|
|
set_sr_c(carry);
|
|
set_sr_x(carry);
|
|
|
|
if (m) {
|
|
lset(shoe.a[x]-1, 1, packed_sum);
|
|
if (shoe.abort) return ;
|
|
|
|
shoe.a[x]--;
|
|
if (x != y)
|
|
shoe.a[y]--;
|
|
}
|
|
else {
|
|
set_d(x, packed_sum, 1);
|
|
}
|
|
|
|
}
|
|
|
|
static void inst_muls (void) {
|
|
~decompose(shoe.op, 1100 rrr 011 MMMMMM);
|
|
|
|
call_ea_read(M, 2);
|
|
call_ea_read_commit(M, 2);
|
|
|
|
const uint16_t u_a = get_d(r, 2);
|
|
const uint16_t u_b = shoe.dat;
|
|
const int16_t s_a = (int16_t)u_a;
|
|
const int16_t s_b = (int16_t)u_b;
|
|
|
|
const int32_t s_result = ((int32_t)s_a) * ((int32_t)s_b);
|
|
const uint32_t result = (uint32_t)s_result;
|
|
|
|
//slog("muls: %d * %d = %d\n", s_a, s_b, s_result);
|
|
|
|
shoe.d[r] = result;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(result == 0);
|
|
set_sr_n(mib(result, 4));
|
|
}
|
|
|
|
/* short form unsigned multiply */
|
|
static void inst_mulu (void) {
|
|
~decompose(shoe.op, 1100 rrr 011 MMMMMM);
|
|
|
|
call_ea_read(M, 2);
|
|
call_ea_read_commit(M, 2);
|
|
|
|
const uint16_t a = (uint16_t) get_d(r, 2);
|
|
const uint16_t b = (uint16_t) shoe.dat;
|
|
const uint32_t result = ((uint32_t)a) * ((uint32_t)b);
|
|
|
|
shoe.d[r] = result;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(result == 0);
|
|
set_sr_n(mib(result, 4));
|
|
}
|
|
|
|
static void inst_exg (void) {
|
|
~decompose(shoe.op, 1100 xxx 1 ppppp yyy);
|
|
|
|
if (p == ~b(01000)) { // data reg mode
|
|
const uint32_t tmp = shoe.d[x];
|
|
shoe.d[x] = shoe.d[y];
|
|
shoe.d[y] = tmp;
|
|
}
|
|
else if (p == ~b(01001)) { // address reg mode
|
|
const uint32_t tmp = shoe.a[x];
|
|
shoe.a[x] = shoe.a[y];
|
|
shoe.a[y] = tmp;
|
|
}
|
|
else /* if (p == ~b(10001)) */ { // addr/reg mode
|
|
const uint32_t tmp = shoe.d[x];
|
|
shoe.d[x] = shoe.a[y];
|
|
shoe.a[y] = tmp;
|
|
}
|
|
}
|
|
|
|
static void inst_stop (void) {
|
|
verify_supervisor();
|
|
|
|
const uint16_t ext = nextword();
|
|
set_sr(ext);
|
|
|
|
shoe.cpu_thread_notifications |= SHOEBILL_STATE_STOPPED;
|
|
}
|
|
|
|
static void inst_rtr (void) {
|
|
const uint16_t ccr = lget(shoe.a[7], 2);
|
|
if (shoe.abort) return ;
|
|
|
|
const uint32_t pc = lget(shoe.a[7]+2, 4);
|
|
if (shoe.abort) return ;
|
|
|
|
shoe.a[7] += 6;
|
|
|
|
// We don't need to use sr_set() here because only changing the condition codes
|
|
shoe.sr = (shoe.sr & 0xff00) | (ccr & 0x00ff);
|
|
shoe.pc = pc;
|
|
}
|
|
|
|
static void inst_rtd (void) {
|
|
const int16_t disp = nextword();
|
|
const uint32_t new_pc = lget(shoe.a[7], 4);
|
|
if (shoe.abort) return ;
|
|
|
|
shoe.pc = new_pc;
|
|
shoe.a[7] += 4;
|
|
shoe.a[7] += disp;
|
|
}
|
|
|
|
static void inst_rte (void) {
|
|
verify_supervisor();
|
|
|
|
const uint16_t sr = lget(shoe.a[7], 2);
|
|
if (shoe.abort) return ;
|
|
|
|
const uint32_t pc = lget(shoe.a[7]+2, 4);
|
|
if (shoe.abort) return ;
|
|
|
|
const uint16_t format_word = lget(shoe.a[7]+6, 2);
|
|
if (shoe.abort) return ;
|
|
|
|
// slog("rte: sr=0x%04x pc=0x%08x format=0x%04x, post-pop a7=0x%08x\n", sr, pc, format_word, shoe.a[7]+8);
|
|
|
|
switch (format_word >> 12) {
|
|
case 0:
|
|
case 1: {
|
|
shoe.a[7] += 8;
|
|
shoe.pc = pc;
|
|
set_sr(sr);
|
|
return ;
|
|
}
|
|
case 0xa: {
|
|
// FIXME: I should probably check the special status word here
|
|
shoe.a[7] += 16 * 2;
|
|
shoe.pc = pc;
|
|
set_sr(sr);
|
|
return ;
|
|
}
|
|
case 0xb: {
|
|
//FIXME: Check the special status word + version
|
|
shoe.a[7] += 46 * 2;
|
|
shoe.pc = pc;
|
|
set_sr(sr);
|
|
return ;
|
|
}
|
|
default:
|
|
slog("rte?? I don't recognize this exception format! 0x%04x\n", format_word);
|
|
assert(!"rte?? Don't recognize this exception format!\n");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void inst_move_usp (void) {
|
|
verify_supervisor();
|
|
~decompose(shoe.op, 0100 1110 0110 d rrr);
|
|
|
|
if (d) {
|
|
make_stack_pointers_valid();
|
|
shoe.a[r] = shoe.usp;
|
|
}
|
|
else {
|
|
make_stack_pointers_valid();
|
|
shoe.usp = shoe.a[r];
|
|
load_stack_pointer();
|
|
}
|
|
}
|
|
|
|
static void inst_and (void) {
|
|
~decompose(shoe.op, 1100 rrr dss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
if (d) {
|
|
// Dn ^ <ea> -> <ea>
|
|
// S D R
|
|
call_ea_read(M, sz);
|
|
const uint32_t R = chop(shoe.dat & shoe.d[r], sz);
|
|
shoe.dat = R;
|
|
call_ea_write(M, sz);
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(R==0);
|
|
set_sr_n(mib(R, sz));
|
|
return ;
|
|
}
|
|
else {
|
|
// <ea> ^ Dn -> Dn
|
|
// S D R
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
|
|
const uint32_t R = chop(shoe.dat & shoe.d[r], sz);
|
|
set_d(r, R, sz);
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(R==0);
|
|
set_sr_n(mib(R, sz));
|
|
return ;
|
|
}
|
|
}
|
|
|
|
static void inst_or (void) {
|
|
~decompose(shoe.op, 1000 rrr dss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
if (d) {
|
|
// Dn V <ea> -> <ea>
|
|
// S D R
|
|
call_ea_read(M, sz);
|
|
const uint32_t R = chop(shoe.dat | shoe.d[r], sz);
|
|
shoe.dat = R;
|
|
call_ea_write(M, sz);
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(R==0);
|
|
set_sr_n(mib(R, sz));
|
|
return ;
|
|
}
|
|
else {
|
|
// <ea> V Dn -> Dn
|
|
// S D R
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
|
|
const uint32_t R = chop(shoe.dat | shoe.d[r], sz);
|
|
set_d(r, R, sz);
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(R==0);
|
|
set_sr_n(mib(R, sz));
|
|
return ;
|
|
}
|
|
}
|
|
|
|
|
|
static void inst_moveq (void) {
|
|
~decompose(shoe.op, 0111 rrr 0 dddddddd);
|
|
const int32_t dat = ((int8_t)d);
|
|
shoe.d[r] = dat;
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_z(d==0);
|
|
set_sr_n(d>>7);
|
|
// slog("dat = %x, shoe.d[%u] = %x\n", dat, r, shoe.d[r]);
|
|
// slog("I'm called, right?\n");
|
|
}
|
|
|
|
static void inst_add (void) {
|
|
~decompose(shoe.op, 1101 rrr d ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
uint8_t Sm, Dm, Rm;
|
|
|
|
call_ea_read(M, sz);
|
|
if (d) { // store the result in EA
|
|
// source is Dn, dest is <ea>, result is <ea>
|
|
Sm = mib(shoe.d[r], sz);
|
|
Dm = mib(shoe.dat, sz);
|
|
shoe.dat += get_d(r, sz);
|
|
set_sr_z(chop(shoe.dat, sz) == 0);
|
|
Rm = mib(shoe.dat, sz);
|
|
call_ea_write(M, sz);
|
|
}
|
|
else { // store the result in d[r]
|
|
// source is <ea>, dest in Dn, result is Dn
|
|
Sm = mib(shoe.dat, sz);
|
|
Dm = mib(shoe.d[r], sz);
|
|
const uint32_t R = shoe.dat + get_d(r, sz);
|
|
set_sr_z(chop(R, sz) == 0);
|
|
Rm = mib(R, sz);
|
|
set_d(r, R, sz);
|
|
call_ea_read_commit(M, sz);
|
|
}
|
|
set_sr_v((Sm && Dm && !Rm) || (!Sm && !Dm && Rm));
|
|
set_sr_c((Sm && Dm) || (!Rm && Dm) || (Sm && !Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
}
|
|
|
|
static void inst_adda (void) {
|
|
~decompose(shoe.op, 1101 rrr s11 MMMMMM)
|
|
const uint8_t sz = 2 + 2*s;
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
if (s) { // long
|
|
shoe.a[r] += shoe.dat;
|
|
} else { // word
|
|
const int16_t ea = shoe.dat;
|
|
shoe.a[r] += ea;
|
|
}
|
|
}
|
|
|
|
static void inst_addx (void) {
|
|
~decompose(shoe.op, 1101 xxx 1 ss 00 r yyy);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
const uint8_t extend_bit = (sr_x() != 0);
|
|
|
|
uint8_t Sm, Dm, Rm;
|
|
|
|
if (!r) { // x and y are data registers
|
|
// Sm = mib(shoe.d[y] + extend_bit, sz); // FIXME: I am almost certain this is wrong
|
|
Sm = mib(shoe.d[y], sz);
|
|
Dm = mib(shoe.d[x], sz);
|
|
|
|
const uint32_t R = shoe.d[y] + extend_bit + shoe.d[x];
|
|
|
|
// slog("addx: S d[%u] = 0x%x, D d[%u] = 0x%x, R = 0x%x\n", crop(shoe.d[y], sz), crop(shoe.d[x], sz), crop(R, sz));
|
|
|
|
set_d(x, R, sz);
|
|
Rm = mib(R, sz);
|
|
if (chop(R, sz)) // Z is cleared only if result is non-zero
|
|
set_sr_z(0);
|
|
|
|
}
|
|
else { // memory to memory
|
|
// FIXME: Definitely broken!
|
|
// The subx version thinks that (sz==1) means predecrement 2 bytes, but I have no idea where it got that logic from.
|
|
assert(y!=7 && x!=7);
|
|
const uint32_t predec_y = shoe.a[y] - sz;
|
|
const uint32_t predec_x = shoe.a[x] - sz;
|
|
|
|
const uint32_t S = lget(predec_y, 4);
|
|
if (shoe.abort) return ;
|
|
const uint32_t D = lget(predec_x, 4);
|
|
if (shoe.abort) return ;
|
|
|
|
// Sm = mib(S + extend_bit, sz); // FIXME: sure you want to do this?
|
|
Sm = mib(S, sz);
|
|
Dm = mib(D, sz);
|
|
|
|
const uint32_t R = S + extend_bit + D;
|
|
Rm = mib(R, sz);
|
|
|
|
const uint32_t chopped_R = chop(R, sz);
|
|
lset(chopped_R, sz, shoe.dat);
|
|
if (shoe.abort) return ;
|
|
|
|
shoe.a[y] = predec_y;
|
|
shoe.a[x] = predec_x;
|
|
|
|
if (chopped_R) // Z is cleared only if result is non-zero
|
|
set_sr_z(0);
|
|
|
|
}
|
|
|
|
set_sr_v((Sm && Dm && !Rm) || (!Sm && !Dm && Rm));
|
|
set_sr_c((Sm && Dm) || (!Rm && Dm) || (Sm && !Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
}
|
|
|
|
static void inst_cmp (void) {
|
|
// cmp <ea>, Dn
|
|
// <ea> -> source
|
|
// Dn -> dest
|
|
// (Dn-<ea>) -> result
|
|
~decompose(shoe.op, 1011 rrr ooo MMMMMM);
|
|
const uint8_t sz = 1<<o;
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
const uint8_t Sm = ea_n(sz);
|
|
const uint8_t Dm = mib(get_d(r,sz),sz);
|
|
const uint32_t R = shoe.d[r]-shoe.dat;
|
|
const uint8_t Rm = mib(R, sz);
|
|
|
|
set_sr_z(chop(R,sz)==0);
|
|
set_sr_n(Rm);
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
}
|
|
|
|
static void inst_cmpi (void) {
|
|
~decompose(shoe.op, 0000 1100 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
uint32_t immed;
|
|
|
|
if (s < 2) {
|
|
immed = chop(nextword(), sz);
|
|
} else {
|
|
immed = nextlong();
|
|
}
|
|
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
|
|
// subtract (EA-immediate)
|
|
const uint8_t Sm = mib(immed, sz);
|
|
const uint8_t Dm = ea_n(sz);
|
|
const uint32_t R = shoe.dat - immed;
|
|
const uint8_t Rm = mib(R, sz);
|
|
set_sr_z(chop(R,sz)==0);
|
|
set_sr_n(Rm);
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
}
|
|
|
|
static void inst_cmpa (void) {
|
|
// D - S -> cc
|
|
// Dn, ea
|
|
~decompose(shoe.op, 1011 rrr o11 MMMMMM);
|
|
const uint8_t sz = 2<<o;
|
|
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
|
|
if (!o) { // shoe.dat needs to be sign extended
|
|
const int16_t a = shoe.dat;
|
|
const int32_t b = (int32_t)a;
|
|
shoe.dat = b;
|
|
}
|
|
|
|
const uint32_t R = shoe.a[r] - shoe.dat;
|
|
const uint8_t Rm = mib(R, 4);
|
|
const uint8_t Sm = mib(shoe.dat, 4);
|
|
const uint8_t Dm = mib(shoe.a[r], 4);
|
|
set_sr_z(R == 0); /* <- This is where A/UX 3.0.0 started working */
|
|
set_sr_n(Rm);
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
}
|
|
|
|
static void inst_cmpm (void) {
|
|
~decompose(shoe.op, 1011 xxx 1 ss 001 yyy);
|
|
|
|
const uint8_t sz = 1 << s;
|
|
const uint32_t ax = shoe.a[x];
|
|
const uint32_t ay = shoe.a[y] + ((x==y) ? sz : 0); // if x==y, then we pop the stack twice
|
|
|
|
const uint32_t dst = lget(ax, sz);
|
|
if (shoe.abort) return ;
|
|
|
|
const uint32_t src = lget(ay, sz);
|
|
if (shoe.abort) return ;
|
|
|
|
// *I believe* that if x==y, then that register will only be incremented *once*
|
|
// WRONG!
|
|
|
|
if (sz == 1)
|
|
assert(x!=7 && y!=7);
|
|
|
|
if (x == y) {
|
|
shoe.a[x] += sz + sz;
|
|
}
|
|
else {
|
|
shoe.a[x] += sz;
|
|
shoe.a[y] += sz;
|
|
}
|
|
|
|
const uint8_t Sm = ea_n(sz);
|
|
const uint8_t Dm = mib(dst,sz);
|
|
const uint32_t R = dst - src;
|
|
const uint8_t Rm = mib(R, sz);
|
|
|
|
set_sr_z(chop(R, sz) == 0);
|
|
set_sr_n(Rm);
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
}
|
|
|
|
static void inst_eor (void) {
|
|
~decompose(shoe.op, 1011 rrr 1ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
call_ea_read(M, sz);
|
|
shoe.dat ^= shoe.d[r];
|
|
const uint32_t R = shoe.dat;
|
|
call_ea_write(M, sz);
|
|
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_z(ea_z(sz));
|
|
set_sr_n(ea_n(sz));
|
|
}
|
|
|
|
static void inst_long_mul (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 0100 1100 00 MMMMMM);
|
|
~decompose(ext, 0 LLL u s 0000000 HHH);
|
|
|
|
call_ea_read(M, 4);
|
|
call_ea_read_commit(M, 4);
|
|
|
|
uint64_t R;
|
|
|
|
if (!u) { // if unsigned...
|
|
const uint32_t S = shoe.d[L];
|
|
const uint32_t D = shoe.dat;
|
|
R = ((uint64_t)S) * ((uint64_t)D);
|
|
} else { // if signed...
|
|
const int32_t S = shoe.d[L];
|
|
const int32_t D = shoe.dat;
|
|
const int64_t R_signed = ((int64_t)S) * ((int64_t)D);
|
|
R = R_signed;
|
|
|
|
//slog("long_muls: %d * %d = (int64_t)%lld (int32_t)%d\n", S, D, R, (uint32_t)(R&0xffffffff));
|
|
}
|
|
|
|
shoe.d[L] = (uint32_t)R;
|
|
if (s) { // 64-bit result
|
|
shoe.d[H] = (uint32_t)(R>>32);
|
|
set_sr_n((uint8_t)(R>>63));
|
|
set_sr_z(R==0);
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
} else { // 32-bit result
|
|
// FIXME: I'm not positive this behavior is right:
|
|
// Is sr_n set if the quad-word is negative? or if the long-word is?
|
|
// I'm presuming long-word, but I didn't actually check.
|
|
// Also check whether sr_z is set if R==0
|
|
// (The documentation is vague)
|
|
//
|
|
// Update: I checked, and this seems to be right
|
|
set_sr_v((R>>32)!=0);
|
|
set_sr_n((R>>31)&1);
|
|
set_sr_z((R&0xffffffff)==0);
|
|
set_sr_c(0);
|
|
}
|
|
}
|
|
|
|
static void inst_long_div (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 0100 1100 01 MMMMMM);
|
|
~decompose(ext, 0 qqq u s 0000000 rrr);
|
|
|
|
// uncommitted_ea_read_pc is uninitialized for EA modes that don't change the PC,
|
|
// but we might need it if we need to throw a divide-by-zero exception,
|
|
// so initialize it here, just in case.
|
|
shoe.uncommitted_ea_read_pc = shoe.pc;
|
|
|
|
call_ea_read(M, 4);
|
|
|
|
const uint32_t divisor = shoe.dat;
|
|
if (divisor == 0) {
|
|
throw_frame_two(shoe.orig_sr, shoe.uncommitted_ea_read_pc, 5, shoe.orig_pc);
|
|
return ;
|
|
}
|
|
|
|
call_ea_read_commit(M, 4);
|
|
|
|
uint64_t dividend;
|
|
if (s)
|
|
dividend = (((uint64_t)shoe.d[r])<<32) | shoe.d[q];
|
|
else {
|
|
if (u) // if signed-mode, we need to sign extend 32-bit dividends (This caused a quickdraw bug that took forever to debug!)
|
|
dividend = ((int32_t)shoe.d[q]);
|
|
else
|
|
dividend = shoe.d[q];
|
|
}
|
|
|
|
uint64_t Q;
|
|
uint32_t R;
|
|
if (u) { // if signed
|
|
Q = (uint64_t)((int64_t)(((int64_t)dividend) / ((int32_t)divisor)));
|
|
R = (uint32_t)((int32_t)(((int64_t)dividend) % ((int32_t)divisor)));
|
|
} else { // if unsigned
|
|
Q = ((uint64_t)dividend) / ((uint32_t)divisor);
|
|
R = ((uint64_t)dividend) % ((uint32_t)divisor);
|
|
}
|
|
|
|
if (s) { // if 64 bit dividend,
|
|
const uint8_t overflow = ((Q>>32)!=0);
|
|
set_sr_z((Q & 0xffffffff) == 0);
|
|
set_sr_n((Q>>31)&1);
|
|
set_sr_v(overflow);
|
|
set_sr_c(0);
|
|
if (!overflow) { // only modify the registers if !overflow
|
|
shoe.d[r] = R;
|
|
shoe.d[q] = (uint32_t)Q;
|
|
}
|
|
} else { // if 32 bit dividend
|
|
set_sr_z((Q & 0xffffffff) == 0);
|
|
set_sr_n((Q>>31)&1);
|
|
set_sr_v(0); // can't overflow with 32 bit dividend
|
|
set_sr_c(0);
|
|
shoe.d[r] = R;
|
|
shoe.d[q] = (uint32_t)Q; // if r==q, then only set r[q]=Q
|
|
}
|
|
}
|
|
|
|
static void inst_addq (void) {
|
|
~decompose(shoe.op, 0101 ddd 0 ss MMMMMM);
|
|
const uint8_t dat = d + ((!d)<<3);
|
|
|
|
if ((M>>3) == 1) { // size is always long if using addr register, CCodes aren't set.
|
|
shoe.a[M&7] += dat;
|
|
return ;
|
|
}
|
|
|
|
call_ea_read(M, 1<<s);
|
|
const uint8_t Sm = 0; // Sm should be always be 0 because source is always 1-8 (I think)
|
|
const uint8_t Dm = ea_n(1<<s);
|
|
shoe.dat += dat;
|
|
const uint8_t Rm = ea_n(1<<s);
|
|
|
|
set_sr_v((Sm && Dm && !Rm) || (!Sm && !Dm && Rm));
|
|
set_sr_c((Sm && Dm) || (!Rm && Dm) || (Sm && !Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
set_sr_z(ea_z(1<<s));
|
|
call_ea_write(M, 1<<s);
|
|
}
|
|
|
|
static void inst_subq (void) {
|
|
~decompose(shoe.op, 0101 ddd 1 ss MMMMMM);
|
|
const uint8_t dat = d + ((!d)<<3);
|
|
|
|
if ((M>>3) == 1) { // Use long-size for addr registers
|
|
shoe.a[M&7] -= dat;
|
|
return ;
|
|
}
|
|
|
|
call_ea_read(M, 1<<s);
|
|
const uint8_t Sm = 0; // Sm should be always be 0 because source is always 1-8 (I think)
|
|
const uint8_t Dm = ea_n(1<<s);
|
|
shoe.dat -= dat;
|
|
const uint8_t Rm = ea_n(1<<s);
|
|
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
set_sr_z(ea_z(1<<s));
|
|
call_ea_write(M, 1<<s);
|
|
}
|
|
|
|
static void inst_movea (void) {
|
|
~decompose(shoe.op, 00 1s rrr 001 MMMMMM);
|
|
|
|
const uint8_t sz = 4 >> s;
|
|
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
if (s) { // word-size, sign extend shoe.dat
|
|
const int16_t dat = shoe.dat;
|
|
shoe.a[r] = (int32_t)dat;
|
|
} else {
|
|
shoe.a[r] = shoe.dat;
|
|
}
|
|
}
|
|
|
|
static void inst_move_d_to_d (void) {
|
|
~decompose(shoe.op, 00 ab RRR 000 000 rrr); // r=source, R=dest
|
|
|
|
const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long)
|
|
const uint32_t val = chop(shoe.d[r], sz);
|
|
|
|
set_d(R, val, sz);
|
|
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_n(mib(val, sz));
|
|
set_sr_z(val == 0);
|
|
}
|
|
|
|
static void inst_move_from_d (void) {
|
|
~decompose(shoe.op, 00 ab RRR MMM 000 rrr); // r=source, MR=dest
|
|
|
|
const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long)
|
|
const uint32_t val = chop(shoe.d[r], sz);
|
|
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_n(mib(val, sz));
|
|
set_sr_z(val == 0);
|
|
|
|
shoe.dat = val;
|
|
call_ea_write((M << 3) | R, sz);
|
|
}
|
|
|
|
static void inst_move_to_d (void) {
|
|
~decompose(shoe.op, 00 ab rrr 000 mmmmmm); // m=source, r=dest
|
|
const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long)
|
|
|
|
call_ea_read(m, sz);
|
|
call_ea_read_commit(m, sz);
|
|
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_n(ea_n(sz));
|
|
set_sr_z(ea_z(sz));
|
|
set_d(r, shoe.dat, sz);
|
|
}
|
|
|
|
static void inst_move (void) {
|
|
~decompose(shoe.op, 00 ab RRR MMM mmm rrr); // mr=source, MR=dest
|
|
const uint8_t sz = 1<<(a+(!b)); // (1=byte, 3=word, 2=long)
|
|
|
|
call_ea_read((m<<3) | r, sz);
|
|
call_ea_read_commit((m<<3) | r, sz); // We aren't writing-back, we're reading from one EA and writing to another
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_n(ea_n(sz));
|
|
set_sr_z(ea_z(sz));
|
|
|
|
shoe.mr = (M << 3) | R;
|
|
shoe.sz = sz;
|
|
ea_write();
|
|
|
|
// There's a problem here
|
|
// If the source EA is -(a7) or (a7)+, and ea_write fails while running in user-mode
|
|
// Then it'll switch to supervisor mode while throwing the exception, and swap out USP for
|
|
// MSP. Then modifying a[7] here will corrupt MSP.
|
|
// So in this case, we need to switch back to the original a7 (set_sr(shoe.orig_sr)) before modifying it
|
|
// (This is hacky)
|
|
|
|
if (shoe.abort) {
|
|
if (m == 4 || m == 3) {
|
|
const uint16_t new_sr = shoe.sr;
|
|
const uint8_t delta = ((r==7) && (sz==1)) ? 2 : sz;
|
|
|
|
set_sr(shoe.orig_sr); // See hack comment above
|
|
|
|
// if read was a post-increment, pre-decrement, then we need to rollback that change
|
|
if (m == 3) // postincrement
|
|
shoe.a[r] -= delta;
|
|
else // predecrement
|
|
shoe.a[r] += delta;
|
|
|
|
set_sr(new_sr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void inst_not (void) {
|
|
~decompose(shoe.op, 0100 0110 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
call_ea_read(M, sz);
|
|
shoe.dat = ~~shoe.dat;
|
|
set_sr_z(ea_z(sz));
|
|
set_sr_n(ea_n(sz));
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
|
|
static void inst_reset (void) {
|
|
verify_supervisor();
|
|
|
|
shoebill_restart();
|
|
}
|
|
|
|
static void inst_movec (void) {
|
|
verify_supervisor();
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 0100 1110 0111 101x);
|
|
~decompose(ext, t rrr cccccccccccc);
|
|
uint32_t *reg = t?(shoe.a):(shoe.d);
|
|
switch (c) {
|
|
case 0x002: { // CACR
|
|
// FIXME: I think more bits are usable in CACR
|
|
if (x) shoe.cacr = reg[r] & 0x80008080;
|
|
else reg[r] = shoe.cacr;
|
|
return ;
|
|
}
|
|
/* // These are >'020 registers
|
|
case 0x003: { // TC
|
|
// TC is a 16 bit register, but movec is always a 32bit
|
|
if (x) shoe.tc = reg[r] & ~b(1100 0000 0000 0000);
|
|
else reg[r] = shoe.tc & ~b(1100 0000 0000 0000);
|
|
return ;
|
|
}
|
|
case 0x006: // DTT0
|
|
if (x) shoe.dtt0 = reg[r] & ~b(11111111 11111111 11100011 01100100);
|
|
else reg[r] = shoe.dtt0 & ~b(11111111 11111111 11100011 01100100);
|
|
return ;
|
|
case 0x007: // DTT1
|
|
if (x) shoe.dtt1 = reg[r] & ~b(11111111 11111111 11100011 01100100);
|
|
else reg[r] = shoe.dtt1 & ~b(11111111 11111111 11100011 01100100);
|
|
return ; */
|
|
case 0x801: // VBR
|
|
if (x) shoe.vbr = reg[r];
|
|
else reg[r] = shoe.vbr;
|
|
return ;
|
|
case 0x800: // USP
|
|
make_stack_pointers_valid();
|
|
if (x) shoe.usp = reg[r];
|
|
else reg[r] = shoe.usp;
|
|
load_stack_pointer();
|
|
return ;
|
|
// (NO!) case 0x802: // CAAR (not supported on 68040)
|
|
case 0x000: // SFC
|
|
if (x) shoe.sfc = reg[r] & 7;
|
|
else reg[r] = shoe.sfc & 7;
|
|
return ;
|
|
case 0x001: // DFC
|
|
if (x) shoe.dfc = reg[r] & 7;
|
|
else reg[r] = shoe.dfc & 7;
|
|
return ;
|
|
case 0x803: // MSP
|
|
case 0x804: // ISP
|
|
case 0x004: // ITT0
|
|
case 0x005: // ITT1
|
|
case 0x805: // MMUSR
|
|
case 0x806: // URP
|
|
case 0x807: // SRP
|
|
slog("inst_movec: error! I don't support this condition code yet! (0x%03x)\n", c);
|
|
assert(!"inst_movec: error: unknown condition\n");
|
|
return ;
|
|
|
|
default:
|
|
return throw_illegal_instruction();
|
|
}
|
|
}
|
|
|
|
static void inst_moves (void) {
|
|
verify_supervisor();
|
|
const uint16_t ext = nextword();
|
|
|
|
~decompose(shoe.op, 0000 1110 ss MMMMMM);
|
|
~decompose(ext, a rrr d 00000000000);
|
|
|
|
const uint8_t fc = d ? shoe.dfc : shoe.sfc;
|
|
const uint8_t sz = 1<<s;
|
|
|
|
/* From 68851 documentation
|
|
* 0: undefined
|
|
* 1: User data space
|
|
* 2: User code space
|
|
* 3: undefined
|
|
* 4: undefined
|
|
* 5: Supervisor data space
|
|
* 6: Supervisor code space
|
|
* 7: CPU space
|
|
*/
|
|
|
|
// For now, only supporting fc 1 (user data space)
|
|
if (fc != 1) {
|
|
slog("inst_moves: error: hit fc=%u\n", fc);
|
|
assert(!"inst_moves: error, hit weird function code");
|
|
return ;
|
|
}
|
|
|
|
uint32_t addr;
|
|
if ((M>>3) == 4) {
|
|
assert((M&7) != 7);
|
|
// Predecrement
|
|
addr = shoe.a[M & 7] - sz;
|
|
}
|
|
else if ((M>>3) < 4) {
|
|
if ((M>>3) == 3) assert((M&7) != 7);
|
|
// Post increment or addr indirect
|
|
addr = shoe.a[M & 7];
|
|
}
|
|
else {
|
|
// all other EA modes
|
|
call_ea_addr(M);
|
|
addr = (uint32_t)shoe.dat;
|
|
}
|
|
|
|
if (d) {
|
|
const uint32_t data = chop(a ? shoe.a[r] : shoe.d[r], sz);
|
|
|
|
// Motorola's 68k documentation has a note that for moves An,(An)+ or -(An),
|
|
// the value of An written is incremented or decremented
|
|
// XXX: implement this
|
|
assert(! ((a && (M&7)==r) && (((M>>3) == 3) || ((M>>3) == 4))) );
|
|
|
|
lset_fc(addr, sz, data, fc);
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
else {
|
|
uint32_t data = lget_fc(addr, sz, fc);
|
|
if (shoe.abort)
|
|
return ;
|
|
|
|
// if destination is address register, data is sign extended to 32 bits
|
|
if (a && (sz == 1)) {
|
|
int8_t d8 = data;
|
|
int32_t d32 = d8;
|
|
data = d32;
|
|
}
|
|
else if (a && (sz == 2)) {
|
|
int16_t d16 = data;
|
|
int32_t d32 = d16;
|
|
data = d32;
|
|
}
|
|
|
|
if (a)
|
|
shoe.a[r] = data;
|
|
else
|
|
set_d(r, data, sz);
|
|
|
|
}
|
|
|
|
// commit predecrement/postincrement addr reg changes
|
|
if ((M>>3) == 4)
|
|
shoe.a[M & 7] -= sz;
|
|
else if ((M>>3) == 3)
|
|
shoe.a[M & 7] += sz;
|
|
|
|
}
|
|
|
|
static void inst_cas (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 0000 1ss0 11 MMMMMM);
|
|
~decompose(ext, 0000 000 uuu 000 ccc);
|
|
|
|
const uint8_t sz = (1 << s) >> 1; // (01 -> byte, 10 -> word, 11 -> long)
|
|
|
|
call_ea_read(M, sz);
|
|
|
|
/* The dest/source/result operands are reversed here from inst_cmp */
|
|
|
|
// EA = destination
|
|
// Rc = source
|
|
// EA = result
|
|
const uint32_t chopped_source = get_d(c, sz);
|
|
const uint32_t R = shoe.dat - chopped_source;
|
|
const _Bool Sm = mib(get_d(c, sz), sz);
|
|
const _Bool Dm = ea_n(sz);
|
|
const _Bool Rm = mib(R, sz);
|
|
const _Bool z = (chop(R, sz) == 0);
|
|
|
|
if (z) {
|
|
// "If the operands are equal, the instruction writes the
|
|
// update operand (Du) to the effective address operand"
|
|
shoe.dat = get_d(u, sz);
|
|
call_ea_write(M, sz);
|
|
}
|
|
else {
|
|
// "otherwise, the instruction writes the effective
|
|
// address operand to the compare operand"
|
|
call_ea_read_commit(M, sz);
|
|
set_d(c, shoe.dat, sz);
|
|
}
|
|
|
|
set_sr_z(z);
|
|
set_sr_n(Rm);
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
}
|
|
|
|
static void inst_cas2 (void) {
|
|
assert(!"inst_cas2: error: not implemented!");
|
|
}
|
|
|
|
static void inst_move_to_sr (void) {
|
|
verify_supervisor();
|
|
~decompose(shoe.op, 0100 0110 11 MMMMMM);
|
|
call_ea_read(M, 2); // sr is 2 bytes
|
|
call_ea_read_commit(M, 2);
|
|
|
|
set_sr(shoe.dat);
|
|
}
|
|
|
|
static void inst_move_from_sr (void) {
|
|
verify_supervisor();
|
|
~decompose(shoe.op, 0100 0000 11 MMMMMM);
|
|
shoe.dat = shoe.sr;
|
|
call_ea_write(M, 2);
|
|
}
|
|
|
|
static void inst_neg (void) {
|
|
~decompose(shoe.op, 0100 0100 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
call_ea_read(M, sz);
|
|
const uint32_t D = shoe.dat;
|
|
const uint32_t R = 0 - D;
|
|
const uint8_t Dm = mib(D, sz);
|
|
const uint8_t Rm = mib(R, sz);
|
|
shoe.dat = R;
|
|
call_ea_write(M, sz);
|
|
|
|
const uint32_t result = chop(R, sz);
|
|
|
|
set_sr_z(result == 0);
|
|
set_sr_c(result != 0);
|
|
set_sr_x(result != 0);
|
|
set_sr_v(Dm && Rm);
|
|
set_sr_n(Rm);
|
|
}
|
|
|
|
static void inst_negx (void) {
|
|
~decompose(shoe.op, 0100 0000 ss MMMMMM);
|
|
|
|
const uint8_t x = (sr_x() != 0);
|
|
|
|
const uint8_t sz = 1<<s;
|
|
call_ea_read(M, sz);
|
|
|
|
const uint32_t D = shoe.dat;
|
|
const uint32_t R = 0 - D - x;
|
|
const uint8_t Dm = mib(D, sz);
|
|
const uint8_t Rm = mib(R, sz);
|
|
shoe.dat = R;
|
|
call_ea_write(M, sz);
|
|
|
|
const uint32_t c = chop(R, sz);
|
|
if (c != 0)
|
|
set_sr_z(0);
|
|
set_sr_n(Rm);
|
|
set_sr_v(Dm && Rm);
|
|
set_sr_c(Dm || Rm);
|
|
set_sr_x(Dm || Rm);
|
|
}
|
|
|
|
static void inst_move_from_ccr (void) {
|
|
~decompose(shoe.op, 0100 0010 11 MMMMMM);
|
|
shoe.dat = shoe.sr & 0xff;
|
|
call_ea_write(M, 2);
|
|
}
|
|
|
|
static void inst_move_to_ccr (void) {
|
|
~decompose(shoe.op, 0100 0100 11 MMMMMM);
|
|
call_ea_read(M, 2); // sr is 2 bytes
|
|
call_ea_read_commit(M, 2);
|
|
|
|
const uint16_t new_sr = (shoe.sr & 0xff00) | (shoe.dat & 0xff);
|
|
|
|
set_sr(new_sr);
|
|
}
|
|
|
|
static void inst_tst (void) {
|
|
~decompose(shoe.op, 0100 1010 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
set_sr_n(ea_n(sz));
|
|
set_sr_z(ea_z(sz));
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
}
|
|
|
|
static void inst_clr (void) {
|
|
~decompose(shoe.op, 0100 0010 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
shoe.dat = 0;
|
|
call_ea_write(M, sz);
|
|
set_sr_n(0);
|
|
set_sr_z(1);
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
}
|
|
|
|
static void inst_lea (void) {
|
|
~decompose(shoe.op, 0100 rrr 111 MMMMMM);
|
|
call_ea_addr(M);
|
|
shoe.a[r] = shoe.dat;
|
|
}
|
|
|
|
static void inst_sub (void) {
|
|
~decompose(shoe.op, 1001 rrr dss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
// make sure the high order bytes of shoe.dat are cleared
|
|
// (I don't think this should be necessary, ea_*() should guarantee the highorder bytes are cleared)
|
|
shoe.dat = 0;
|
|
call_ea_read(M, sz);
|
|
if (d) { // <ea> - Dn -> <ea>
|
|
const uint32_t result = shoe.dat - get_d(r, sz);
|
|
{
|
|
const uint8_t Sm = mib(shoe.d[r], sz);
|
|
const uint8_t Dm = mib(shoe.dat, sz);
|
|
const uint8_t Rm = mib(result, sz);
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
set_sr_z(chop(result,sz)==0);
|
|
}
|
|
shoe.dat = result;
|
|
call_ea_write(M, sz);
|
|
}
|
|
else { // Dn - <ea> -> Dn
|
|
const uint32_t result = get_d(r, sz) - shoe.dat;
|
|
{
|
|
const uint8_t Sm = mib(shoe.dat, sz);
|
|
const uint8_t Dm = mib(shoe.d[r], sz);
|
|
const uint8_t Rm = mib(result, sz);
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
set_sr_z(chop(result,sz)==0);
|
|
}
|
|
set_d(r, result, sz);
|
|
call_ea_read_commit(M, sz);
|
|
}
|
|
}
|
|
|
|
static void inst_subi (void) {
|
|
~decompose(shoe.op, 0000 0100 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
// ea = ea - immed
|
|
// R D S
|
|
|
|
// fetch immediate word
|
|
uint32_t immed;
|
|
if (s==0) {
|
|
immed = (int8_t)(nextword() & 0xff);
|
|
} else if (s==1) {
|
|
immed = (int16_t)nextword();
|
|
} else {
|
|
immed = nextlong();
|
|
}
|
|
|
|
// fetch the destination operand
|
|
call_ea_read(M, sz);
|
|
|
|
// do the subtraction
|
|
const uint32_t result = shoe.dat - immed;
|
|
// find the MIBs for source, dest, and result
|
|
const uint8_t Sm = mib(immed, sz);
|
|
const uint8_t Dm = mib(shoe.dat, sz);
|
|
const uint8_t Rm = mib(result, sz);
|
|
// set sr flags
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
set_sr_z(chop(result,sz)==0);
|
|
|
|
// writeback
|
|
shoe.dat = chop(result, sz);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
static void inst_addi (void) {
|
|
~decompose(shoe.op, 0000 0110 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
// fetch immediate word
|
|
uint32_t immed;
|
|
if (s==0) {
|
|
immed = (int8_t)(nextword() & 0xff);
|
|
} else if (s==1) {
|
|
immed = (int16_t)nextword();
|
|
} else {
|
|
immed = nextlong();
|
|
}
|
|
|
|
call_ea_read(M, sz);
|
|
const uint32_t Sm = mib(immed, sz);
|
|
const uint32_t Dm = mib(shoe.dat, sz);
|
|
const uint32_t R = shoe.dat + immed;
|
|
const uint32_t Rm = mib(R, sz);
|
|
shoe.dat = R;
|
|
call_ea_write(M, sz);
|
|
|
|
set_sr_z(chop(R, sz)==0);
|
|
set_sr_n(mib(R, sz));
|
|
set_sr_v((Sm && Dm && !Rm) || (!Sm && !Dm && Rm));
|
|
const uint8_t carry = ((Sm && Dm) || (!Rm && Dm) || (Sm && !Rm));
|
|
set_sr_c(carry);
|
|
set_sr_x(carry);
|
|
}
|
|
|
|
static void inst_eori (void) {
|
|
~decompose(shoe.op, 0000 1010 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
// fetch immediate word
|
|
uint32_t immed;
|
|
if (s==0) {
|
|
immed = (int8_t)(nextword() & 0xff);
|
|
} else if (s==1) {
|
|
immed = (int16_t)nextword();
|
|
} else {
|
|
immed = nextlong();
|
|
}
|
|
|
|
call_ea_read(M, sz);
|
|
const uint32_t R = shoe.dat ^ immed;
|
|
shoe.dat = R;
|
|
call_ea_write(M, sz);
|
|
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_n(mib(R, sz));
|
|
set_sr_z(chop(R, sz)==0);
|
|
}
|
|
|
|
|
|
static void inst_andi (void) {
|
|
~decompose(shoe.op, 0000 0010 ss MMMMMM);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
// fetch immediate word
|
|
uint32_t immed;
|
|
if (s==0) {
|
|
immed = (int8_t)(nextword() & 0xff);
|
|
} else if (s==1) {
|
|
immed = (int16_t)nextword();
|
|
} else {
|
|
immed = nextlong();
|
|
}
|
|
|
|
// fetch the destination operand
|
|
call_ea_read(M, sz);
|
|
// do the AND
|
|
const uint32_t result = shoe.dat & immed;
|
|
// writeback
|
|
shoe.dat = result;
|
|
call_ea_write(M, sz);
|
|
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_n(mib(result, sz));
|
|
set_sr_z(chop(result,sz)==0);
|
|
}
|
|
|
|
static void inst_ori (void) {
|
|
~decompose(shoe.op, 0000 0000 ss MMMMMM)
|
|
const uint8_t sz = 1<<s;
|
|
|
|
// fetch immediate word
|
|
uint32_t immed;
|
|
if (s==0) {
|
|
immed = (int8_t)(nextword() & 0xff);
|
|
} else if (s==1) {
|
|
immed = (int16_t)nextword();
|
|
} else {
|
|
immed = nextlong();
|
|
}
|
|
|
|
// fetch the destination operand
|
|
call_ea_read(M, sz);
|
|
// do the AND
|
|
const uint32_t result = shoe.dat | immed;
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
set_sr_n(mib(result, sz));
|
|
set_sr_z(chop(result,sz)==0);
|
|
|
|
// writeback
|
|
shoe.dat = chop(result, sz);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
static void inst_btst_immediate (void) {
|
|
~decompose(shoe.op, 0000 1000 00 MMMMMM);
|
|
const uint16_t ext = nextword();
|
|
~decompose(ext, 00000000 bbbbbbbb);
|
|
|
|
/*
|
|
* "When a data register is the destination, any of the 32 bits can be
|
|
* specified by a modulo 32-bit number." Otherwise, it's modulo 8.
|
|
*/
|
|
if ((M >> 3) == 0) {
|
|
set_sr_z(! ((shoe.d[M&7] >> (ext % 32)) & 1) );
|
|
return ;
|
|
}
|
|
|
|
const uint8_t n = b % 8;
|
|
call_ea_read(M, 1);
|
|
call_ea_read_commit(M, 1);
|
|
|
|
set_sr_z(!((shoe.dat >> n)&1));
|
|
}
|
|
|
|
static void inst_pea (void) {
|
|
~decompose(shoe.op, 0100 1000 01 MMMMMM);
|
|
// fetch the EA
|
|
call_ea_addr(M);
|
|
// push it onto the stack
|
|
lset(shoe.a[7]-4, 4, shoe.dat);
|
|
if (shoe.abort) return ;
|
|
// decrement the stack pointer if lset didn't abort
|
|
shoe.a[7] -= 4;
|
|
}
|
|
|
|
static void inst_subx (void) {
|
|
~decompose(shoe.op, 1001 yyy 1 ss 00 m xxx);
|
|
const uint8_t sz = 1<<s;
|
|
|
|
if (m) {
|
|
assert(x!=7 && y!=7);
|
|
assert(sz != 1); // If this ever fires, the next line is broken
|
|
const uint8_t predecrement_sz = s?(1<<s):2; // predecrement-mode for size==byte decrements by 2 bytes
|
|
const uint32_t predecrement_x = shoe.a[x]-predecrement_sz;
|
|
const uint32_t predecrement_y = shoe.a[y]-predecrement_sz;
|
|
|
|
const uint32_t src = lget(predecrement_x, sz);
|
|
if (shoe.abort) return ;
|
|
|
|
const uint32_t dst = lget(predecrement_y, sz);
|
|
if (shoe.abort) return ;
|
|
|
|
const uint32_t result = dst - src - (sr_x()?1:0);
|
|
|
|
lset(predecrement_y, sz, result);
|
|
|
|
shoe.a[x] = predecrement_x;
|
|
shoe.a[y] = predecrement_y;
|
|
|
|
const uint8_t Sm = mib(src, sz);
|
|
const uint8_t Dm = mib(dst, sz);
|
|
const uint8_t Rm = mib(result, sz);
|
|
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
if (chop(result, sz)!=0)
|
|
set_sr_z(0);
|
|
}
|
|
else {
|
|
const uint32_t src = shoe.d[x];
|
|
const uint32_t dst = shoe.d[y];
|
|
const uint32_t result = dst - src - (sr_x()?1:0);
|
|
|
|
set_d(y, result, sz);
|
|
|
|
const uint8_t Sm = mib(src, sz);
|
|
const uint8_t Dm = mib(dst, sz);
|
|
const uint8_t Rm = mib(result, sz);
|
|
|
|
set_sr_v((!Sm && Dm && !Rm) || (Sm && !Dm && Rm));
|
|
set_sr_c((Sm && !Dm) || (Rm && !Dm) || (Sm && Rm));
|
|
set_sr_x(sr_c());
|
|
set_sr_n(Rm);
|
|
if (chop(result, sz)!=0)
|
|
set_sr_z(0);
|
|
}
|
|
}
|
|
|
|
static void inst_suba (void) {
|
|
~decompose(shoe.op, 1001 rrr s11 MMMMMM);
|
|
const uint8_t sz = 2<<s;
|
|
|
|
// suba <ea>, An
|
|
// An - <ea> -> An
|
|
// D S R
|
|
|
|
call_ea_read(M, sz);
|
|
call_ea_read_commit(M, sz);
|
|
// if sz==word, sign extend source to long
|
|
const uint32_t S = s ? shoe.dat : ((uint32_t) ((int16_t)shoe.dat));
|
|
const uint32_t R = shoe.a[r] - S;
|
|
shoe.a[r] = R;
|
|
|
|
// don't set any condition codes!
|
|
}
|
|
|
|
static void inst_dbcc (void) {
|
|
~decompose(shoe.op, 0101 cccc 11001 rrr);
|
|
if (evaluate_cc[c]()) {
|
|
shoe.pc += 2;
|
|
}
|
|
else {
|
|
const int16_t disp = nextword();
|
|
const uint16_t newd = get_d(r, 2) - 1;
|
|
set_d(r, newd, 2);
|
|
if (newd != 0xffff)
|
|
shoe.pc = shoe.pc + disp - 2;
|
|
}
|
|
}
|
|
|
|
static void inst_bsr (void) {
|
|
~decompose(shoe.op, 0110 0001 dddddddd);
|
|
uint32_t new_pc = shoe.pc;
|
|
|
|
// find the new PC
|
|
if ((d==0) || (d==0xff)) {
|
|
if (d==0xff) {
|
|
new_pc += nextlong();
|
|
}
|
|
else
|
|
new_pc += ((int16_t)nextword());
|
|
}
|
|
else {
|
|
uint8_t tmp = d;
|
|
new_pc += ((int8_t)d);
|
|
}
|
|
|
|
lset(shoe.a[7]-4, 4, shoe.pc);
|
|
if (shoe.abort) return ;
|
|
shoe.a[7] -= 4;
|
|
shoe.pc = new_pc;
|
|
}
|
|
|
|
static void inst_bcc (void) {
|
|
const uint32_t orig_pc = shoe.pc;
|
|
~decompose(shoe.op, 0110 cccc dddddddd);
|
|
|
|
if (evaluate_cc[c]()) {
|
|
if (d == 0) {
|
|
const int16_t ext = (int16_t)nextword();
|
|
shoe.pc = orig_pc + ext;
|
|
}
|
|
else if (d == 0xff) {
|
|
shoe.pc = orig_pc + nextlong();
|
|
}
|
|
else {
|
|
const int8_t tmp = (int8_t)d;
|
|
shoe.pc = orig_pc + tmp;
|
|
}
|
|
}
|
|
else {
|
|
if (d == 0) shoe.pc += 2;
|
|
else if (d == 0xff) shoe.pc += 4;
|
|
}
|
|
}
|
|
|
|
static void inst_scc (void) {
|
|
~decompose(shoe.op, 0101 cccc 11 MMMMMM);
|
|
|
|
shoe.dat = evaluate_cc[c]() ? 0xff : 0;
|
|
call_ea_write(M, 1);
|
|
}
|
|
|
|
static void inst_nop (void) {}
|
|
|
|
static void inst_chk (void) {
|
|
~decompose(shoe.op, 0100 rrr 1s 0 MMMMMM);
|
|
|
|
const uint8_t sz = s ? 2 : 4;
|
|
|
|
call_ea_read(M, sz);
|
|
|
|
int32_t reg, ea;
|
|
|
|
if (s) { // word
|
|
int16_t tmp = shoe.d[r] & 0xffff;
|
|
reg = tmp;
|
|
tmp = shoe.dat & 0xffff;
|
|
ea = tmp;
|
|
}
|
|
else { // long word
|
|
reg = shoe.d[r];
|
|
ea = shoe.dat & 0xffffffff;
|
|
}
|
|
|
|
if ((reg < 0) || (reg > ea)) {
|
|
set_sr_n((reg < 0));
|
|
throw_frame_two(shoe.sr, shoe.pc, 6, shoe.orig_pc);
|
|
}
|
|
|
|
call_ea_read_commit(M, sz);
|
|
|
|
return ;
|
|
}
|
|
|
|
static void inst_jsr (void) {
|
|
~decompose(shoe.op, 0100 1110 10 MMMMMM);
|
|
call_ea_addr(M);
|
|
|
|
// my quadra 800 doesn't object when a7 is odd...
|
|
// so, no extra error checking needed
|
|
lset(shoe.a[7]-4, 4, shoe.pc);
|
|
if (shoe.abort) return ;
|
|
|
|
// slog("jsr: writing pc (0x%08x) to *0x%08x (phys=0x%08x)\n", shoe.pc, shoe.a[7]-4, shoe.physical_addr);
|
|
|
|
shoe.a[7] -= 4;
|
|
shoe.pc = shoe.dat;
|
|
/*
|
|
if (sr_s()) {
|
|
//return ;
|
|
coff_symbol *symb = coff_find_func(shoe.coff, shoe.pc);
|
|
|
|
if (symb) {
|
|
sprintf(ring_tmp, "CALL (s) %s+%u 0x%08x\n", symb->name, shoe.pc-symb->value, shoe.pc);
|
|
ring_print(ring_tmp);
|
|
}
|
|
else {
|
|
sprintf(ring_tmp, "CALL (s) unknown+ 0x%08x\n", shoe.pc);
|
|
ring_print(ring_tmp);
|
|
}
|
|
|
|
}
|
|
else {
|
|
char *name = (char*)"unknown";
|
|
uint32_t value = 0;
|
|
if ((shoe.pc >= 0x40000000) && (shoe.pc < 0x50000000)) {
|
|
uint32_t i, addr = shoe.pc % (shoe.physical_rom_size);
|
|
for (i=0; macii_rom_symbols[i].name; i++) {
|
|
if (macii_rom_symbols[i].addr > addr) {
|
|
break;
|
|
}
|
|
name = (char*)macii_rom_symbols[i].name;
|
|
value = macii_rom_symbols[i].addr;
|
|
}
|
|
}
|
|
|
|
|
|
sprintf(ring_tmp, "CALL (u) %s+%u 0x%08x\n", name, shoe.pc-value, shoe.pc);
|
|
ring_print(ring_tmp);
|
|
}
|
|
*/
|
|
|
|
}
|
|
|
|
static void inst_jmp (void) {
|
|
~decompose(shoe.op, 0100 1110 11 MMMMMM);
|
|
call_ea_addr(M);
|
|
shoe.pc = shoe.dat;
|
|
}
|
|
|
|
static void inst_link_word (void) {
|
|
~decompose(shoe.op, 0100 1110 0101 0 rrr);
|
|
const int16_t ext = nextword();
|
|
|
|
// push the contents of the address register onto the stack
|
|
lset(shoe.a[7]-4, 4, shoe.a[r]);
|
|
if (shoe.abort) return ;
|
|
shoe.a[7] -= 4;
|
|
|
|
// load the updated stack pointer into the address register
|
|
shoe.a[r] = shoe.a[7];
|
|
|
|
// add the (sign-extended) displacement value to the stack pointer
|
|
shoe.a[7] += ext;
|
|
}
|
|
|
|
static void inst_unlk (void) {
|
|
~decompose(shoe.op, 0100 1110 0101 1 rrr);
|
|
|
|
const uint32_t pop = lget(shoe.a[r], 4);
|
|
if (shoe.abort) return ;
|
|
|
|
// loads the stack pointer from the address register
|
|
shoe.a[7] = shoe.a[r]+4;
|
|
|
|
// loads the address register with the long word popped from the stack
|
|
shoe.a[r] = pop;
|
|
}
|
|
|
|
static void inst_rts (void) {
|
|
const uint32_t pop = lget(shoe.a[7], 4);
|
|
if (shoe.abort) return ;
|
|
|
|
shoe.a[7] += 4;
|
|
shoe.pc = pop;
|
|
|
|
/*
|
|
if (sr_s() && 0) {
|
|
// return ;
|
|
coff_symbol *symb = coff_find_func(shoe.coff, shoe.pc);
|
|
if (symb)
|
|
slog("RETURN TO %s+%u 0x%08x\n", symb->name, shoe.pc-symb->value, shoe.pc);
|
|
}
|
|
else if (0){
|
|
char *name = (char*)"unknown";
|
|
uint32_t value = 0;
|
|
if ((shoe.pc >= 0x40000000) && (shoe.pc < 0x50000000)) {
|
|
uint32_t i, addr = shoe.pc % (shoe.physical_rom_size);
|
|
for (i=0; macii_rom_symbols[i].name; i++) {
|
|
if (macii_rom_symbols[i].addr > addr) {
|
|
break;
|
|
}
|
|
name = (char*)macii_rom_symbols[i].name;
|
|
value = macii_rom_symbols[i].addr;
|
|
}
|
|
}
|
|
|
|
slog("RETURN TO %s+%u 0x%08x\n", name, shoe.pc-value, shoe.pc);
|
|
}*/
|
|
}
|
|
|
|
|
|
static void inst_link_long (void) {
|
|
~decompose(shoe.op, 0100 1000 0000 1 rrr);
|
|
const uint32_t disp = nextlong();
|
|
|
|
// push the contents of the address register onto the stack
|
|
lset(shoe.a[7]-4, 4, shoe.a[r]);
|
|
if (shoe.abort) return ;
|
|
shoe.a[7] -= 4;
|
|
|
|
// load the updated stack pointer into the address register
|
|
shoe.a[r] = shoe.a[7];
|
|
|
|
// add the displacement value to the stack pointer
|
|
shoe.a[7] += disp;
|
|
}
|
|
|
|
static void inst_movem (void) {
|
|
const uint16_t mask = nextword();
|
|
~decompose(shoe.op, 0100 1d00 1s MMMMMM);
|
|
const uint8_t sz = 2<<s; // s==0 => short, s==1 => long
|
|
uint32_t i;
|
|
|
|
if (d) { // memory->register
|
|
if (~bmatch(M, xx011xxx)) // if postincrement,
|
|
shoe.dat = shoe.a[M&7]; // hand-parse this address mode
|
|
else
|
|
call_ea_addr(M); // otherwise, ea_addr() can handle it
|
|
|
|
// extract the bitfields for a and d registers,
|
|
const uint8_t dfield = mask&0xff;
|
|
const uint8_t afield = (mask>>8)&0xff;
|
|
|
|
// read in the data registers
|
|
for (i=0; i<8; i++) {
|
|
if ((dfield >> i) & 1) {
|
|
uint32_t tmp;
|
|
if (s)
|
|
tmp = lget(shoe.dat, 4);
|
|
else
|
|
tmp = (uint32_t)((int32_t)((int16_t)lget(shoe.dat, 2))); // sign-extend if short-mode
|
|
if (shoe.abort) goto abort;
|
|
shoe.d[i] = tmp;
|
|
shoe.dat += sz;
|
|
}
|
|
}
|
|
|
|
// read in the address registers
|
|
for (i=0; i<8; i++) {
|
|
if ((afield >> i) & 1) {
|
|
uint32_t tmp;
|
|
if (s)
|
|
tmp = lget(shoe.dat, 4);
|
|
else
|
|
tmp = (uint32_t)((int32_t)((int16_t)lget(shoe.dat, 2))); // sign-extend if short-mode
|
|
if (shoe.abort) goto abort;
|
|
shoe.a[i] = tmp;
|
|
shoe.dat += sz;
|
|
}
|
|
}
|
|
|
|
// update register if postincrement
|
|
if (~bmatch(M, xx011xxx))
|
|
shoe.a[M&7] = shoe.dat;
|
|
}
|
|
else { // register->memory
|
|
uint32_t addr;
|
|
uint16_t newmask = mask; // if predecrement-mode, bit-reversed mask. Regular mask otherwise.
|
|
uint16_t numbits = 0; // the number of set bits in mask
|
|
|
|
if (~bmatch(M, xx100xxx)) { // if predecrement,
|
|
uint16_t maskcopy;
|
|
|
|
// slog("*0x%08x movem %02x\n", shoe.orig_pc, shoe.op);
|
|
|
|
for (maskcopy=mask, i=0; i < 16; i++) {
|
|
newmask = (newmask<<1) | (maskcopy & 1); // build a flipped version of mask
|
|
numbits += (maskcopy & 1); // count the bits in mask, to pre-predecrement the addr
|
|
maskcopy >>= 1;
|
|
}
|
|
addr = shoe.a[M&7] - numbits * sz; // hand-parse and pre-predecrement the addr
|
|
|
|
// XXX: This is broken - according to the documentation, 68020 will write (a7-sz)
|
|
// if it's written during pre-decrement mode.
|
|
// shoe.a[M&7] -= sz; // "pre-decrement" the address register itself (see abort:)
|
|
}
|
|
else {
|
|
call_ea_addr(M); // otherwise, ea_addr() can handle it
|
|
addr = shoe.dat;
|
|
}
|
|
|
|
const uint32_t addr_copy = addr;
|
|
const uint8_t dfield = newmask&0xff;
|
|
const uint8_t afield = (newmask>>8)&0xff;
|
|
|
|
// write the data registers
|
|
for (i=0; i<8; i++) {
|
|
// FIXME: determine what happens when the predecrementing address register is written
|
|
if ((dfield >> i) & 1) {
|
|
lset(addr, sz, shoe.d[i]);
|
|
if (shoe.abort)
|
|
goto abort; // FIXME: figure out how to abort cleanly
|
|
addr += sz;
|
|
}
|
|
}
|
|
|
|
// write the address registers
|
|
for (i=0; i<8; i++) {
|
|
// FIXME: determine what happens when the predecrementing address register is written
|
|
if ((afield >> i) & 1) {
|
|
|
|
// For the MC68020-40, for predecrement mode, if the address register
|
|
// is written to memory, it is "predecremented" by the size of the operation.
|
|
// (Literally the "size" of the operation, 2 or 4 bytes)
|
|
|
|
uint32_t data = shoe.a[i];
|
|
// if this is pre-dec mode, and we're pushing the address reg specified in the EA
|
|
if ( (M>>3) == 4 && (M&7)==i ) {
|
|
data -= sz;
|
|
slog("movem: For movem %u/%u, deciding to write 0x%08x for a%u\n", M>>3, M&7, data, M&7);
|
|
}
|
|
|
|
lset(addr, sz, data);
|
|
if (shoe.abort)
|
|
goto abort; // FIXME: figure out how to abort cleanly
|
|
addr += sz;
|
|
}
|
|
}
|
|
|
|
|
|
// update register if predecrement
|
|
if (~bmatch(M, xx100xxx))
|
|
shoe.a[M&7] = addr_copy;
|
|
}
|
|
|
|
return ;
|
|
|
|
abort:
|
|
// For the MC68020-40, for predecrement mode, if the address register is written to memory, it is "predecremented" by the size of the operation. (Literally, 2 or 4 bytes.)
|
|
// So undo that here:
|
|
|
|
// XXX: This is broken. We should only undo the predecrement if we DID predecrement
|
|
|
|
//if (~bmatch(M, xx100xxx))
|
|
//shoe.a[M&7] += sz;
|
|
|
|
return ;
|
|
}
|
|
|
|
static void inst_nbcd (void) {
|
|
assert(!"inst_nbcd: fixme: I'm not implemented!\n");
|
|
}
|
|
|
|
static void inst_eori_to_ccr (void) {
|
|
const uint16_t val = 0xff & nextword();
|
|
const uint16_t new_sr = shoe.sr ^ val;
|
|
|
|
set_sr(new_sr);
|
|
}
|
|
|
|
static void inst_eori_to_sr (void) {
|
|
verify_supervisor();
|
|
|
|
const uint16_t new_sr = shoe.sr ^ nextword();
|
|
|
|
set_sr(new_sr);
|
|
}
|
|
|
|
static void inst_movep (void) {
|
|
~decompose(shoe.op, 0000 ddd 1mm 001 aaa);
|
|
|
|
/*
|
|
* 68kprm thoughtfully doesn't clarify whether the
|
|
* displacement is sign-extended or not. (It is.)
|
|
*/
|
|
const int16_t disp = nextword();
|
|
const uint32_t addr = shoe.a[a] + disp;
|
|
|
|
switch (m) {
|
|
case 0: { // word, mem->reg
|
|
uint16_t val = lget(addr, 1);
|
|
if (shoe.abort) return;
|
|
|
|
val = (val << 8) | lget(addr + 2, 1);
|
|
if (shoe.abort) return;
|
|
|
|
set_d(d, val, 2);
|
|
break;
|
|
}
|
|
case 1: { // long, mem->reg
|
|
uint32_t val = lget(addr, 1);
|
|
if (shoe.abort) return;
|
|
|
|
val = (val << 8) | lget(addr + 2, 1);
|
|
if (shoe.abort) return;
|
|
|
|
val = (val << 8) | lget(addr + 4, 1);
|
|
if (shoe.abort) return;
|
|
|
|
val = (val << 8) | lget(addr + 6, 1);
|
|
if (shoe.abort) return;
|
|
|
|
shoe.d[d] = val;
|
|
break;
|
|
}
|
|
case 2: { // word, reg->mem
|
|
const uint16_t val = shoe.d[d];
|
|
lset(addr + 0, 1, (val >> 8) & 0xff);
|
|
if (shoe.abort) return;
|
|
|
|
lset(addr + 2, 1, (val >> 0) & 0xff);
|
|
if (shoe.abort) return;
|
|
|
|
break;
|
|
}
|
|
case 3: { // long, reg->mem
|
|
const uint32_t val = shoe.d[d];
|
|
lset(addr + 0, 1, (val >> 24) & 0xff);
|
|
if (shoe.abort) return;
|
|
|
|
lset(addr + 2, 1, (val >> 16) & 0xff);
|
|
if (shoe.abort) return;
|
|
|
|
lset(addr + 4, 1, (val >> 8) & 0xff);
|
|
if (shoe.abort) return;
|
|
|
|
lset(addr + 6, 1, (val >> 0) & 0xff);
|
|
if (shoe.abort) return;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void write_bitfield(const uint32_t width, const uint32_t offset, const uint32_t M, const uint32_t ea, const uint32_t raw_field)
|
|
{
|
|
const int32_t soffset = offset;
|
|
const uint32_t field = bitchop(raw_field, width); // make sure that field is all 0's beyond (width)
|
|
|
|
// special case for EA-mode == data register
|
|
if ((M>>3) == 0) {
|
|
const uint32_t reg = shoe.d[M&7]; // the EA reg
|
|
const uint32_t ones = 0xffffffff >> (32-width); // unrotated mask
|
|
if (soffset >= 0) {
|
|
const uint32_t mask = ~~(rrot32(ones << (32-width), soffset&31));
|
|
const uint32_t rotatedfield = rrot32(field << (32-width), soffset&31);
|
|
shoe.d[M&7] = (shoe.d[M&7] & mask) | rotatedfield;
|
|
}
|
|
else {
|
|
const uint32_t mask = ~~(lrot32(ones << (32-width), (-soffset)&31));
|
|
const uint32_t rotatedfield = lrot32(field << (32-width), (-soffset)&31);
|
|
shoe.d[M&7] = (shoe.d[M&7] & mask) | rotatedfield;
|
|
}
|
|
}
|
|
else {
|
|
const uint32_t bit_offset = offset & 7;
|
|
// byte_offset = offset (arithmetic-right-shift) 3
|
|
const uint32_t byte_offset = ((offset|(offset>>1)|(offset>>2)) & 0xe0000000) | (offset>>3);
|
|
|
|
// call_ea_addr(M);
|
|
|
|
const uint32_t first_byte_addr = ea + byte_offset;
|
|
//slog("debug: extract_bitfield: addr = 0x%08x, first_byte_addr = 0x%08x\n", ea, first_byte_addr);
|
|
|
|
|
|
// if the field is contained entirely within the first byte
|
|
if (width <= (8-bit_offset)) {
|
|
// yes this is convoluted, it simply creates a chopped/rotated mask that matches the bits in the first byte
|
|
const uint8_t byte_mask = bitchop(0xff, width) << (8-(bit_offset+width));
|
|
const uint8_t field_mask = field << (8-(bit_offset+width));
|
|
const uint8_t old_byte = lget(first_byte_addr, 1);
|
|
const uint8_t new_byte = (old_byte & (~~byte_mask)) | field_mask;
|
|
|
|
lset(first_byte_addr, 1, new_byte);
|
|
//slog("write_bitfield: byte_mask = 0x%02x field_mask = 0x%02x bit_offset=%u byte_offset=%u\n", byte_mask, field_mask, bit_offset, byte_offset);
|
|
//slog("write_bitfield: changing byte at 0x%08x from 0x%02x to 0x%02x\n",
|
|
//first_byte_addr, old_byte, new_byte);
|
|
if (shoe.abort) return ;
|
|
}
|
|
else {
|
|
uint32_t boff = bit_offset;
|
|
uint32_t curwidth = 8-bit_offset;
|
|
uint32_t remaining_width = width;
|
|
uint32_t addr = first_byte_addr;
|
|
uint32_t remaining_field = field<<(32-width); // left-aligned
|
|
while (remaining_width > 0) {
|
|
const uint8_t byte = lget(addr, 1);
|
|
if (shoe.abort) return ;
|
|
|
|
const uint8_t mask = ~~(((uint8_t)((0xff) << (8-curwidth))) >> boff);
|
|
const uint8_t field_chunk = remaining_field >> (32-curwidth);
|
|
const uint8_t rotated_chunk = (field_chunk << (8-curwidth)) >> boff;
|
|
|
|
// slog("mask = 0x%02x\nfield = 0x%02x\n", mask, rotated_chunk);
|
|
|
|
lset(addr, 1, (byte & mask) | rotated_chunk);
|
|
//slog("write_bitfield: changing byte at 0x%08x from 0x%02x to 0x%02x\n",
|
|
//addr, byte, (byte & mask) | rotated_chunk);
|
|
if (shoe.abort) return ;
|
|
|
|
addr++;
|
|
remaining_field <<= curwidth;
|
|
remaining_width -= curwidth;
|
|
curwidth = (remaining_width>8)? 8 : remaining_width;
|
|
boff = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t extract_bitfield(const uint32_t width, const uint32_t offset, const uint32_t M, const uint32_t ea)
|
|
{
|
|
const int32_t soffset = offset;
|
|
uint32_t field;
|
|
|
|
// special case for EA-mode == data register
|
|
if ((M>>3) == 0) {
|
|
const uint32_t reg = shoe.d[M&7]; // the EA reg
|
|
|
|
// OKAY: I think I have this figured out. It was laborious to discover, so don't forget it.
|
|
// When mode=datareg, the register is ROTATED by the offset, not shifted.
|
|
// This has nonintuitive consequences.
|
|
// {Offset=-27, length=32, data=1} will say a bit is set at the (-27+5)th position
|
|
|
|
if (soffset >= 0)
|
|
field = lrot32(reg, soffset&31) >> (32-width);
|
|
else
|
|
field = rrot32(reg, (-soffset)&31) >> (32-width);
|
|
}
|
|
else {
|
|
const uint32_t bit_offset = offset & 7;
|
|
// byte_offset = offset (arithmetic-right-shift) 3
|
|
const uint32_t byte_offset = ((offset|(offset>>1)|(offset>>2)) & 0xe0000000) | (offset>>3);
|
|
|
|
// this is call_ea_addr(M);
|
|
// shoe.mr=M;
|
|
// ea_addr();
|
|
// if (shoe.abort) return 0;
|
|
|
|
//slog("debug: extract_bitfield: offset = 0x%08x, byte_offset = %d, bit_offset = %d\n", offset, byte_offset, bit_offset);
|
|
|
|
const uint32_t first_byte_addr = ea + byte_offset;
|
|
//slog("debug: extract_bitfield: addr = 0x%08x, first_byte_addr = 0x%08x\n", ea, first_byte_addr);
|
|
|
|
field = bitchop(lget(first_byte_addr, 1), 8-bit_offset);
|
|
//slog("debug: extract_bitfield: first byte field (low %u bits): 0x%02x\n", 8-bit_offset, field);
|
|
if (shoe.abort) return 0;
|
|
if (width > (8-bit_offset)) { // if the data isn't entirely contained in the first byte
|
|
uint32_t last_long = lget(first_byte_addr+1, 4);
|
|
if (shoe.abort) return 0;
|
|
field = (field<<(width - (8-bit_offset))) | // first_byte, left shifted
|
|
(last_long >> (32 - (width - (8-bit_offset)))); // last_long, right shifted
|
|
}
|
|
else // otherwise, right-align the first byte
|
|
field >>= (8-(bit_offset+width));
|
|
}
|
|
|
|
return field;
|
|
}
|
|
|
|
static void inst_bfextu (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1001 11 MMMMMM);
|
|
~decompose(ext, 0 rrr Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
const uint32_t field = extract_bitfield(width, offset, M, ea);
|
|
if (shoe.abort) return;
|
|
|
|
shoe.d[r] = field;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(field >> (width-1));
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_bfchg (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1011 11 MMMMMM);
|
|
~decompose(ext, 0 000 Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
const uint32_t field = extract_bitfield(width, offset, M, ea);
|
|
if (shoe.abort) return;
|
|
write_bitfield(width, offset, M, ea, ~~field);
|
|
if (shoe.abort) return;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(field >> (width-1));
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_bfexts (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1011 11 MMMMMM);
|
|
~decompose(ext, 0 rrr Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
const uint32_t field = extract_bitfield(width, offset, M, ea);
|
|
if (shoe.abort) return;
|
|
const uint32_t mib = (field >> (width-1))&1;
|
|
|
|
const uint32_t maskA = (((uint32_t)0) - mib) << 1;
|
|
const uint32_t maskB = (maskA << (width-1));
|
|
const uint32_t result = maskB | field;
|
|
shoe.d[r] = result;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(mib);
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_bfclr (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1011 11 MMMMMM);
|
|
~decompose(ext, 0 000 Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
const uint32_t field = extract_bitfield(width, offset, M, ea);
|
|
if (shoe.abort) return;
|
|
write_bitfield(width, offset, M, ea, 0);
|
|
if (shoe.abort) return;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(field >> (width-1));
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_bfset (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1011 11 MMMMMM);
|
|
~decompose(ext, 0 000 Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
const uint32_t field = extract_bitfield(width, offset, M, ea);
|
|
if (shoe.abort) return;
|
|
write_bitfield(width, offset, M, ea, 0xffffffff);
|
|
if (shoe.abort) return;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(field >> (width-1));
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_bftst (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1000 11 MMMMMM);
|
|
~decompose(ext, 0 000 Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
const uint32_t field = extract_bitfield(width, offset, M, ea);
|
|
if (shoe.abort) return;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(field >> (width-1));
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_bfffo (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1110 11 MMMMMM);
|
|
~decompose(ext, 0 rrr Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
const uint32_t field = extract_bitfield(width, offset, M, ea);
|
|
if (shoe.abort) return;
|
|
|
|
uint32_t i;
|
|
for (i=1; (i<=width) && ((field>>(width-i))&1)==0; i++) ;
|
|
shoe.d[r] = offset+i-1;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(field >> (width-1));
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_bfins (void) {
|
|
const uint16_t ext = nextword();
|
|
~decompose(shoe.op, 1110 1011 11 MMMMMM);
|
|
~decompose(ext, 0 rrr Ffffff Wwwwww);
|
|
|
|
const uint32_t width_ua = W ? (shoe.d[w]%32) : w; // unadjusted, [0, 31]
|
|
const uint32_t width = (width_ua==0)?32:width_ua; // width_ua==0 => width==32 [1, 32]
|
|
const uint32_t offset = F ? (shoe.d[f]) : f; // [0, 31]
|
|
|
|
const uint32_t field = bitchop(shoe.d[r], width);
|
|
// slog("write_bitfield: writing %u at offset=%u width=%u\n", field, offset, width);
|
|
|
|
uint32_t ea = 0;
|
|
if (M >> 3) { // If ea isn't data reg mode (handled separately in *_bitfield())
|
|
call_ea_addr(M);
|
|
ea = shoe.dat;
|
|
}
|
|
|
|
write_bitfield(width, offset, M, ea, field);
|
|
if (shoe.abort) return;
|
|
|
|
set_sr_c(0);
|
|
set_sr_v(0);
|
|
set_sr_n(field >> (width-1)); // set according to the inserted value
|
|
set_sr_z(field == 0);
|
|
}
|
|
|
|
static void inst_btst_reg (void) {
|
|
~decompose(shoe.op, 0000 rrr 100 MMMMMM);
|
|
|
|
if ((M>>3)==0) { // if the dest is a data reg, handle specially
|
|
set_sr_z(! ((shoe.d[M&7]>>(shoe.d[r]%32)) & 1) );
|
|
return ;
|
|
}
|
|
|
|
call_ea_read(M, 1);
|
|
call_ea_read_commit(M, 1);
|
|
set_sr_z(! ((shoe.dat >> (shoe.d[r] % 8)) & 1));
|
|
}
|
|
|
|
static void inst_bchg_reg (void) {
|
|
~decompose(shoe.op, 0000 rrr 101 MMMMMM);
|
|
|
|
// The LSB bit is idx:0, MSB is idx:7 or :31
|
|
|
|
if ((M>>3)==0) { // if the dest is a data reg, handle specially
|
|
set_sr_z((shoe.d[M&7] & (1 << (shoe.d[r]%32))) == 0);
|
|
|
|
shoe.d[M&7] ^= (1 << (shoe.d[r]%32));
|
|
|
|
return ;
|
|
}
|
|
|
|
call_ea_read(M, 1);
|
|
|
|
const uint32_t z = ((shoe.d[r] & (1 << (shoe.d[r] % 8))) == 0);
|
|
|
|
// shoe.dat ^= (0x80 >> (shoe.d[r] % 8)); // NO NO! BAD
|
|
shoe.dat ^= (1 << (shoe.d[r] % 8));
|
|
|
|
call_ea_write(M, 1);
|
|
|
|
set_sr_z(z);
|
|
}
|
|
|
|
static void inst_bclr_reg (void) {
|
|
~decompose(shoe.op, 0000 rrr 111 MMMMMM);
|
|
|
|
const uint8_t is_data_reg = (M>>3) == 0;
|
|
const uint8_t sz = is_data_reg ? 4 : 1;
|
|
const uint8_t shift = shoe.d[r] % (is_data_reg ? 32 : 8);
|
|
|
|
call_ea_read(M, sz);
|
|
|
|
set_sr_z(((shoe.dat >> shift) & 1) == 0);
|
|
|
|
shoe.dat &= ~~(1 << shift);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
static void inst_bclr_immediate (void) {
|
|
~decompose(shoe.op, 0000 1000 11 MMMMMM);
|
|
const uint16_t ext = nextword();
|
|
~decompose(ext, 00000000 bbbbbbbb);
|
|
|
|
const uint8_t is_data_reg = (M>>3)==0;
|
|
const uint8_t sz = is_data_reg ? 4 : 1;
|
|
const uint8_t shift = b % (is_data_reg ? 32 : 8);
|
|
|
|
call_ea_read(M, sz);
|
|
|
|
set_sr_z(((shoe.dat >> shift) & 1) == 0);
|
|
|
|
shoe.dat &= ~~(1 << shift);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
static void inst_bset_reg (void) {
|
|
~decompose(shoe.op, 0000 rrr 111 MMMMMM);
|
|
|
|
const uint8_t is_data_reg = (M>>3)==0;
|
|
const uint8_t sz = is_data_reg ? 4 : 1;
|
|
const uint8_t shift = shoe.d[r] % (is_data_reg ? 32 : 8);
|
|
|
|
call_ea_read(M, sz);
|
|
|
|
set_sr_z(((shoe.dat >> shift) & 1) == 0);
|
|
|
|
shoe.dat |= (1 << shift);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
static void inst_bset_immediate (void) {
|
|
~decompose(shoe.op, 0000 1000 11 MMMMMM);
|
|
const uint16_t ext = nextword();
|
|
~decompose(ext, 00000000 bbbbbbbb);
|
|
|
|
const uint8_t is_data_reg = (M>>3)==0;
|
|
const uint8_t sz = is_data_reg ? 4 : 1;
|
|
const uint8_t shift = b % (is_data_reg ? 32 : 8);
|
|
|
|
call_ea_read(M, sz);
|
|
|
|
set_sr_z(((shoe.dat >> shift) & 1) == 0);
|
|
|
|
shoe.dat |= (1 << shift);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
static void inst_bchg_immediate (void) {
|
|
~decompose(shoe.op, 0000 1000 01 MMMMMM);
|
|
const uint16_t ext = nextword();
|
|
~decompose(ext, 00000000 bbbbbbbb);
|
|
|
|
const uint8_t is_data_reg = (M>>3)==0;
|
|
const uint8_t sz = is_data_reg ? 4 : 1;
|
|
const uint8_t shift = b % (is_data_reg ? 32 : 8);
|
|
|
|
call_ea_read(M, sz);
|
|
|
|
set_sr_z(((shoe.dat >> shift) & 1) == 0);
|
|
|
|
shoe.dat ^= (1 << shift);
|
|
call_ea_write(M, sz);
|
|
}
|
|
|
|
static void inst_ext (void) {
|
|
~decompose(shoe.op, 0100 100 ooo 000 rrr);
|
|
switch (o) {
|
|
case ~b(010): { // byte -> word
|
|
uint16_t val = (int8_t)get_d(r, 1);
|
|
set_d(r, val, 2);
|
|
break;
|
|
} case ~b(011): { // word -> long
|
|
uint32_t val = (int16_t)get_d(r, 2);
|
|
set_d(r, val, 4);
|
|
break;
|
|
} case ~b(111): { // byte -> long
|
|
uint32_t val = (int8_t)get_d(r, 1);
|
|
set_d(r, val, 4);
|
|
break;
|
|
}
|
|
}
|
|
set_sr_v(0);
|
|
set_sr_c(0);
|
|
// if the LSb of o is 1, then result size == long
|
|
set_sr_z(get_d(r, 2+2*(o&1))==0);
|
|
set_sr_n(mib(shoe.d[r], 2+2*(o&1)));
|
|
}
|
|
|
|
static void inst_andi_to_sr (void) {
|
|
verify_supervisor();
|
|
const uint16_t ext = nextword();
|
|
|
|
set_sr(shoe.sr & ext);
|
|
}
|
|
|
|
static void inst_andi_to_ccr (void) {
|
|
const uint16_t ext = nextword();
|
|
|
|
set_sr(shoe.sr & (ext & 0xff));
|
|
}
|
|
|
|
static void inst_ori_to_sr (void) {
|
|
verify_supervisor();
|
|
const uint16_t ext = nextword();
|
|
|
|
set_sr(shoe.sr | ext);
|
|
}
|
|
|
|
static void inst_ori_to_ccr (void) {
|
|
const uint16_t ext = nextword();
|
|
|
|
set_sr(shoe.sr | (ext & 0xff));
|
|
}
|
|
|
|
static void inst_mc68851_decode (void) {
|
|
~decompose(shoe.op, 1111 000 a b c MMMMMM);
|
|
|
|
// prestore or psave
|
|
if (a) {
|
|
if (~bmatch(shoe.op, 1111 000 101 xxxxxx))
|
|
inst_mc68851_prestore();
|
|
else if (~bmatch(shoe.op, 1111 000 100 xxxxxx))
|
|
inst_mc68851_psave();
|
|
else
|
|
throw_illegal_instruction();
|
|
return ;
|
|
}
|
|
|
|
// pbcc
|
|
if (~bmatch(shoe.op, 1111 000 01x 00xxxx)) {
|
|
inst_mc68851_pbcc();
|
|
return ;
|
|
}
|
|
|
|
const uint16_t ext = nextword();
|
|
|
|
// pdbcc, ptrapcc, pscc
|
|
if (~bmatch(shoe.op, 1111 000 001 xxxxxx)) {
|
|
~decompose(shoe.op, 1111 000 001 mmm rrr);
|
|
// These all just store a condition code in the extension word
|
|
~decompose(ext, 0000 0000 00 cccccc);
|
|
|
|
if (m == 1)
|
|
inst_mc68851_pdbcc(c);
|
|
else if ((m == ~b(111)) && (r > 2))
|
|
inst_mc68851_ptrapcc(c);
|
|
else
|
|
inst_mc68851_pscc(c);
|
|
return ;
|
|
}
|
|
|
|
// shoe.op must have the form (1111 000 000 xxxxxx) now
|
|
~decompose(ext, XXX YYY 00 0000 0000);
|
|
switch (X) {
|
|
case 1: // pflush, pload, pvalid
|
|
if (Y == ~b(000))
|
|
inst_mc68851_pload(ext);
|
|
else if ((Y == ~b(010)) || (Y == ~b(011)))
|
|
inst_mc68851_pvalid(ext);
|
|
else
|
|
inst_mc68851_pflush(ext);
|
|
return ;
|
|
case 2: // pmove format 1
|
|
inst_mc68851_pmove(ext);
|
|
return ;
|
|
case 3: // pmove formats 2 and 3,
|
|
inst_mc68851_pmove(ext);
|
|
return ;
|
|
case 4: // ptest
|
|
inst_mc68851_ptest(ext);
|
|
return ;
|
|
case 5: // pflushr
|
|
inst_mc68851_pflushr(ext);
|
|
return ;
|
|
default:
|
|
throw_illegal_instruction();
|
|
return ;
|
|
}
|
|
assert(!"never get here");
|
|
}
|
|
|
|
static void inst_unknown (void) {
|
|
slog("Unknown instruction (0x%04x)!\n", shoe.op);
|
|
/*if (shoe.op == 0x33fe) {
|
|
dump_ring();
|
|
assert(!"dumped");
|
|
}*/
|
|
throw_illegal_instruction();
|
|
}
|
|
|
|
static void inst_a_line (void) {
|
|
|
|
/*if (shoe.op == 0xA9EB || shoe.op == 0xA9EC) {
|
|
shoe.suppress_exceptions = 1;
|
|
|
|
uint32_t fp_op = lget(shoe.a[7]+0, 2);
|
|
uint32_t fp_operand_addr = lget(shoe.a[7]+2, 4);
|
|
|
|
slog("%s : op 0x%04x (", (shoe.op == 0xA9EB) ? "FP68K" : "Elems68K", fp_op);
|
|
|
|
uint8_t buf[10];
|
|
uint32_t i;
|
|
for (i=0; i<10; i++) {
|
|
buf[i] = lget(fp_operand_addr+i, 1);
|
|
slog("%02x ", buf[i]);
|
|
}
|
|
|
|
{
|
|
uint8_t castable[12] = {
|
|
buf[9], buf[8], buf[7], buf[6], buf[5],
|
|
buf[4], buf[3], buf[2], buf[1], buf[0],
|
|
0, 0
|
|
};
|
|
slog("%Lf)\n", *(long double*)&castable[0]);
|
|
}
|
|
|
|
|
|
|
|
shoe.abort = 0;
|
|
shoe.suppress_exceptions = 0;
|
|
}*/
|
|
|
|
throw_illegal_instruction();
|
|
}
|
|
|
|
void trap_debug()
|
|
{
|
|
shoe.suppress_exceptions = 1;
|
|
|
|
const uint32_t syscall = lget(shoe.a[7]+4, 4);
|
|
slog("syscall = %u\n", shoe.d[0]);
|
|
|
|
|
|
switch (shoe.d[0]) {
|
|
case 4: {
|
|
uint32_t fd = lget(shoe.a[7]+4, 4);
|
|
uint32_t buf = lget(shoe.a[7]+8, 4);
|
|
uint32_t len = lget(shoe.a[7]+12, 4);
|
|
slog("write(%u, 0x%08x, %u) \"", fd, buf, len);
|
|
|
|
uint32_t i;
|
|
for (i=0; i<len; i++) {
|
|
slog("%c", (uint32_t)lget(buf+i, 1));
|
|
}
|
|
slog("\"\n");
|
|
|
|
break;
|
|
}
|
|
/*case 5: {
|
|
uint32_t path_p = lget(shoe.a[7]+4, 4);
|
|
uint32_t oflag = lget(shoe.a[7]+8, 4);
|
|
|
|
slog("shoe.orig_pc = 0x%08x\n", shoe.orig_pc);
|
|
slog("open(0x%08x, %u) \"", path_p, oflag);
|
|
uint32_t i;
|
|
for (i=0; 1; i++) {
|
|
uint8_t c = lget(path_p+i, 1);
|
|
if (c == 0) break;
|
|
slog("%c", c);
|
|
}
|
|
slog("\"\n");
|
|
break;
|
|
}*/
|
|
case 59: // exece
|
|
case 11: { // exec
|
|
char name[64];
|
|
uint32_t path_p = lget(shoe.a[7]+4, 4);
|
|
slog("exec(0x%08x, ...) \"", path_p);
|
|
uint32_t i;
|
|
for (i=0; i < 64; i++) {
|
|
name[i] = lget(path_p+i, 1);
|
|
if (name[i] == 0) break;
|
|
}
|
|
name[63] = 0;
|
|
slog("%s\"\n", name);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
shoe.abort = 0;
|
|
shoe.suppress_exceptions = 0;
|
|
}
|
|
|
|
static void inst_trap (void) {
|
|
~decompose(shoe.op, 0100 1110 0100 vvvv);
|
|
|
|
const uint32_t vector_num = 32 + v;
|
|
const uint32_t vector_offset = vector_num * 4;
|
|
|
|
// trap_debug();
|
|
|
|
set_sr_s(1);
|
|
|
|
push_a7(vector_offset, 2);
|
|
if (shoe.abort) goto fail;
|
|
push_a7(shoe.pc, 4);
|
|
if (shoe.abort) goto fail;
|
|
push_a7(shoe.orig_sr, 2);
|
|
if (shoe.abort) goto fail;
|
|
|
|
const uint32_t newpc = lget(shoe.vbr + vector_offset, 4);
|
|
if (shoe.abort) goto fail;
|
|
|
|
shoe.pc = newpc;
|
|
return ;
|
|
|
|
fail:
|
|
assert(!"trap - push_a7 raised shoe.abort\n"); // I can't handle this yet
|
|
}
|
|
|
|
|
|
#include "inst_decoder_guts.c"
|
|
|
|
|
|
void cpu_step()
|
|
{
|
|
// remember the PC and SR (so we can throw exceptions later)
|
|
shoe.orig_pc = shoe.pc;
|
|
shoe.orig_sr = shoe.sr;
|
|
|
|
// Is this an odd address? Throw an address exception!
|
|
if (shoe.pc & 1) {
|
|
// throw_address_error(shoe.pc, 0);
|
|
// I'm leaving this assert in here for now because it almost always indicates a bug in the emulator when it fires
|
|
assert(!"odd PC address (probably a bug)");
|
|
return ;
|
|
}
|
|
|
|
// Fetch the next instruction word
|
|
shoe.op = lget(shoe.pc, 2);
|
|
|
|
// If there was an exception, then the pc changed. Restart execution from the beginning.
|
|
if (shoe.abort) {
|
|
shoe.abort = 0;
|
|
return ;
|
|
}
|
|
shoe.pc+=2;
|
|
|
|
inst_instruction_to_pointer[inst_opcode_map[shoe.op]]();
|
|
|
|
/* The abort flag indicates that a routine should stop trying to execute the
|
|
instruction and return immediately to cpu_step(), usually to begin
|
|
exception processing */
|
|
|
|
shoe.abort = 0; // clear the abort flag
|
|
return ;
|
|
}
|
|
|