// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
//
// MAS 3507D MPEG audio decoder
//
#include "emu.h"
#include "mas3507d.h"
// device type definition
DEFINE_DEVICE_TYPE(MAS3507D, mas3507d_device, "mas3507d", "MAS 3507D MPEG decoder")
mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, MAS3507D, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, i2c_bus_state(), i2c_bus_address(), i2c_scli(false), i2c_sclo(false), i2c_sdai(false), i2c_sdao(false)
, i2c_bus_curbit(0), i2c_bus_curval(0), i2c_subdest(), i2c_command(), i2c_bytecount(0), i2c_io_bank(0), i2c_io_adr(0), i2c_io_count(0), i2c_io_val(0)
{
}
void mas3507d_device::device_start()
{
}
void mas3507d_device::device_reset()
{
i2c_scli = i2c_sdai = true;
i2c_sclo = i2c_sdao = true;
i2c_bus_state = IDLE;
i2c_bus_address = UNKNOWN;
i2c_bus_curbit = -1;
i2c_bus_curval = 0;
}
void mas3507d_device::i2c_scl_w(bool line)
{
if(line == i2c_scli)
return;
i2c_scli = line;
if(i2c_scli) {
if(i2c_bus_state == STARTED) {
if(i2c_sdai)
i2c_bus_curval |= 1 << i2c_bus_curbit;
i2c_bus_curbit --;
if(i2c_bus_curbit == -1) {
if(i2c_bus_address == UNKNOWN) {
if(i2c_device_got_address(i2c_bus_curval)) {
i2c_bus_state = ACK;
i2c_bus_address = VALIDATED;
} else {
i2c_bus_state = NAK;
i2c_bus_address = WRONG;
}
} else if(i2c_bus_address == VALIDATED) {
i2c_bus_state = ACK;
i2c_device_got_byte(i2c_bus_curval);
}
}
} else if(i2c_bus_state == ACK) {
i2c_bus_state = ACK2;
i2c_sdao = false;
}
} else {
if(i2c_bus_state == ACK2) {
i2c_bus_state = STARTED;
i2c_bus_curbit = 7;
i2c_bus_curval = 0;
i2c_sdao = true;
}
}
}
void mas3507d_device::i2c_nak()
{
assert(i2c_bus_state == ACK);
i2c_bus_state = NAK;
}
void mas3507d_device::i2c_sda_w(bool line)
{
if(line == i2c_sdai)
return;
i2c_sdai = line;
if(i2c_scli) {
if(!i2c_sdai) {
i2c_bus_state = STARTED;
i2c_bus_address = UNKNOWN;
i2c_bus_curbit = 7;
i2c_bus_curval = 0;
} else {
i2c_device_got_stop();
i2c_bus_state = IDLE;
i2c_bus_address = UNKNOWN;
i2c_bus_curbit = 7;
i2c_bus_curval = 0;
}
}
}
int mas3507d_device::i2c_scl_r()
{
return i2c_scli && i2c_sclo;
}
int mas3507d_device::i2c_sda_r()
{
return i2c_sdai && i2c_sdao;
}
bool mas3507d_device::i2c_device_got_address(uint8_t address)
{
i2c_subdest = UNDEFINED;
return (address & 0xfe) == 0x3a;
}
void mas3507d_device::i2c_device_got_byte(uint8_t byte)
{
switch(i2c_subdest) {
case UNDEFINED:
if(byte == 0x68)
i2c_subdest = DATA;
else if(byte == 0x69)
i2c_subdest = DATA;
else if(byte == 0x6a)
i2c_subdest = CONTROL;
else
i2c_subdest = BAD;
i2c_bytecount = 0;
break;
case BAD:
logerror("MAS I2C: Dropping byte %02x\n", byte);
break;
case DATA:
if(!i2c_bytecount) {
switch(byte >> 4) {
case 0: case 1:
i2c_command = CMD_RUN;
i2c_io_adr = byte << 8;
break;
case 3:
i2c_command = CMD_READ_CTRL;
logerror("MAS I2C: READ_CTRL\n");
break;
case 9:
i2c_io_adr = (byte & 15) << 4;
i2c_command = CMD_WRITE_REG;
break;
case 0xa: case 0xb:
i2c_io_bank = (byte >> 4) & 1;
i2c_command = CMD_WRITE_MEM;
break;
case 0xd:
i2c_command = CMD_READ_REG;
logerror("MAS I2C: READ_REG\n");
break;
case 0xe: case 0xf:
i2c_io_bank = (byte >> 4) & 1;
i2c_command = CMD_READ_MEM;
logerror("MAS I2C: READ_MEM\n");
break;
default:
i2c_command = CMD_BAD;
logerror("MAS I2C: BAD\n");
break;
}
} else {
switch(i2c_command) {
default:
logerror("MAS I2C: Ignoring byte %02x\n", byte);
break;
case CMD_WRITE_REG:
switch(i2c_bytecount) {
case 1: i2c_io_adr |= byte >> 4; i2c_io_val = byte & 15; break;
case 2: i2c_io_val |= byte << 12; break;
case 3: i2c_io_val |= byte << 4; reg_write(i2c_io_adr, i2c_io_val); break;
case 4: i2c_nak(); return;
}
break;
case CMD_RUN:
if(i2c_bytecount > 1) {
i2c_nak();
return;
}
i2c_io_adr |= byte;
run_program(i2c_io_adr);
break;
case CMD_WRITE_MEM:
switch(i2c_bytecount) {
case 2: i2c_io_count = byte << 8; break;
case 3: i2c_io_count |= byte; break;
case 4: i2c_io_adr = byte << 8; break;
case 5: i2c_io_adr |= byte; break;
}
if(i2c_bytecount >= 6) {
uint32_t i2c_wordid = (i2c_bytecount - 6) >> 2;
uint32_t i2c_offset = (i2c_bytecount - 6) & 3;
if(i2c_wordid >= i2c_io_count) {
i2c_nak();
return;
}
switch(i2c_offset) {
case 0: i2c_io_val = byte << 8; break;
case 1: i2c_io_val |= byte; break;
case 3: i2c_io_val |= (byte & 15) << 16; mem_write(i2c_io_bank, i2c_io_adr + i2c_wordid, i2c_io_val); break;
}
}
break;
}
}
i2c_bytecount++;
break;
case CONTROL:
logerror("MAS I2C: Control byte %02x\n", byte);
break;
}
}
void mas3507d_device::i2c_device_got_stop()
{
logerror("MAS I2C: got stop\n");
}
void mas3507d_device::mem_write(int bank, uint32_t adr, uint32_t val)
{
switch(adr | (bank ? 0x10000 : 0)) {
case 0x0032f: logerror("MAS3507D: OutputConfig = %05x\n", val); break;
case 0x107f8: logerror("MAS3507D: left->left gain = %05x\n", val); break;
case 0x107f9: logerror("MAS3507D: left->right gain = %05x\n", val); break;
case 0x107fa: logerror("MAS3507D: right->left gain = %05x\n", val); break;
case 0x107fb: logerror("MAS3507D: right->right gain = %05x\n", val); break;
default: logerror("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
}
}
void mas3507d_device::reg_write(uint32_t adr, uint32_t val)
{
switch(adr) {
case 0x8e: logerror("MAS3507D: DCCF = %05x\n", val); break;
case 0xaa: logerror("MAS3507D: Mute/bypass = %05x\n", val); break;
case 0xe6: logerror("MAS3507D: StartupConfig = %05x\n", val); break;
case 0xe7: logerror("MAS3507D: Kprescale = %05x\n", val); break;
case 0x6b: logerror("MAS3507D: Kbass = %05x\n", val); break;
case 0x6f: logerror("MAS3507D: Ktreble = %05x\n", val); break;
default: logerror("MAS3507D: reg %02x = %05x\n", adr, val); break;
}
}
void mas3507d_device::run_program(uint32_t adr)
{
switch(adr) {
case 0xfcb: logerror("MAS3507D: validate OutputConfig\n"); break;
default: logerror("MAS3507D: run %04x\n", adr); break;
}
}
void mas3507d_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
}