// 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 // 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 (;;) { } }