// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************
macadb.c - handles various aspects of ADB on the Mac.
***************************************************************************/
#include "emu.h"
#include "includes/mac.h"
#define LOG_ADB 0
#define LOG_ADB_MCU_CMD 0
#define LOG_ADB_TALK_LISTEN 0
// ADB states
#define ADB_STATE_NEW_COMMAND (0)
#define ADB_STATE_XFER_EVEN (1)
#define ADB_STATE_XFER_ODD (2)
#define ADB_STATE_IDLE (3)
#define ADB_STATE_NOTINIT (4)
// ADB commands
#define ADB_CMD_RESET (0)
#define ADB_CMD_FLUSH (1)
// ADB line states
enum
{
// receive states
LST_IDLE = 0,
LST_ATTENTION,
LST_BIT0,
LST_BIT1,
LST_BIT2,
LST_BIT3,
LST_BIT4,
LST_BIT5,
LST_BIT6,
LST_BIT7,
LST_TSTOP,
LST_WAITT1T,
LST_RCVSTARTBIT,
LST_SRQNODATA,
// send states
LST_TSTOPSTART,
LST_TSTOPSTARTa,
LST_STARTBIT,
LST_SENDBIT0,
LST_SENDBIT0a,
LST_SENDBIT1,
LST_SENDBIT1a,
LST_SENDBIT2,
LST_SENDBIT2a,
LST_SENDBIT3,
LST_SENDBIT3a,
LST_SENDBIT4,
LST_SENDBIT4a,
LST_SENDBIT5,
LST_SENDBIT5a,
LST_SENDBIT6,
LST_SENDBIT6a,
LST_SENDBIT7,
LST_SENDBIT7a,
LST_SENDSTOP,
LST_SENDSTOPa
};
/* *************************************************************************
* High-level ADB primitives used by all lower-level implementations
* *************************************************************************/
#if LOG_ADB
static char const *const adb_statenames[4] = { "NEW", "EVEN", "ODD", "IDLE" };
#endif
int mac_state::adb_pollkbd(int update)
{
int i, j, keybuf, report, codes[2], result;
codes[0] = codes[1] = 0xff; // key up
report = result = 0;
for (i = 0; i < 6; i++)
{
keybuf = m_keys[i]->read();
// any changes in this row?
if ((keybuf != m_key_matrix[i]) && (report < 2))
{
// check each column bit
for (j=0; j<16; j++)
{
if (((keybuf ^ m_key_matrix[i]) >> j) & 1)
{
// update m_key_matrix
if (update)
{
m_key_matrix[i] = (m_key_matrix[i] & ~ (1 << j)) | (keybuf & (1 << j));
}
codes[report] = (i<<4)|j;
// key up?
if (!(keybuf & (1 << j)))
{
codes[report] |= 0x80;
}
// update modifier state
if (update)
{
if (((i<<4)|j) == 0x39)
{
if (codes[report] & 0x80)
{
m_adb_modifiers &= ~0x20;
}
else
{
m_adb_modifiers |= 0x20;
}
}
if (((i<<4)|j) == 0x36)
{
if (codes[report] & 0x80)
{
m_adb_modifiers &= ~0x8;
}
else
{
m_adb_modifiers |= 0x08;
}
}
if (((i<<4)|j) == 0x38)
{
if (codes[report] & 0x80)
{
m_adb_modifiers &= ~0x4;
}
else
{
m_adb_modifiers |= 0x04;
}
}
if (((i<<4)|j) == 0x3a)
{
if (codes[report] & 0x80)
{
m_adb_modifiers &= ~0x2;
}
else
{
m_adb_modifiers |= 0x02;
}
}
if (((i<<4)|j) == 0x37)
{
if (codes[report] & 0x80)
{
m_adb_modifiers &= ~0x1;
}
else
{
m_adb_modifiers |= 0x01;
}
}
}
// we run out of keys we can track?
report++;
if (report == 2)
{
break;
}
}
}
// we run out of keys we can track?
if (report == 2)
{
break;
}
}
}
// printf("ADB keyboard: update %d keys %02x %02x\n", update, codes[0], codes[1]);
// figure out if there was a change
if ((m_adb_currentkeys[0] != codes[0]) || (m_adb_currentkeys[1] != codes[1]))
{
result = 1;
// if we want to update the current read, do so
if (update)
{
if(m_adb_currentkeys[0] != codes[0]) {
m_adb_keybuf[m_adb_keybuf_end] = codes[0];
m_adb_keybuf_end = (m_adb_keybuf_end+1) % kADBKeyBufSize;
}
if(m_adb_currentkeys[1] != codes[1]) {
m_adb_keybuf[m_adb_keybuf_end] = codes[1];
m_adb_keybuf_end = (m_adb_keybuf_end+1) % kADBKeyBufSize;
}
m_adb_currentkeys[0] = codes[0];
m_adb_currentkeys[1] = codes[1];
}
}
return result;
}
int mac_state::adb_pollmouse()
{
int NewX, NewY, NewButton;
if (!m_adb_mouse_initialized)
{
return 0;
}
NewButton = m_mouse0->read() & 0x01;
NewX = m_mouse2->read();
NewY = m_mouse1->read();
if ((NewX != m_adb_lastmousex) || (NewY != m_adb_lastmousey) || (NewButton != m_adb_lastbutton))
{
return 1;
}
return 0;
}
void mac_state::adb_accummouse( uint8_t *MouseX, uint8_t *MouseY )
{
int MouseCountX = 0, MouseCountY = 0;
int NewX, NewY;
NewX = ioport("MOUSE2")->read();
NewY = ioport("MOUSE1")->read();
/* see if it moved in the x coord */
if (NewX != m_adb_lastmousex)
{
int diff = NewX - m_adb_lastmousex;
/* check for wrap */
if (diff > 0x80)
diff = 0x100-diff;
if (diff < -0x80)
diff = -0x100-diff;
MouseCountX += diff;
m_adb_lastmousex = NewX;
}
/* see if it moved in the y coord */
if (NewY != m_adb_lastmousey)
{
int diff = NewY - m_adb_lastmousey;
/* check for wrap */
if (diff > 0x80)
diff = 0x100-diff;
if (diff < -0x80)
diff = -0x100-diff;
MouseCountY += diff;
m_adb_lastmousey = NewY;
}
m_adb_lastbutton = m_mouse0->read() & 0x01;
*MouseX = (uint8_t)MouseCountX;
*MouseY = (uint8_t)MouseCountY;
}
void mac_state::adb_talk()
{
int addr, reg;
addr = (m_adb_command>>4);
reg = (m_adb_command & 3);
//printf("Mac sent %x (cmd %d addr %d reg %d mr %d kr %d)\n", m_adb_command, (m_adb_command>>2)&3, addr, reg, m_adb_mouseaddr, m_adb_keybaddr);
if (m_adb_waiting_cmd)
{
switch ((m_adb_command>>2)&3)
{
case 0:
case 1:
switch (reg)
{
case ADB_CMD_RESET:
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("ADB RESET: reg %x address %x\n", reg, addr);
#endif
m_adb_direction = 0;
m_adb_send = 0;
break;
case ADB_CMD_FLUSH:
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("ADB FLUSH: reg %x address %x\n", reg, addr);
#endif
m_adb_direction = 0;
m_adb_send = 0;
break;
default: // reserved/unused
break;
}
break;
case 2: // listen
m_adb_datasize = 0;
if ((addr == m_adb_keybaddr) || (addr == m_adb_mouseaddr))
{
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("ADB LISTEN: reg %x address %x\n", reg, addr);
#endif
m_adb_direction = 1; // input from Mac
m_adb_command = 0;
m_adb_listenreg = reg;
m_adb_listenaddr = addr;
if ((ADB_IS_EGRET) || (ADB_IS_CUDA))
{
m_adb_stream_ptr = 0;
memset(m_adb_buffer, 0, sizeof(m_adb_buffer));
}
}
else
{
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("ADB LISTEN to unknown device, timing out\n");
#endif
m_adb_direction = 0;
}
break;
case 3: // talk
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("ADB TALK: reg %x address %x (K %x M %x)\n", reg, addr, m_adb_keybaddr, m_adb_mouseaddr);
#endif
// keep track of what device the Mac last TALKed to
m_adb_last_talk = addr;
m_adb_direction = 0; // output to Mac
if (addr == m_adb_mouseaddr)
{
uint8_t mouseX, mouseY;
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("Talking to mouse, register %x\n", reg);
#endif
switch (reg)
{
// read mouse
case 0:
if (m_adb_srq_switch)
{
m_adb_srq_switch = 0;
mouseX = mouseY = 0;
}
else
{
this->adb_accummouse(&mouseX, &mouseY);
}
m_adb_buffer[0] = (m_adb_lastbutton & 0x01) ? 0x00 : 0x80;
m_adb_buffer[0] |= mouseX & 0x7f;
m_adb_buffer[1] = mouseY & 0x7f;
m_adb_datasize = 2;
break;
// get ID/handler
case 3:
m_adb_buffer[0] = 0x60 | (m_adb_mouseaddr&0xf); // SRQ enable, no exceptional event
m_adb_buffer[1] = 0x01; // handler 1
m_adb_datasize = 2;
m_adb_mouse_initialized = 1;
break;
default:
break;
}
if (adb_pollkbd(0))
{
m_adb_srqflag = true;
}
}
else if (addr == m_adb_keybaddr)
{
int kbd_has_data = 1;
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("Talking to keyboard, register %x\n", reg);
#endif
switch (reg)
{
// read keyboard
case 0:
if (m_adb_srq_switch)
{
m_adb_srq_switch = 0;
}
else
{
kbd_has_data = this->adb_pollkbd(1);
}
/* if (m_adb_currentkeys[0] != 0xff)
{
printf("Keys[0] = %02x\n", m_adb_currentkeys[0]);
}
if (m_adb_currentkeys[1] != 0xff)
{
printf("Keys[1] = %02x\n", m_adb_currentkeys[1]);
}*/
if(kbd_has_data)
{
if(m_adb_keybuf_start == m_adb_keybuf_end)
{
// printf("%s: buffer empty\n", __func__);
m_adb_buffer[0] = 0xff;
m_adb_buffer[1] = 0xff;
}
else
{
m_adb_buffer[1] = m_adb_keybuf[m_adb_keybuf_start];
m_adb_keybuf_start = (m_adb_keybuf_start+1) % kADBKeyBufSize;
if(m_adb_keybuf_start != m_adb_keybuf_end)
{
m_adb_buffer[0] = m_adb_keybuf[m_adb_keybuf_start];
m_adb_keybuf_start = (m_adb_keybuf_start+1) % kADBKeyBufSize;
}
else
{
m_adb_buffer[0] = 0xff;
}
}
m_adb_datasize = 2;
}
else
{
m_adb_buffer[0] = 0xff;
m_adb_buffer[1] = 0xff;
}
break;
// read modifier keys
case 2:
{
this->adb_pollkbd(1);
m_adb_buffer[0] = m_adb_modifiers;
m_adb_buffer[1] = 0xff;
m_adb_datasize = 2;
}
break;
// get ID/handler
case 3:
m_adb_buffer[0] = 0x60 | (m_adb_keybaddr&0xf); // SRQ enable, no exceptional event
m_adb_buffer[1] = 0x01; // handler 1
m_adb_datasize = 2;
m_adb_keybinitialized = 1;
break;
default:
break;
}
if (adb_pollmouse())
{
m_adb_srqflag = true;
}
}
else
{
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("ADB: talking to unconnected device %d (K %d M %d)\n", addr, m_adb_keybaddr, m_adb_mouseaddr);
#endif
m_adb_buffer[0] = m_adb_buffer[1] = 0;
m_adb_datasize = 0;
if ((adb_pollkbd(0)) || (adb_pollmouse()))
{
m_adb_srqflag = true;
}
}
break;
}
m_adb_waiting_cmd = 0;
}
else
{
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("Got LISTEN data %02x %02x for device %x reg %x\n", m_adb_command, m_adb_buffer[1], m_adb_listenaddr, m_adb_listenreg);
#endif
m_adb_direction = 0;
if (m_adb_listenaddr == m_adb_mouseaddr)
{
if ((m_adb_listenreg == 3) && (m_adb_command > 0) && (m_adb_command < 16))
{
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("MOUSE: moving to address %x\n", m_adb_command);
#endif
m_adb_mouseaddr = m_adb_command&0x0f;
}
}
else if (m_adb_listenaddr == m_adb_keybaddr)
{
if ((m_adb_listenreg == 3) && (m_adb_command > 0) && (m_adb_command < 16))
{
#if LOG_ADB || LOG_ADB_TALK_LISTEN
printf("KEYBOARD: moving to address %x\n", m_adb_command);
#endif
m_adb_keybaddr = m_adb_command&0x0f;
}
}
}
}
TIMER_CALLBACK_MEMBER(mac_state::mac_adb_tick)
{
if ((ADB_IS_EGRET) || (ADB_IS_CUDA))
{
switch (m_adb_linestate)
{
case LST_SRQNODATA:
set_adb_line(ASSERT_LINE);
m_adb_linestate = LST_IDLE;
break;
case LST_TSTOPSTART:
set_adb_line(ASSERT_LINE);
m_adb_timer->adjust(attotime::from_ticks(57, 1000000));
m_adb_linestate++;
break;
case LST_TSTOPSTARTa:
set_adb_line(CLEAR_LINE);
m_adb_timer->adjust(attotime::from_ticks(57, 1000000));
m_adb_linestate++;
break;
case LST_STARTBIT:
set_adb_line(ASSERT_LINE);
m_adb_timer->adjust(attotime::from_ticks(105, 1000000));
m_adb_linestate++;
break;
case LST_SENDBIT0:
case LST_SENDBIT1:
case LST_SENDBIT2:
case LST_SENDBIT3:
case LST_SENDBIT4:
case LST_SENDBIT5:
case LST_SENDBIT6:
case LST_SENDBIT7:
set_adb_line(CLEAR_LINE);
if (m_adb_buffer[m_adb_stream_ptr] & 0x80)
{
// printf("1 ");
m_adb_timer->adjust(attotime::from_ticks(57, 1000000));
}
else
{
// printf("0 ");
m_adb_timer->adjust(attotime::from_ticks(105, 1000000));
}
m_adb_linestate++;
break;
case LST_SENDBIT0a:
case LST_SENDBIT1a:
case LST_SENDBIT2a:
case LST_SENDBIT3a:
case LST_SENDBIT4a:
case LST_SENDBIT5a:
case LST_SENDBIT6a:
set_adb_line(ASSERT_LINE);
if (m_adb_buffer[m_adb_stream_ptr] & 0x80)
{
m_adb_timer->adjust(attotime::from_ticks(105, 1000000));
}
else
{
m_adb_timer->adjust(attotime::from_ticks(57, 1000000));
}
m_adb_buffer[m_adb_stream_ptr] <<= 1;
m_adb_linestate++;
break;
case LST_SENDBIT7a:
set_adb_line(ASSERT_LINE);
if (m_adb_buffer[m_adb_stream_ptr] & 0x80)
{
// printf(" ");
m_adb_timer->adjust(attotime::from_ticks(105, 1000000));
}
else
{
// printf(" ");
m_adb_timer->adjust(attotime::from_ticks(57, 1000000));
}
m_adb_stream_ptr++;
if (m_adb_stream_ptr == m_adb_datasize)
{
m_adb_linestate++;
}
else
{
m_adb_linestate = LST_SENDBIT0;
}
break;
case LST_SENDSTOP:
set_adb_line(CLEAR_LINE);
m_adb_timer->adjust(attotime::from_ticks((57*2), 1000000));
m_adb_linestate++;
break;
case LST_SENDSTOPa:
set_adb_line(ASSERT_LINE);
m_adb_timer->adjust(attotime::never);
m_adb_linestate = LST_IDLE;
break;
}
}
else
{
// for input to Mac, the VIA reads on the *other* clock edge, so update this here
if (!m_adb_direction)
{
m_via1->write_cb2((m_adb_send & 0x80)>>7);
m_adb_send <<= 1;
}
// do one clock transition on CB1 to advance the VIA shifter
//printf("ADB transition (%d)\n", m_adb_timer_ticks);
if (m_adb_direction)
{
m_via1->write_cb1(m_adb_extclock ^ 1);
m_via1->write_cb1(m_adb_extclock);
}
else
{
m_via1->write_cb1(m_adb_extclock);
m_via1->write_cb1(m_adb_extclock ^ 1);
}
m_adb_timer_ticks--;
if (!m_adb_timer_ticks)
{
m_adb_timer->adjust(attotime::never);
if ((m_adb_direction) && (ADB_IS_BITBANG_CLASS))
{
adb_talk();
if((m_adb_last_talk == 2) && m_adb_datasize) {
m_adb_timer_ticks = 8;
m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(100)));
}
}
if (!(m_adb_direction) && (ADB_IS_BITBANG_CLASS))
{
// m_via1->write_cb1(m_adb_extclock);
m_via1->write_cb1(m_adb_extclock ^ 1);
}
}
else
{
m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
}
}
}
void mac_state::mac_adb_newaction(int state)
{
if (state != m_adb_state)
{
#if LOG_ADB
printf("New ADB state: %s\n", adb_statenames[state]);
#endif
m_adb_state = state;
m_adb_timer_ticks = 8;
switch (state)
{
case ADB_STATE_NEW_COMMAND:
m_adb_command = m_adb_send = 0;
m_adb_direction = 1; // Mac is shifting us a command
m_adb_waiting_cmd = 1; // we're going to get a command
m_adb_irq_pending = 0;
m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(100)));
break;
case ADB_STATE_XFER_EVEN:
case ADB_STATE_XFER_ODD:
//printf("EVEN/ODD: adb datasize %d\n", m_adb_datasize);
if (m_adb_datasize > 0)
{
int i;
// is something trying to send to the Mac?
if (m_adb_direction == 0)
{
// set up the byte
m_adb_send = m_adb_buffer[0];
//printf("ADB sending %02x\n", m_adb_send);
m_adb_datasize--;
// move down the rest of the buffer, if any
for (i = 0; i < m_adb_datasize; i++)
{
m_adb_buffer[i] = m_adb_buffer[i+1];
}
}
}
else
{
m_adb_send = 0;
m_adb_irq_pending = 1;
}
m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(100)));
break;
case ADB_STATE_IDLE:
m_adb_irq_pending = 0;
break;
}
}
}
TIMER_CALLBACK_MEMBER(mac_state::mac_pmu_tick)
{
// state 10 means this is in response to an ADB command
if (m_pm_state == 10)
{
#if LOG_ADB
printf("PM: was state 10, chunk-chunking CB1\n");
#endif
m_pm_state = 0;
// tick CB1, which should cause a PMU interrupt on PMU machines
m_adb_extclock ^= 1;
m_via1->write_cb1(m_adb_extclock);
m_adb_extclock ^= 1;
m_via1->write_cb1(m_adb_extclock);
}
else
{
#if LOG_ADB
printf("PM: timer tick, lowering ACK\n");
#endif
m_pm_ack &= ~2; // lower ACK to handshake next step
}
}
void mac_state::pmu_one_byte_reply(uint8_t result)
{
m_pm_out[0] = m_pm_out[1] = 1; // length
m_pm_out[2] = result;
m_pm_slen = 3;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
}
void mac_state::pmu_three_byte_reply(uint8_t result1, uint8_t result2, uint8_t result3)
{
m_pm_out[0] = m_pm_out[1] = 3; // length
m_pm_out[2] = result1;
m_pm_out[3] = result2;
m_pm_out[4] = result3;
m_pm_slen = 5;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
}
void mac_state::pmu_exec()
{
m_pm_sptr = 0; // clear send pointer
m_pm_slen = 0; // and send length
m_pm_dptr = 0; // and receive pointer
// printf("PMU: Command %02x\n", mac->m_pm_cmd[0]);
switch (m_pm_cmd[0])
{
case 0x10: // subsystem power and clock ctrl
break;
case 0x20: // send ADB command (PMU must issue an IRQ on completion)
#if 0
printf("PMU: Send ADB %02x %02x cmd %02x flag %02x data %02x %02x\n",
m_pm_cmd[0], // 0x20
m_pm_cmd[1], // ???
m_pm_cmd[2], // adb flags (2 for autopoll active, 3 to reset bus?)
m_pm_cmd[3], // length of ADB data
m_pm_cmd[4], // adb data
m_pm_cmd[5]);
#endif
#if 0
m_pm_state = 10;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
if (ADB_IS_PM_VIA1_CLASS)
{
m_pmu_int_status = 0x1;
}
else if (ADB_IS_PM_VIA2_CLASS)
{
m_pmu_int_status = 0x10;
}
else
{
fatalerror("mac: unknown ADB PMU type\n");
}
m_pmu_last_adb_command = m_pm_cmd[2];
m_adb_command = m_pm_cmd[2];
m_adb_waiting_cmd = 1;
adb_talk();
if ((m_pm_cmd[2] & 0xf) == 0xb) // LISTEN register 3 (remap)
{
m_adb_waiting_cmd = 0;
m_adb_command = mac->m_pm_cmd[5];
adb_talk();
}
#else
if (((m_pm_cmd[2] == 0xfc) || (m_pm_cmd[2] == 0x2c)) && (m_pm_cmd[3] == 4))
{
// printf("PMU: request to poll ADB, returning nothing\n");
m_pm_slen = 0;
m_pmu_int_status = 0;
}
else
{
m_pm_state = 10;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
if (ADB_IS_PM_VIA1_CLASS)
{
m_pmu_int_status = 0x1;
}
else if (ADB_IS_PM_VIA2_CLASS)
{
m_pmu_int_status = 0x10;
}
else
{
fatalerror("mac: unknown ADB PMU type\n");
}
m_pmu_last_adb_command = m_pm_cmd[2];
}
m_adb_command = m_pm_cmd[2];
m_adb_waiting_cmd = 1;
adb_talk();
#endif
break;
case 0x21: // turn ADB auto-poll off (does this need a reply?)
break;
case 0x28: // read ADB
if (m_adb_datasize > 0)
{
m_adb_datasize = 1; // hack
m_pm_out[0] = m_pm_out[1] = 3 + m_adb_datasize;
m_pm_out[2] = 0;
// m_pm_out[3] = m_pmu_last_adb_command;
m_pm_out[3] = 0;
m_pm_out[4] = m_adb_datasize;
for (int i = 0; i < m_adb_datasize; i++)
{
m_pm_out[5+i] = 0; //mac->m_adb_buffer[i];
}
m_pm_slen = 5 + m_adb_datasize;
}
else
{
m_pm_out[0] = m_pm_out[1] = 4;
m_pm_out[2] = 0;
m_pm_out[3] = 0;
// m_pm_out[3] = m_pmu_last_adb_command;
m_pm_out[4] = 1; // length of following data
m_pm_out[5] = 0;
m_pm_slen = 6;
}
/* printf("ADB packet: ");
for (int i = 0; i < m_pm_slen; i++)
{
printf("%02x ", m_pm_out[i]);
}
printf("\n");*/
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(1000)));
break;
case 0x31: // write first 20 bytes of PRAM
{
for (int i = 0; i < 20; i++)
{
m_adb_pram[i] = m_pm_cmd[1+i];
}
}
break;
case 0x32: // write extended PRAM byte(s). cmd[2] = address, cmd[3] = length, cmd[4...] = data
if ((m_pm_cmd[2] + m_pm_cmd[3]) < 0x100)
{
int i;
for (i = 0; i < m_pm_cmd[3]; i++)
{
m_adb_pram[m_pm_cmd[2] + i] = m_pm_cmd[4+i];
}
}
break;
case 0x38: // read time
{
m_pm_out[0] = m_pm_out[1] = 4;
m_pm_out[2] = 0x63; // famous Mac RTC value of 8/27/56 8:35:00 PM
m_pm_out[3] = 0x0b;
m_pm_out[4] = 0xd1;
m_pm_out[5] = 0x78;
m_pm_slen = 6;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
}
break;
case 0x39: // read first 20 bytes of PRAM
{
int i;
m_pm_out[0] = m_pm_out[1] = 20;
for (i = 0; i < 20; i++)
{
m_pm_out[2 + i] = m_adb_pram[i];
}
m_pm_slen = 22;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
}
break;
case 0x3a: // read extended PRAM byte(s). cmd[2] = address, cmd[3] = length
if ((m_pm_cmd[2] + m_pm_cmd[3]) < 0x100)
{
int i;
m_pm_out[0] = m_pm_out[1] = m_pm_cmd[3];
for (i = 0; i < m_pm_cmd[3]; i++)
{
m_pm_out[2 + i] = m_adb_pram[m_pm_cmd[2] + i];
}
m_pm_slen = m_pm_out[0] + 2;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
}
break;
case 0x40: // set screen contrast
break;
case 0x41:
break;
case 0x58: // read internal modem status
pmu_one_byte_reply(0);
break;
case 0x60: // set low power warning and cutoff battery levels
break;
case 0x68: // read battery/charger level
pmu_three_byte_reply(255, 255, 255);
break;
case 0x69: // read battery/charger instantaneous level and status
pmu_three_byte_reply(255, 255, 255);
break;
case 0x6b: // read extended battery/charger level and status (wants an 8 byte reply)
m_pm_out[0] = m_pm_out[1] = 8; // length
m_pm_out[2] = 255;
m_pm_out[3] = 255;
m_pm_out[4] = 255;
m_pm_out[5] = 255;
m_pm_out[6] = 255;
m_pm_out[7] = 255;
m_pm_out[8] = 255;
m_pm_out[9] = 255;
m_pm_slen = 10;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
break;
case 0x6c: // read battery ID
pmu_one_byte_reply(1);
break;
case 0x78: // read interrupt flag
if (ADB_IS_PM_VIA2_CLASS) // PB 140/170 use a "leaner" PMU protocol where you get the data for a PMU interrupt here
{
#if 0
if ((m_pmu_int_status&0xf0) == 0x10)
{
if (m_adb_datasize > 0)
{
m_adb_datasize = 1; // hack
m_pm_out[0] = m_pm_out[1] = 2 + m_adb_datasize;
m_pm_out[2] = m_pmu_int_status; // ADB status in low nibble
m_pm_out[3] = m_pmu_last_adb_command; // ADB command that was sent
for (int i = 0; i < m_adb_datasize; i++)
{
m_pm_out[4+i] = 0; //m_adb_buffer[i];
}
m_pm_slen = 4 + m_adb_datasize;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(1500)));
/* printf("ADB packet: ");
for (int i = 0; i < m_pm_slen; i++)
{
printf("%02x ", m_pm_out[i]);
}
printf("\n");*/
}
else
{
m_pm_out[0] = m_pm_out[1] = 2;
m_pm_out[2] = m_pmu_int_status; // ADB status in low nibble
m_pm_out[3] = m_pmu_last_adb_command; // ADB command that was sent OR 0x80 for extra error-ness
m_pm_out[4] = 0; // return data
m_pm_slen = 4;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(1500)));
}
}
else
{
pmu_one_byte_reply(mac, m_pmu_int_status);
}
#else
if ((m_pmu_int_status&0xf0) == 0x10)
{
m_pm_out[0] = m_pm_out[1] = 2;
m_pm_out[2] = m_pmu_int_status; // ADB status in low nibble
m_pm_out[3] = m_pmu_last_adb_command; // ADB command that was sent OR 0x80 for extra error-ness
m_pm_out[4] = 0; // return data
m_pm_slen = 4;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(1500)));
}
else
{
pmu_one_byte_reply(m_pmu_int_status);
}
#endif
}
else
{
pmu_one_byte_reply(m_pmu_int_status);
}
m_pmu_int_status = 0;
break;
case 0x90: // sound power control
break;
case 0x98: // read sound power state
pmu_one_byte_reply(1);
break;
case 0xd8: // read A/D converter (not sure what this does)
pmu_one_byte_reply(0);
break;
case 0xe0: // write PMU internal RAM
break;
case 0xe8: // read PMU internal RAM (just return zeroes)
{
int i;
m_pm_out[0] = m_pm_out[1] = m_pm_cmd[4];
// printf("PMU read at %x\n", m_pm_cmd[2] | (m_pm_cmd[3]<<8));
// note: read at 0xEE00 0 = target disk mode, 0xff = normal bootup
// (actually 0x00EE, the 50753 port 6)
for (i = 0; i < m_pm_cmd[4]; i++)
{
m_pm_out[2 + i] = 0xff;
}
m_pm_slen = m_pm_out[0] + 2;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
}
break;
case 0xec: // PMU self-test (send 1 count byte + reply)
pmu_one_byte_reply(0);
break;
default:
fatalerror("PMU: Unhandled command %02x\n", m_pm_cmd[0]);
}
if (m_pm_slen > 0)
{
m_pm_state = 1;
}
}
void mac_state::adb_vblank()
{
if ((m_adb_state == ADB_STATE_IDLE) || ((ADB_IS_PM_CLASS) && (m_pmu_poll)))
{
if (this->adb_pollmouse())
{
// if the mouse was the last TALK, we can just send the new data
// otherwise we need to pull SRQ
if ((m_adb_last_talk == m_adb_mouseaddr) && !(ADB_IS_PM_CLASS))
{
// repeat last TALK to get updated data
m_adb_waiting_cmd = 1;
this->adb_talk();
m_adb_timer_ticks = 8;
this->m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(100)));
}
#if 0
else if (ADB_IS_PM_CLASS)
{
m_adb_waiting_cmd = 1;
this->adb_talk();
m_pm_state = 10;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
if (ADB_IS_PM_VIA1_CLASS)
{
m_pmu_int_status = 0x1;
}
else if (ADB_IS_PM_VIA2_CLASS)
{
m_pmu_int_status = 0x10;
}
}
#endif
else
{
m_adb_irq_pending = 1;
m_adb_command = m_adb_send = 0;
m_adb_timer_ticks = 1; // one tick should be sufficient to make it see the IRQ
this->m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(100)));
m_adb_srq_switch = 1;
}
}
else if (this->adb_pollkbd(0))
{
if ((m_adb_last_talk == m_adb_keybaddr) && !(ADB_IS_PM_CLASS))
{
// repeat last TALK to get updated data
m_adb_waiting_cmd = 1;
this->adb_talk();
m_adb_timer_ticks = 8;
this->m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(100)));
}
#if 0
else if (ADB_IS_PM_CLASS)
{
m_adb_waiting_cmd = 1;
this->adb_talk();
m_pm_state = 10;
m_pmu_send_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(200)));
m_pmu_int_status = 0x1;
}
#endif
else
{
m_adb_irq_pending = 1;
m_adb_command = m_adb_send = 0;
m_adb_timer_ticks = 1; // one tick should be sufficient to make it see the IRQ
this->m_adb_timer->adjust(attotime(0, ATTOSECONDS_IN_USEC(100)));
m_adb_srq_switch = 1;
}
}
}
}
void mac_state::adb_reset()
{
int i;
m_adb_srq_switch = 0;
m_adb_irq_pending = 0; // no interrupt
m_adb_timer_ticks = 0;
m_adb_command = 0;
m_adb_extclock = 0;
m_adb_send = 0;
m_adb_waiting_cmd = 0;
m_adb_state = 0;
m_adb_srqflag = false;
m_pmu_poll = 0;
if (ADB_IS_BITBANG_CLASS)
{
m_adb_state = ADB_STATE_NOTINIT;
}
m_adb_direction = 0;
m_adb_datasize = 0;
m_adb_last_talk = -1;
m_adb_linestate = 0;
// mouse
m_adb_mouseaddr = 3;
m_adb_lastmousex = m_adb_lastmousey = m_adb_lastbutton = 0;
m_adb_mouse_initialized = 0;
// keyboard
m_adb_keybaddr = 2;
m_adb_keybinitialized = 0;
m_adb_currentkeys[0] = m_adb_currentkeys[1] = 0xff;
m_adb_modifiers = 0xff;
for (i=0; i<7; i++)
{
m_key_matrix[i] = 0;
}
m_adb_keybuf_start = 0;
m_adb_keybuf_end = 0;
}
WRITE_LINE_MEMBER(mac_state::adb_linechange_w)
{
int dtime = 0;
/* static char const *const states[] =
{
"idle",
"attention",
"bit0",
"bit1",
"bit2",
"bit3",
"bit4",
"bit5",
"bit6",
"bit7",
"tstop",
"waitt1t",
"rcvstartbit",
"srqnodata"
};*/
if (ADB_IS_EGRET)
{
dtime = m_egret->get_adb_dtime();
}
else if (ADB_IS_CUDA)
{
dtime = m_cuda->get_adb_dtime();
}
/* if (m_adb_linestate <= 12)
{
printf("linechange: %d -> %d, time %d (state %d = %s)\n", state^1, state, dtime, m_adb_linestate, states[m_adb_linestate]);
}
else
{
printf("linechange: %d -> %d, time %d (state %d)\n", state^1, state, dtime, m_adb_linestate);
}*/
if ((m_adb_direction) && (m_adb_linestate == LST_TSTOP))
{
if (m_adb_stream_ptr & 1) // odd byte, can't end here
{
// printf("critical linechange: odd, cont\n");
m_adb_linestate = LST_BIT0;
}
else
{
if (dtime < 90)
{
// printf("critical linechange: even, and it's another bit\n");
m_adb_linestate = LST_BIT0;
}
}
}
switch (m_adb_linestate)
{
case LST_IDLE:
if ((state) && (dtime >= 4500)) // reset
{
// printf("ADB RESET\n");
}
else if ((state) && (dtime >= 1200)) // attention
{
// printf("ADB ATTENTION\n");
m_adb_waiting_cmd = 1;
m_adb_direction = 0;
m_adb_linestate++;
}
break;
case LST_ATTENTION:
if ((!state) && (dtime >= 90)) // Tsync
{
// printf("ADB Tsync\n");
m_adb_command = 0;
m_adb_linestate++;
}
break;
case LST_BIT0:
case LST_BIT1:
case LST_BIT2:
case LST_BIT3:
case LST_BIT4:
case LST_BIT5:
case LST_BIT6:
case LST_BIT7:
if (!state)
{
if (dtime >= 90) // "1" bit
{
m_adb_command |= 1;
}
// printf("ADB bit %d\n", m_adb_command & 1);
if (m_adb_linestate != LST_BIT7)
{
m_adb_command <<= 1;
}
else
{
if (m_adb_direction)
{
// printf("listen byte[%d] = %02x\n", m_adb_stream_ptr, m_adb_command);
m_adb_buffer[m_adb_stream_ptr++] = m_adb_command;
m_adb_command = 0;
}
}
m_adb_linestate++;
}
break;
case LST_TSTOP:
if (state)
{
// printf("ADB TSTOP, command byte %02x\n", m_adb_command);
if (m_adb_direction)
{
m_adb_command = m_adb_buffer[0];
}
m_adb_srqflag = false;
adb_talk();
if (!m_adb_srqflag)
{
set_adb_line(ASSERT_LINE);
}
else
{
set_adb_line(CLEAR_LINE);
}
if (m_adb_datasize > 0)
{
/* printf("Device has %d bytes of data: ", m_adb_datasize);
for (int i = 0; i < m_adb_datasize; i++)
{
printf("%02x ", m_adb_buffer[i]);
}*/
m_adb_linestate = LST_TSTOPSTART; // T1t
m_adb_timer->adjust(attotime::from_ticks(324/4, 1000000));
m_adb_stream_ptr = 0;
}
else if (m_adb_direction) // if direction is set, we LISTENed to a valid device
{
m_adb_linestate = LST_WAITT1T;
}
else // no valid device targetted, time out
{
if (m_adb_srqflag)
{
m_adb_linestate = LST_SRQNODATA;
m_adb_timer->adjust(attotime::from_ticks(486, 1000000)); // SRQ time
}
else
{
m_adb_linestate = LST_IDLE;
}
}
}
break;
case LST_WAITT1T:
if ((!state) && (dtime >= 300)) // T1t
{
// printf("ADB T1t\n");
m_adb_linestate++;
}
break;
case LST_RCVSTARTBIT:
if ((!state) && (dtime >= 90)) // start
{
// printf("ADB start\n");
m_adb_linestate = LST_BIT0;
m_adb_command = 0;
}
break;
}
}
void mac_state::set_adb_line(int linestate)
{
if (ADB_IS_EGRET)
{
m_egret->set_adb_line(linestate);
}
else if (ADB_IS_CUDA)
{
m_cuda->set_adb_line(linestate);
}
}