joeylib2/examples/sccprobe/sccprobe.c

118 lines
4 KiB
C

// Does opening one built-in SCC port survive the OTHER one?
//
// The Zilog 8530 in a IIgs carries both built-in ports: channel A is the
// printer port (where AppleTalk lives), channel B is the modem port. WR9's
// reset command is chip-wide, so an open that issues the force-hardware-reset
// (0xC0) takes the other channel down with it -- registers, FIFOs, baud
// generator and, decisively for this probe, the transmit enable in WR5.
//
// So: bring channel A up through the real HAL, hand it back (the IIgs close
// deliberately leaves the chip configured), then open channel B -- the
// operation under test -- and finally poke a byte STRAIGHT into channel A's
// data register, bypassing the library. If channel A survived, its transmitter
// is still enabled and the byte reaches the host peer. If the open reset the
// whole chip, WR5 went to zero, the transmitter is off, and nothing arrives.
//
// Run headless by scripts/verify-iigs-serial.sh, which reads the bytes off a
// socket and the progress flags out of the SHR framebuffer.
#include <joey/serial.h>
// SHR framebuffer, used purely as a mailbox the emulator harness can read: the
// probe never calls jlInit, so nothing else is drawing here.
#define SHR_BASE ((volatile uint8_t *)0x00E12000L)
#define MB_SIGNATURE_0 0
#define MB_SIGNATURE_1 1
#define MB_PRINTER_OPEN 2
#define MB_PRINTER_WROTE 3
#define MB_MODEM_OPEN 4
#define MB_DIRECT_WROTE 5
#define MB_SIG_VALUE_0 0xA5u
#define MB_SIG_VALUE_1 0x5Au
// Channel A (printer port) registers -- the channel this probe checks for
// survival. Deliberately spelled out here rather than shared with the HAL: the
// point is to look at the hardware independently of the code under test.
#define SCC_A_CTRL ((volatile uint8_t *)0x00C039L)
#define SCC_A_DATA ((volatile uint8_t *)0x00C03BL)
#define SCC_RR0_TX_EMPTY 0x04u
// Bounded so a dead transmitter reports "nothing arrived" instead of hanging
// the machine and stalling the gate.
#define TX_SPIN_LIMIT 200000ul
static void mailbox(uint16_t slot, uint8_t value);
static bool pokeChannelA(uint8_t byte);
static void mailbox(uint16_t slot, uint8_t value) {
SHR_BASE[slot] = value;
}
// Write one byte to channel A without going through the HAL. Returns false if
// the transmitter never reports empty, which is exactly what a wiped channel
// looks like.
static bool pokeChannelA(uint8_t byte) {
uint32_t spin;
for (spin = 0; spin < TX_SPIN_LIMIT; spin++) {
if ((*SCC_A_CTRL & SCC_RR0_TX_EMPTY) != 0u) {
*SCC_A_DATA = byte;
return true;
}
}
return false;
}
int main(void) {
jlSerialConfigT cfg;
uint16_t i;
mailbox(MB_SIGNATURE_0, MB_SIG_VALUE_0);
mailbox(MB_SIGNATURE_1, MB_SIG_VALUE_1);
cfg.baud = 9600u;
cfg.dataBits = 8u;
cfg.stopBits = 1u;
cfg.parity = JL_SERIAL_PARITY_NONE;
cfg.flow = JL_SERIAL_FLOW_NONE;
cfg.unit = 0u;
// 1. Bring channel A (printer) up through the HAL and prove it transmits.
if (!jlSerialOpen(JL_SERIAL_PRINTER, &cfg)) {
return 1;
}
mailbox(MB_PRINTER_OPEN, 1u);
if (jlSerialWrite((const uint8_t *)"A1\r\n", 4u) == 4u) {
mailbox(MB_PRINTER_WROTE, 1u);
}
jlSerialFlush();
jlSerialClose();
// 2. Open channel B (modem). THIS is the operation under test: with the
// old chip-wide 0xC0 reset it also wipes channel A.
if (!jlSerialOpen(JL_SERIAL_MODEM, &cfg)) {
return 1;
}
mailbox(MB_MODEM_OPEN, 1u);
// 3. Channel A is untouched by the library from here on. Poke it directly:
// the byte only leaves the chip if channel A's transmitter is still on.
if (pokeChannelA('A')) {
mailbox(MB_DIRECT_WROTE, 1u);
}
(void)pokeChannelA('2');
(void)pokeChannelA('\r');
(void)pokeChannelA('\n');
// Hold the machine still so the harness can read the mailbox, and give the
// last byte time to clock out at 9600 baud.
for (i = 0; i < 60000u; i++) {
(void)*SCC_A_CTRL;
}
jlSerialClose();
for (;;) {
}
}