Fixed/implemented roxx_mem, rox_mem, abcd, pack, unpk, cas2

Trying to finish up the last few unimplemented 68020 insts
This commit is contained in:
Peter Rutenbar 2015-08-31 01:57:37 -04:00
parent 3bc6aed00a
commit 36fdd1a6d6
3 changed files with 289 additions and 77 deletions

View File

@ -305,7 +305,30 @@ static void inst_roxx_reg (void) {
}
static void inst_roxx_mem (void) {
assert(!"roxx_mem: unimplemented\n");
~decompose(shoe.op, 1110 010 d 11 MMMMMM);
call_ea_read(M, 2);
const uint16_t dat = shoe.dat;
const uint16_t x = sr_x();
uint16_t b;
if (!d) { // right
b = dat & 1;
shoe.dat = (dat >> 1) | (x << 15);
}
else { // left
b = dat >> 15;
shoe.dat = (dat << 1) | x;
}
set_sr_x(b);
set_sr_v(0);
set_sr_c(b);
set_sr_n(shoe.dat & 0x8000);
set_sr_z(dat == 0);
call_ea_write(M, 2);
}
static void inst_rox_reg (void) {
@ -346,7 +369,28 @@ static void inst_rox_reg (void) {
}
static void inst_rox_mem (void) {
assert(!"rox_mem: unimplemented\n");
~decompose(shoe.op, 1110 011 d 11 MMMMMM);
call_ea_read(M, 2);
const uint16_t dat = shoe.dat;
uint16_t b;
if (!d) { // right
b = dat & 1;
shoe.dat = (dat >> 1) | (dat << 15);
}
else { // left
b = dat >> 15;
shoe.dat = (dat << 1) | (dat >> 15);
}
set_sr_v(0);
set_sr_c(b);
set_sr_n(shoe.dat & 0x8000);
set_sr_z(dat == 0);
call_ea_write(M, 2);
}
static void inst_sbcd (void) {
@ -354,11 +398,57 @@ static void inst_sbcd (void) {
}
static void inst_pack (void) {
assert(!"Hey! inst_pack isn't implemented!\n");
~decompose(shoe.op, 1000 yyy 10100 r xxx);
const uint16_t adj = nextword();
if (!r) { // register mode
const uint16_t unpacked = (adj + (shoe.d[x] & 0xffff)) & 0x0f0f;
const uint8_t packed = unpacked | (unpacked >> 4);
set_d(y, packed, 1);
}
else { // predecrement mode
const uint16_t unpacked = (adj + lget(shoe.a[x] - 2, 2)) & 0x0f0f;
if sunlikely(shoe.abort) return ;
const uint8_t packed = unpacked | (unpacked >> 4);
uint32_t dest_addr = shoe.a[y] - (y == 7 ? 2 : 1); // regular rules apply to byte-size predec/postinc on a7
if (x == y)
dest_addr -= 2; // decrements are cumulative when x==y
lset(dest_addr, 1, packed);
if sunlikely(shoe.abort) return ;
shoe.a[x] -= 2;
shoe.a[y] = dest_addr;
}
}
static void inst_unpk (void) {
assert(!"Hey! inst_unpk isn't implemented!\n");
~decompose(shoe.op, 1000 yyy 11000 r xxx);
const uint16_t adj = nextword();
if (!r) { // register mode
const uint16_t packed = shoe.d[x] & 0xff;
const uint16_t unpacked = ((packed & 0x0f) | ((packed & 0xf0) << 4)) + adj;
set_d(y, unpacked, 2);
}
else { // predecrement mode
// regular rules apply to byte-size predec/postinc on a7
const uint32_t src_addr = shoe.a[x] - (y == 7 ? 2 : 1);
// decrements are cumulative when x==y
const uint32_t dest_addr = (x == y) ? src_addr - 2 : shoe.a[y] - 2;
const uint16_t packed = lget(src_addr, 1);
if sunlikely(shoe.abort) return ;
const uint16_t unpacked = ((packed & 0x0f) | ((packed & 0xf0) << 4)) + adj;
lset(dest_addr, 2, unpacked);
if sunlikely(shoe.abort) return ;
shoe.a[x] = src_addr;
shoe.a[y] = dest_addr;
}
}
static void inst_divu (void) {
@ -428,7 +518,9 @@ static void inst_divs (void) {
}
static void inst_bkpt (void) {
assert(!"Hey! inst_bkpt isn't implemented!\n");
~decompose(shoe.op, 0100 1000 0100 1 vvv);
slog("bkpt: vector %u\n", v);
throw_illegal_instruction();
}
static void inst_swap (void) {
@ -440,56 +532,74 @@ static void inst_swap (void) {
set_sr_n(mib(shoe.d[r], 4));
}
/*
* This matches 68020's abcd implementation for every possible a, b, and X-bit
* Given packed BCD a and b, returns a + b + X as packed BCD
*
* (e.g. X=1: abcd f7,ff -> 5d,
* X=0: abcd 05,05 -> 10)
*/
static uint8_t _abcd_core(const uint8_t a, const uint8_t b)
{
uint16_t sum = sr_x();
// First add the low digits
const uint8_t al = a & 0xf;
const uint8_t bl = b & 0xf;
sum += al + bl;
if (sum >= 10) {
// If al+bl > 10, carry the 1.
sum += 0x10;
sum -= 10;
}
// Then add the high digits
const uint16_t ah = a & 0xf0;
const uint16_t bh = b & 0xf0;
sum += ah + bh;
if (sum >= (10 << 4)) {
sum += 0x100;
sum -= (10 << 4);
}
// sr_n is undefined, but apparently unmodified on 68020
// same goes for sr_v
set_sr_x(sum >= 0x100);
set_sr_c(sum >= 0x100);
if (sum & 0xff)
set_sr_z(0);
return sum & 0xff;
}
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 sunlikely(shoe.abort) return ;
packed_y = lget(shoe.a[y], 1);
if sunlikely(shoe.abort) return ;
if (!m) {
// Register -> register
const uint8_t sum = _abcd_core((uint8_t)shoe.d[x], (uint8_t)shoe.d[y]);
set_d(x, sum, 1);
}
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);
// Memory -> memory
// Usual rules apply for byte-size if x or y is a7
const uint32_t source_addr = shoe.a[y] - (y == 7 ? 2 : 1);
// The decrements are cumulative if x==y
const uint32_t dest_addr = (x == y ? source_addr : shoe.a[x]) - (x == 7 ? 2 : 1);
const uint8_t a = lget(source_addr, 1);
if sunlikely(shoe.abort) return ;
shoe.a[x]--;
if (x != y)
shoe.a[y]--;
const uint8_t b = lget(dest_addr, 1);
if sunlikely(shoe.abort) return ;
const uint8_t sum = _abcd_core(a, b);
lset(dest_addr, 1, sum);
if sunlikely(shoe.abort) return ;
shoe.a[y] = source_addr;
shoe.a[x] = dest_addr;
}
else {
set_d(x, packed_sum, 1);
}
}
static void inst_muls (void) {
@ -1363,6 +1473,23 @@ static void inst_moves (void) {
}
static void _cas_comparator(uint32_t source, uint32_t dest, uint8_t sz)
{
const uint32_t chopped_dest = chop(dest, sz);
const uint32_t chopped_source = chop(source, sz);
const uint32_t R = chopped_dest - chopped_source;
const _Bool Sm = mib(chopped_source, sz);
const _Bool Dm = mib(chopped_dest, sz);
const _Bool Rm = mib(R, sz);
const _Bool z = (chop(R, sz) == 0);
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_cas (void) {
const uint16_t ext = nextword();
~decompose(shoe.op, 0000 1ss0 11 MMMMMM);
@ -1377,14 +1504,9 @@ static void inst_cas (void) {
// 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);
_cas_comparator(get_d(c, sz), (uint32_t)shoe.dat, sz);
if (z) {
if (sr_z()) {
// "If the operands are equal, the instruction writes the
// update operand (Du) to the effective address operand"
shoe.dat = get_d(u, sz);
@ -1396,15 +1518,73 @@ static void inst_cas (void) {
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!");
~decompose(shoe.op, 0000 1 1s 0 1111 1100);
const uint16_t r1 = nextword();
const uint16_t r2 = nextword();
~decompose(r1, d nnn 000 uuu 000 ccc);
~decompose(r2, D NNN 000 UUU 000 CCC);
const uint8_t sz = 2 << s;
const uint32_t memory_op_addr_1 = d ? shoe.a[n] : shoe.d[n];
const uint32_t memory_op_addr_2 = D ? shoe.a[N] : shoe.d[N];
const uint32_t compare_op_1 = get_d(c, sz);
const uint32_t compare_op_2 = get_d(C, sz);
const uint32_t update_op_1 = get_d(u, sz);
const uint32_t update_op_2 = get_d(U, sz);
uint32_t memory_op_data_1, memory_op_data_2;
_Bool equal = 0;
// -- If we ever support SMP, synchronize and block the other CPUs here --
memory_op_data_1 = lget(memory_op_addr_1, sz);
if sunlikely(shoe.abort) goto unblock ;
memory_op_data_2 = lget(memory_op_addr_2, sz);
if sunlikely(shoe.abort) goto unblock ;
// Compare operand pair 1, and if it's equal, compare operand pair 2
_cas_comparator(compare_op_1, memory_op_data_1, sz);
if (sr_z()) {
_cas_comparator(compare_op_2, memory_op_data_2, sz);
equal = sr_z();
if (equal) {
// Both comparison pairs match, write the update operands
lset(memory_op_addr_1, sz, update_op_1);
if sunlikely(shoe.abort) goto unblock ;
lset(memory_op_addr_2, sz, update_op_2);
}
}
// Else, one of the comparisons failed, we can unblock now
unblock:
// -- unblock CPUs here --
if sunlikely(shoe.abort) return ;
if (!equal) {
/*
* 68040 note:
* 68kprm says (I think?) that memory_op_data_1 is written
* back to memory_op_addr_1 as part of the locked access cycle
* Remember to do that if we ever support 68040 cores.
*/
// Copy memory_op_data_1/2 to d[c] and d[C]
// (if c==C, that register gets memory_op_data_1)
set_d(C, memory_op_data_2, sz);
set_d(c, memory_op_data_1, sz);
}
return ;
}
static void inst_move_to_sr (void) {

View File

@ -422,7 +422,7 @@ void sound_dma_write_raw(uint16_t addr, uint8_t sz, uint32_t data);
uint32_t sound_dma_read_raw(uint16_t addr, uint8_t sz);
void init_asc_state(void);
typedef struct __attribute__ ((__packed__)) {
typedef struct {
uint8_t buf[0x800];
uint8_t version; // read-only
uint8_t asc_mode;
@ -432,7 +432,7 @@ typedef struct __attribute__ ((__packed__)) {
uint8_t unknown1;
uint8_t volume_ctrl;
uint8_t clock_ctrl;
uint8_t unknown2[8];
uint16_t left_ptr, right_ptr;
} apple_sound_chip_registers_t;

View File

@ -23,10 +23,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Props to MESS for their EASC register map: http://www.mess.org/mess/driver_info/easc
* EASC's PCM mode is backwards compatible with ASC, so their registers are very similar.
*/
#include <assert.h>
#include <string.h>
#include "../core/shoebill.h"
#define r_ptr (0x400 + (asc->right_ptr & 0x3ff)) /* right buf ptr */
#define l_ptr (asc->left_ptr & 0x3ff) /* left buf ptr */
#define m_ptr (asc->left_ptr & 0x7ff) /* mono buf ptr */
void init_asc_state(void)
{
memset(&shoe.asc, 0, sizeof(shoe.asc));
@ -40,42 +49,48 @@ static uint8_t sound_dma_read_byte(uint16_t addr)
apple_sound_chip_registers_t *asc = &shoe.asc;
if (addr < 0x800) {
if (asc->asc_mode == 1) {
// PCM mode (FIFO is append-only)
if (asc->channel_ctrl & 2) {
// stereo mode - return the byte referenced by the right pointer
return asc->buf[r_ptr];
}
else {
// mono mode - return the byte referenced by the left pointer
return asc->buf[m_ptr];
}
}
else {
// Off or wavetable mode (FIFO is random access)
return asc->buf[addr];
}
}
switch (addr) {
case 0x800: // Version
return asc->version;
break;
case 0x801: // ASC-mode
return asc->asc_mode;
break;
case 0x802: // Channel control
return asc->channel_ctrl;
break;
case 0x803: // FIFO control
return asc->fifo_ctrl;
break;
case 0x804: // FIFO interrupt
//return asc->fifo_intr;
return 0xff;
break;
case 0x805: // Unknown (??)
return asc->unknown1;
break;
case 0x806: // Volume control
return asc->volume_ctrl;
break;
case 0x807: // Clock control
return asc->clock_ctrl;
break;
}
return 0;
@ -90,15 +105,30 @@ static void sound_dma_write_byte(uint16_t addr, uint8_t data)
// PCM mode (FIFO is append-only)
if (asc->channel_ctrl & 2) {
// stereo mode - each channel has its own buffer pointer
if (addr < 0x400) {
// left buffer
asc->buf[l_ptr] = data;
asc->left_ptr++;
}
else {
// right buffer
asc->buf[r_ptr] = data;
asc->right_ptr++;
}
return ;
}
else {
// mono mode
// Mono mode
asc->buf[m_ptr] = data;
asc->left_ptr++;
return ;
}
}
else {
// Off or wavetable mode (FIFO is random access)
asc->buf[asc->left_ptr] = data;
asc->left_ptr = (asc->left_ptr + 1) & 0x7ff;
asc->buf[addr] = data;
return ;
}
}
else {
@ -199,13 +229,15 @@ ASC notes:
0x800 - Version (?) MacII only ever seems to TST.B it
(READ ONLY)
$00 -> My Mac II's ASC's version. According to the Mac II rom, there seem to be other masks with non-0x00 versions.
$00 -> My Mac II's ASC's version
$b0 -> something more advanced than regular ASC (something with registers in the $fxx range)
$?? -> Mac II's ROM is aware of other non-$00 ASC masks
$b0 -> EASC
0x801 - Mode (?) 0 == no output, 1 == PCM, 2 == wavetable
0x801 - Mode (?) 0 == no output, 1 == PCM, 2 == wavetable, 3 == hissing static(??)
- Preserves low 2 bits, ignores high 6 bits
0x802 - Channel selector