uvmac/src/ADBEMDEV.c

215 lines
5.2 KiB
C

/*
ADBEMDEV.c
Copyright (C) 2008 Paul C. Pratt
You can redistribute this file and/or modify it under the terms
of version 2 of the GNU General Public License as published by
the Free Software Foundation. You should have received a copy
of the license along with this file; see the file COPYING.
This file is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
license for more details.
*/
/*
Apple Desktop Bus EMulated DEVice
*/
#ifndef AllFiles
#include "SYSDEPNS.h"
#include "UI/MYOSGLUE.h"
#include "EMCONFIG.h"
#include "GLOBGLUE.h"
#endif
#include "ADBEMDEV.h"
#ifdef _VIA_Debug
#include <stdio.h>
#endif
/*
ReportAbnormalID unused 0x0C06 - 0x0CFF
*/
IMPORTPROC ADB_ShiftOutData(uint8_t v);
IMPORTFUNC uint8_t ADB_ShiftInData(void);
#include "ADBSHARE.h"
LOCALVAR bool ADB_ListenDatBuf;
LOCALVAR uint8_t ADB_IndexDatBuf;
GLOBALPROC ADB_DoNewState(void)
{
uint8_t state = ADB_st1 * 2 + ADB_st0;
#ifdef _VIA_Debug
fprintf(stderr, "ADB_DoNewState: %d\n", state);
#endif
{
ADB_Int = 1;
switch (state) {
case 0: /* Start a new command */
if (ADB_ListenDatBuf) {
ADB_ListenDatBuf = false;
ADB_SzDatBuf = ADB_IndexDatBuf;
ADB_EndListen();
}
ADB_TalkDatBuf = false;
ADB_IndexDatBuf = 0;
ADB_CurCmd = ADB_ShiftInData();
/* which sets interrupt, acknowleding command */
#ifdef _VIA_Debug
fprintf(stderr, "in: %d\n", ADB_CurCmd);
#endif
switch ((ADB_CurCmd >> 2) & 3) {
case 0: /* reserved */
switch (ADB_CurCmd & 3) {
case 0: /* Send Reset */
ADB_DoReset();
break;
case 1: /* Flush */
ADB_Flush();
break;
case 2: /* reserved */
case 3: /* reserved */
ReportAbnormalID(0x0C01,
"Reserved ADB command");
break;
}
break;
case 1: /* reserved */
ReportAbnormalID(0x0C02,
"Reserved ADB command");
break;
case 2: /* listen */
ADB_ListenDatBuf = true;
#ifdef _VIA_Debug
fprintf(stderr, "*** listening\n");
#endif
break;
case 3: /* talk */
ADB_DoTalk();
break;
}
break;
case 1: /* Transfer date byte (even) */
case 2: /* Transfer date byte (odd) */
if (! ADB_ListenDatBuf) {
/*
will get here even if no pending talk data,
when there is pending event from device
other than the one polled by the last talk
command. this probably indicates a bug.
*/
if ((! ADB_TalkDatBuf)
|| (ADB_IndexDatBuf >= ADB_SzDatBuf))
{
ADB_ShiftOutData(0xFF);
ADB_Data = 1;
ADB_Int = 0;
} else {
#ifdef _VIA_Debug
fprintf(stderr, "*** talk one\n");
#endif
ADB_ShiftOutData(ADB_DatBuf[ADB_IndexDatBuf]);
ADB_Data = 1;
ADB_IndexDatBuf += 1;
}
} else {
if (ADB_IndexDatBuf >= ADB_MaxSzDatBuf) {
ReportAbnormalID(0x0C03, "ADB listen too much");
/* ADB_MaxSzDatBuf isn't big enough */
(void) ADB_ShiftInData();
} else {
#ifdef _VIA_Debug
fprintf(stderr, "*** listen one\n");
#endif
ADB_DatBuf[ADB_IndexDatBuf] = ADB_ShiftInData();
ADB_IndexDatBuf += 1;
}
}
break;
case 3: /* idle */
if (ADB_ListenDatBuf) {
ReportAbnormalID(0x0C04, "ADB idle follows listen");
/* apparently doesn't happen */
}
if (ADB_TalkDatBuf) {
if (ADB_IndexDatBuf != 0) {
ReportAbnormalID(0x0C05,
"idle when not done talking");
}
ADB_ShiftOutData(0xFF);
/* ADB_Int = 0; */
} else if (CheckForADBanyEvt()) {
if (((ADB_CurCmd >> 2) & 3) == 3) {
ADB_DoTalk();
}
ADB_ShiftOutData(0xFF);
/* ADB_Int = 0; */
}
break;
}
}
}
GLOBALPROC ADBstate_ChangeNtfy(void)
{
#ifdef _VIA_Debug
fprintf(stderr, "ADBstate_ChangeNtfy: %d, %d, %d\n",
ADB_st1, ADB_st0, GetCuriCount());
#endif
ICT_add(kICT_ADB_NewState,
348160UL * kCycleScale / 64 * ClockMult);
/*
Macintosh Family Hardware Reference say device "must respond
to talk command within 260 microseconds", which translates
to about 190 instructions. But haven't seen much problems
even for very large values (tens of thousands), and do see
problems for small values. 50 is definitely too small,
mouse doesn't move smoothly. There may still be some
signs of this problem with 150.
On the other hand, how fast the device must respond may
not be related to how fast the ADB transceiver responds.
*/
}
GLOBALPROC ADB_DataLineChngNtfy(void)
{
#ifdef _VIA_Debug
fprintf(stderr, "ADB_DataLineChngNtfy: %d\n", ADB_Data);
#endif
}
GLOBALPROC ADB_Update(void)
{
uint8_t state = ADB_st1 * 2 + ADB_st0;
if (state == 3) { /* idle */
if (ADB_TalkDatBuf) {
/* ignore, presumably being taken care of */
} else if (CheckForADBanyEvt())
{
if (((ADB_CurCmd >> 2) & 3) == 3) {
ADB_DoTalk();
}
ADB_ShiftOutData(0xFF);
/*
Wouldn't expect this would be needed unless
there is actually talk data. But without it,
ADB never polls the other devices. Clearing
ADB_Int has no effect.
*/
/*
ADB_Int = 0;
seems to have no effect, which probably indicates a bug
*/
}
}
}