mame/src/devices/cpu/tms57002/tms57002.cpp

918 lines
17 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/***************************************************************************
tms57002.c
TMS57002 "DASP" emulator.
***************************************************************************/
#include "emu.h"
#include "debugger.h"
#include "tms57002.h"
const device_type TMS57002 = &device_creator<tms57002_device>;
// Can't use a DEVICE_ADDRESS_MAP, not yet anyway
static ADDRESS_MAP_START(internal_pgm, AS_PROGRAM, 32, tms57002_device)
AM_RANGE(0x00, 0xff) AM_RAM
ADDRESS_MAP_END
tms57002_device::tms57002_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: cpu_device(mconfig, TMS57002, "TMS57002", tag, owner, clock, "tms57002", __FILE__),
device_sound_interface(mconfig, *this), macc(0), st0(0), st1(0), sti(0),
txrd(0),
program_config("program", ENDIANNESS_LITTLE, 32, 8, -2, ADDRESS_MAP_NAME(internal_pgm)),
data_config("data", ENDIANNESS_LITTLE, 8, 20)
{
}
WRITE_LINE_MEMBER(tms57002_device::pload_w)
{
UINT8 olds = sti;
if(state)
sti &= ~IN_PLOAD;
else
sti |= IN_PLOAD;
if(olds ^ sti) {
if (sti & IN_PLOAD) {
hidx = 0;
hpc = 0;
}
}
}
WRITE_LINE_MEMBER(tms57002_device::cload_w)
{
UINT8 olds = sti;
if(state)
sti &= ~IN_CLOAD;
else
sti |= IN_CLOAD;
if(olds ^ sti) {
if (sti & IN_CLOAD) {
hidx = 0;
ca = 0;
}
}
}
void tms57002_device::device_reset()
{
sti = (sti & ~(SU_MASK|S_READ|S_WRITE|S_BRANCH|S_HOST)) | (SU_ST0|S_IDLE);
pc = 0;
ca = 0;
hidx = 0;
id = 0;
ba0 = 0;
ba1 = 0;
st0 &= ~(ST0_INCS | ST0_DIRI | ST0_FI | ST0_SIM | ST0_PLRI |
ST0_PBCI | ST0_DIRO | ST0_FO | ST0_SOM | ST0_PLRO |
ST0_PBCO | ST0_CNS);
st1 &= ~(ST1_AOV | ST1_SFAI | ST1_SFAO | ST1_MOVM | ST1_MOV |
ST1_SFMA | ST1_SFMO | ST1_RND | ST1_CRM | ST1_DBP);
xba = 0;
xoa = 0;
cache_flush();
}
WRITE8_MEMBER(tms57002_device::data_w)
{
switch(sti & (IN_PLOAD|IN_CLOAD)) {
case 0:
hidx = 0;
sti &= ~SU_CVAL;
break;
case IN_PLOAD:
host[hidx++] = data;
if(hidx >= 3) {
UINT32 val = (host[0]<<16) | (host[1]<<8) | host[2];
hidx = 0;
switch(sti & SU_MASK) {
case SU_ST0:
st0 = val;
sti = (sti & ~SU_MASK) | SU_ST1;
break;
case SU_ST1:
st1 = val;
sti = (sti & ~SU_MASK) | SU_PRG;
break;
case SU_PRG:
program->write_dword(pc++ << 2, val);
break;
}
}
break;
case IN_CLOAD:
if(sti & SU_CVAL) {
host[hidx++] = data;
if(hidx >= 4) {
UINT32 val = (host[0]<<24) | (host[1]<<16) | (host[2]<<8) | host[3];
cmem[sa] = val;
sti &= ~SU_CVAL;
allow_update = 0;
}
} else {
sa = data;
hidx = 0;
sti |= SU_CVAL;
}
break;
case IN_PLOAD|IN_CLOAD:
host[hidx++] = data;
if(hidx >= 4) {
UINT32 val = (host[0]<<24) | (host[1]<<16) | (host[2]<<8) | host[3];
hidx = 0;
cmem[ca++] = val;
}
break;
};
}
READ8_MEMBER(tms57002_device::data_r)
{
UINT8 res;
if(!(sti & S_HOST))
return 0xff;
res = host[hidx];
hidx++;
if(hidx == 4) {
hidx = 0;
sti &= ~S_HOST;
}
return res;
}
READ_LINE_MEMBER(tms57002_device::empty_r)
{
return 1;
}
READ_LINE_MEMBER(tms57002_device::dready_r)
{
return sti & S_HOST ? 0 : 1;
}
READ_LINE_MEMBER(tms57002_device::pc0_r)
{
return pc == 0 ? 0 : 1;
}
WRITE_LINE_MEMBER(tms57002_device::sync_w)
{
if(sti & (IN_PLOAD | IN_CLOAD))
return;
allow_update = 1;
pc = 0;
ca = 0;
id = 0;
if(!(st0 & ST0_INCS)) {
ba0--;
ba1++;
}
xba = (xba-1) & 0x7ffff;
st1 &= ~(ST1_AOV | ST1_MOV);
sti &= ~S_IDLE;
}
void tms57002_device::xm_init()
{
UINT32 adr = xoa + xba;
UINT32 mask = 0;
switch(st0 & ST0_M) {
case ST0_M_64K: mask = 0x0ffff; break;
case ST0_M_256K: mask = 0x3ffff; break;
case ST0_M_1M: mask = 0xfffff; break;
}
if(st0 & ST0_WORD)
adr <<= 2;
else
adr <<= 1;
if(!(st0 & ST0_SEL))
adr <<= 1;
xm_adr = adr & mask;
}
inline void tms57002_device::xm_step_read()
{
UINT32 adr = xm_adr;
UINT8 v = data->read_byte(adr);
int done;
if(st0 & ST0_WORD) {
if(st0 & ST0_SEL) {
int off = 16 - ((adr & 3) << 3);
txrd = (txrd & ~(0xff << off)) | (v << off);
done = off == 0;
} else {
int off = 20 - ((adr & 7) << 2);
txrd = (txrd & ~(0xf << off)) | ((v & 0xf) << off);
done = off == 0;
}
} else {
if(st0 & ST0_SEL) {
int off = 16 - ((adr & 1) << 3);
txrd = (txrd & ~(0xff << off)) | (v << off);
done = off == 8;
if(done)
txrd &= 0xffff00;
} else {
int off = 20 - ((adr & 3) << 2);
txrd = (txrd & ~(0xf << off)) | ((v & 0xf) << off);
done = off == 8;
if(done)
txrd &= 0xffff00;
}
}
if(done) {
xrd = txrd;
sti &= ~S_READ;
xm_adr = 0;
} else
xm_adr = adr+1;
}
inline void tms57002_device::xm_step_write()
{
UINT32 adr = xm_adr;
UINT8 v;
int done;
if(st0 & ST0_WORD) {
if(st0 & ST0_SEL) {
int off = 16 - ((adr & 3) << 3);
v = xwr >> off;
done = off == 0;
} else {
int off = 20 - ((adr & 7) << 2);
v = (xwr >> off) & 0xf;
done = off == 0;
}
} else {
if(st0 & ST0_SEL) {
int off = 16 - ((adr & 1) << 3);
v = xwr >> off;
done = off == 8;
} else {
int off = 20 - ((adr & 3) << 2);
v = (xwr >> off) & 0xf;
done = off == 8;
}
}
data->write_byte(adr, v);
if(done) {
sti &= ~S_WRITE;
xm_adr = 0;
} else
xm_adr = adr+1;
}
INT64 tms57002_device::macc_to_output_0(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
}
return m;
}
INT64 tms57002_device::macc_to_output_1(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m1 = m & 0xfe00000000000ULL;
if(m1 && m1 != 0xfe00000000000ULL)
over = 1;
m <<= 2;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
}
return m;
}
INT64 tms57002_device::macc_to_output_2(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m1 = m & 0xff80000000000ULL;
if(m1 && m1 != 0xff80000000000ULL)
over = 1;
m <<= 4;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
}
return m;
}
INT64 tms57002_device::macc_to_output_3(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m >>= 8;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
}
return m;
}
INT64 tms57002_device::macc_to_output_0s(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
if(m & 0x8000000000000ULL)
m = 0xffff800000000000ULL;
else
m = 0x00007fffffffffffULL;
}
return m;
}
INT64 tms57002_device::macc_to_output_1s(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m1 = m & 0xfe00000000000ULL;
if(m1 && m1 != 0xfe00000000000ULL)
over = 1;
m <<= 2;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
if(m & 0x8000000000000ULL)
m = 0xffff800000000000ULL;
else
m = 0x00007fffffffffffULL;
}
return m;
}
INT64 tms57002_device::macc_to_output_2s(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m1 = m & 0xff80000000000ULL;
if(m1 && m1 != 0xff80000000000ULL)
over = 1;
m <<= 4;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
if(m & 0x8000000000000ULL)
m = 0xffff800000000000ULL;
else
m = 0x00007fffffffffffULL;
}
return m;
}
INT64 tms57002_device::macc_to_output_3s(INT64 rounding, UINT64 rmask)
{
INT64 m = macc;
UINT64 m1;
int over = 0;
// Overflow detection and shifting
m >>= 8;
m = (m + rounding) & rmask;
// Second overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL)
over = 1;
// Overflow handling
if(over) {
st1 |= ST1_MOV;
if(m & 0x8000000000000ULL)
m = 0xffff800000000000ULL;
else
m = 0x00007fffffffffffULL;
}
return m;
}
INT64 tms57002_device::check_macc_overflow_0()
{
INT64 m = macc;
UINT64 m1;
// Overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL) {
st1 |= ST1_MOV;
}
return m;
}
INT64 tms57002_device::check_macc_overflow_1()
{
INT64 m = macc;
UINT64 m1;
// Overflow detection
m1 = m & 0xfe00000000000ULL;
if(m1 && m1 != 0xfe00000000000ULL) {
st1 |= ST1_MOV;
}
return m;
}
INT64 tms57002_device::check_macc_overflow_2()
{
INT64 m = macc;
UINT64 m1;
// Overflow detection
m1 = m & 0xff80000000000ULL;
if(m1 && m1 != 0xff80000000000ULL) {
st1 |= ST1_MOV;
}
return m;
}
INT64 tms57002_device::check_macc_overflow_3()
{
return macc;
}
INT64 tms57002_device::check_macc_overflow_0s()
{
INT64 m = macc;
UINT64 m1;
// Overflow detection
m1 = m & 0xf800000000000ULL;
if(m1 && m1 != 0xf800000000000ULL) {
st1 |= ST1_MOV;
if(m & 0x8000000000000ULL)
m = 0xffff800000000000ULL;
else
m = 0x00007fffffffffffULL;
}
return m;
}
INT64 tms57002_device::check_macc_overflow_1s()
{
INT64 m = macc;
UINT64 m1;
// Overflow detection
m1 = m & 0xfe00000000000ULL;
if(m1 && m1 != 0xfe00000000000ULL) {
st1 |= ST1_MOV;
if(m & 0x8000000000000ULL)
m = 0xffff800000000000ULL;
else
m = 0x00007fffffffffffULL;
}
return m;
}
INT64 tms57002_device::check_macc_overflow_2s()
{
INT64 m = macc;
UINT64 m1;
// Overflow detection
m1 = m & 0xff80000000000ULL;
if(m1 && m1 != 0xff80000000000ULL) {
st1 |= ST1_MOV;
if(m & 0x8000000000000ULL)
m = 0xffff800000000000ULL;
else
m = 0x00007fffffffffffULL;
}
return m;
}
INT64 tms57002_device::check_macc_overflow_3s()
{
return macc;
}
void tms57002_device::cache_flush()
{
int i;
cache.hused = cache.iused = 0;
for(i=0; i != 256; i++)
cache.hashbase[i] = -1;
for(i=0; i != HBS; i++) {
cache.hashnode[i].st1 = 0;
cache.hashnode[i].ipc = -1;
cache.hashnode[i].next = -1;
}
for(i=0; i != IBS; i++) {
cache.inst[i].op = 0;
cache.inst[i].next = -1;
cache.inst[i].param = 0;
}
}
void tms57002_device::add_one(cstate *cs, unsigned short op, UINT8 param)
{
short ipc = cache.iused++;
cache.inst[ipc].op = op;
cache.inst[ipc].param = param;
cache.inst[ipc].next = -1;
if(cs->ipc != -1)
cache.inst[cs->ipc].next = ipc;
cs->ipc = ipc;
if(cs->hnode != -1) {
cache.hashnode[cs->hnode].ipc = ipc;
cs->hnode = -1;
}
}
void tms57002_device::decode_one(UINT32 opcode, cstate *cs, void (tms57002_device::*dec)(UINT32 opcode, unsigned short *op, cstate *cs))
{
unsigned short op = 0;
(this->*dec)(opcode, &op, cs);
if(!op)
return;
add_one(cs, op, opcode & 0xff);
}
short tms57002_device::get_hash(unsigned char adr, UINT32 st1, short *pnode)
{
short hnode;
st1 &= ST1_CACHE;
*pnode = -1;
hnode = cache.hashbase[adr];
while(hnode != -1) {
if(cache.hashnode[hnode].st1 == st1)
return cache.hashnode[hnode].ipc;
*pnode = hnode;
hnode = cache.hashnode[hnode].next;
}
return -1;
}
short tms57002_device::get_hashnode(unsigned char adr, UINT32 st1, short pnode)
{
short hnode = cache.hused++;
cache.hashnode[hnode].st1 = st1 & ST1_CACHE;
cache.hashnode[hnode].ipc = -1;
cache.hashnode[hnode].next = -1;
if(pnode == -1)
cache.hashbase[adr] = hnode;
else
cache.hashnode[pnode].next = hnode;
return hnode;
}
int tms57002_device::decode_get_pc()
{
short pnode, res;
cstate cs;
UINT8 adr = pc;
res = get_hash(adr, st1, &pnode);
if(res != -1)
return res;
if(HBS - cache.hused < 256 || IBS - cache.iused < 256*3) {
cache_flush();
pnode = -1;
}
cs.hnode = res = get_hashnode(adr, st1, pnode);
cs.ipc = -1;
cs.branch = 0;
for(;;) {
short ipc;
UINT32 opcode = program->read_dword(adr << 2);
cs.inc = 0;
if((opcode & 0xfc0000) == 0xfc0000)
decode_one(opcode, &cs, &tms57002_device::decode_cat3);
else {
decode_one(opcode, &cs, &tms57002_device::decode_cat2_pre);
decode_one(opcode, &cs, &tms57002_device::decode_cat1);
decode_one(opcode, &cs, &tms57002_device::decode_cat2_post);
}
add_one(&cs, cs.inc, 0);
if(cs.branch)
break;
adr++;
ipc = get_hash(adr, st1, &pnode);
if(ipc != -1) {
cache.inst[cs.ipc].next = ipc;
break;
}
cs.hnode = get_hashnode(adr, st1, pnode);
}
return cache.hashnode[res].ipc;
}
void tms57002_device::execute_run()
{
int ipc = -1;
while(icount > 0 && !(sti & (S_IDLE | IN_PLOAD | IN_CLOAD))) {
int iipc;
debugger_instruction_hook(this, pc);
if(ipc == -1)
ipc = decode_get_pc();
iipc = ipc;
if(sti & (S_READ|S_WRITE)) {
if(sti & S_READ)
xm_step_read();
else
xm_step_write();
}
for(;;) {
UINT32 c, d;
INT64 r;
const icd *i = cache.inst + ipc;
ipc = i->next;
switch(i->op) {
case 0:
goto inst;
case 1:
++ca;
goto inst;
case 2:
++id;
goto inst;
case 3:
++ca, ++id;
goto inst;
#define CINTRP
#include "cpu/tms57002/tms57002.hxx"
#undef CINTRP
default:
fatalerror("Unhandled opcode in tms57002_execute\n");
}
}
inst:
icount--;
if(rptc) {
rptc--;
ipc = iipc;
} else if(sti & S_BRANCH) {
sti &= ~S_BRANCH;
ipc = -1;
} else
pc++; // Wraps if it reaches 256, next wraps too
if(rptc_next) {
rptc = rptc_next;
rptc_next = 0;
}
}
if(icount > 0)
icount = 0;
}
void tms57002_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
assert(samples == 1);
if(st0 & ST0_SIM) {
si[0] = (inputs[0][0] << 8) & 0xffffff;
si[1] = (inputs[1][0] << 8) & 0xffffff;
si[2] = (inputs[2][0] << 8) & 0xffffff;
si[3] = (inputs[3][0] << 8) & 0xffffff;
} else {
si[0] = inputs[0][0] & 0xffffff;
si[1] = inputs[1][0] & 0xffffff;
si[2] = inputs[2][0] & 0xffffff;
si[3] = inputs[3][0] & 0xffffff;
}
outputs[0][0] = INT16(so[0] >> 8);
outputs[1][0] = INT16(so[1] >> 8);
outputs[2][0] = INT16(so[2] >> 8);
outputs[3][0] = INT16(so[3] >> 8);
sync_w(1);
}
void tms57002_device::device_start()
{
sti = S_IDLE;
program = &space(AS_PROGRAM);
data = &space(AS_DATA);
state_add(STATE_GENPC, "GENPC", pc).noshow();
state_add(TMS57002_PC, "PC", pc);
state_add(TMS57002_ST0, "ST0", st0);
state_add(TMS57002_ST1, "ST1", st1);
state_add(TMS57002_RPTC, "RPTC", rptc);
state_add(TMS57002_AACC, "AACC", aacc);
state_add(TMS57002_MACC, "MACC", macc).mask(U64(0xfffffffffffff));
state_add(TMS57002_BA0, "BA0", ba0);
state_add(TMS57002_BA1, "BA1", ba1);
state_add(TMS57002_CREG, "CREG", creg);
state_add(TMS57002_CA, "CA", ca);
state_add(TMS57002_ID, "ID", id);
state_add(TMS57002_XBA, "XBA", xba);
state_add(TMS57002_XOA, "XOA", xoa);
state_add(TMS57002_XRD, "XRD", xrd);
state_add(TMS57002_XWR, "XWR", xwr);
state_add(TMS57002_HIDX, "HIDX", hidx);
state_add(TMS57002_HOST0, "HOST0", host[0]);
state_add(TMS57002_HOST1, "HOST1", host[1]);
state_add(TMS57002_HOST2, "HOST2", host[2]);
state_add(TMS57002_HOST3, "HOST3", host[3]);
m_icountptr = &icount;
stream_alloc(4, 4, STREAM_SYNC);
save_item(NAME(macc));
save_item(NAME(cmem));
save_item(NAME(dmem0));
save_item(NAME(dmem1));
save_item(NAME(si));
save_item(NAME(so));
save_item(NAME(st0));
save_item(NAME(st1));
save_item(NAME(sti));
save_item(NAME(aacc));
save_item(NAME(xoa));
save_item(NAME(xba));
save_item(NAME(xwr));
save_item(NAME(xrd));
save_item(NAME(txrd));
save_item(NAME(creg));
save_item(NAME(pc));
save_item(NAME(ca));
save_item(NAME(id));
save_item(NAME(ba0));
save_item(NAME(ba1));
save_item(NAME(rptc));
save_item(NAME(rptc_next));
save_item(NAME(sa));
save_item(NAME(xm_adr));
save_item(NAME(host));
save_item(NAME(hidx));
save_item(NAME(allow_update));
}
UINT32 tms57002_device::execute_min_cycles() const
{
return 1;
}
UINT32 tms57002_device::execute_max_cycles() const
{
return 3;
}
UINT32 tms57002_device::execute_input_lines() const
{
return 0;
}
UINT32 tms57002_device::disasm_min_opcode_bytes() const
{
return 4;
}
UINT32 tms57002_device::disasm_max_opcode_bytes() const
{
return 4;
}
offs_t tms57002_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
{
extern CPU_DISASSEMBLE( tms57002 );
return CPU_DISASSEMBLE_NAME(tms57002)(this, buffer, pc, oprom, opram, options);
}
const address_space_config *tms57002_device::memory_space_config(address_spacenum spacenum) const
{
switch(spacenum) {
case AS_PROGRAM: return &program_config;
case AS_DATA: return &data_config;
default: return nullptr;
}
}