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:
parent
3bc6aed00a
commit
36fdd1a6d6
300
core/cpu.c
300
core/cpu.c
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
62
core/sound.c
62
core/sound.c
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue