From 4197c6f149e4d67ce9ad8a0c23eec6572d52416a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 30 May 2019 22:17:49 -0400 Subject: [PATCH] Attempts to make some further semantic sense of the various IWM controls. --- Components/DiskII/IWM.cpp | 191 +++++++++++++++++++++++++++++++------- Components/DiskII/IWM.hpp | 2 +- 2 files changed, 160 insertions(+), 33 deletions(-) diff --git a/Components/DiskII/IWM.cpp b/Components/DiskII/IWM.cpp index 125d8b22e..8b1cfbe0e 100644 --- a/Components/DiskII/IWM.cpp +++ b/Components/DiskII/IWM.cpp @@ -12,64 +12,191 @@ using namespace Apple; -IWM::IWM(int clock_rate) { - +namespace { + const int CA0 = 1 << 0; + const int CA1 = 1 << 1; + const int CA2 = 1 << 2; + const int LSTRB = 1 << 3; + const int ENABLE = 1 << 4; + const int DRIVESEL = 1 << 5; /* This means drive select, like on the original Disk II. */ + const int Q6 = 1 << 6; + const int Q7 = 1 << 7; + const int SEL = 1 << 8; /* This is an additional input, not available on a Disk II, with a confusingly-similar name to SELECT but a distinct purpose. */ } +IWM::IWM(int clock_rate) {} + +// MARK: - Bus accessors + uint8_t IWM::read(int address) { access(address); - printf("IWM r %d (%02x)\n", address&0xf, q_switches_); - switch(q_switches_) { - default: return 0x00; // Undefined. + // Per Inside Macintosh: + // + // "Before you can read from any of the disk registers you must set up the state of the IWM so that it + // can pass the data through to the MC68000's address space where you'll be able to read it. To do that, + // you must first turn off Q7 by reading or writing dBase+q7L. Then turn on Q6 by accessing dBase+q6H. + // After that, the IWM will be able to pass data from the disk's RD/SENSE line through to you." + // + // My thoughts: I'm unclear on how passing data from RD/SENSE is conflated with reading "any of the disk + // registers", but the text reads as though the access happens internally but isn't passed on in the case + // of Q6 and Q7 being in the incorrect state. - case 0x20: return 0x00; // Data register. + printf("Read %d: ", address&1); - case 0x40: - case 0x60: - return (mode_&0x1f); // Status register. + switch(state_ & (Q6 | Q7 | ENABLE)) { + default: + printf("Invalid read\n"); + return 0xff; - case 0x80: - case 0xa0: - return 0x80; // Handshake register. + // "Read all 1s". + case 0: + printf("Reading all 1s\n"); + return 0xff; + + case ENABLE: /* Read data register. */ + printf("Reading data register\n"); + return 0x00; + + case Q6: case Q6|ENABLE: { + /* + [If A = 0], Read status register: + + bits 0-3: same as mode register. + bit 5: 1 = either /ENBL1 or /ENBL2 is currently low. + bit 6: 1 = MZ (reserved for future compatibility; should always be read as 0). + bit 7: 1 = SENSE input high; 0 = SENSE input low. + + (/ENBL1 is low when the first drive's motor is on; /ENBL2 is low when the second drive's motor is on. + If the 1-second timer is enabled, motors remain on for one second after being programmatically disabled.) + */ + printf("Reading status ([%d] including ", (state_&DRIVESEL) ? 2 : 1); + + // Determine the SENSE input. + uint8_t sense = 0; + switch(state_ & (CA2 | CA1 | CA0 | SEL)) { + default: + printf("unknown)\n"); + break; + + case 0: // Head step direction. + printf("head step direction)\n"); + break; + + case SEL: // Disk in place. + printf("disk in place)\n"); + sense = 0x80; + break; + + case CA0: // Disk head stepping. + printf("head stepping)\n"); + break; + + case CA0|SEL: // Disk locked (i.e. write-protect tab). + printf("disk locked)\n"); + break; + + case CA1: // Disk motor running. + printf("disk motor running)\n"); + break; + + case CA1|SEL: // Head at track 0. + printf("head at track 0)\n"); + break; + + case CA1|CA0|SEL: // Tachometer (?) + printf("tachometer)\n"); + break; + + case CA2: // Read data, lower head. + printf("data, lower head)\n"); + break; + + case CA2|SEL: // Read data, upper head. + printf("data, upper head)\n"); + break; + + case CA2|CA1: // Single- or double-sided drive. + printf("single- or double-sided drive)\n"); + break; + + case CA2|CA1|CA0|SEL: // Drive installed. + printf("drive installed)\n"); + break; + } + + return (mode_&0x1f) | sense; + } break; + + case Q7: case Q7|ENABLE: + /* + Read write-handshake register: + + bits 0-5: reserved for future use (currently read as 1). + bit 6: 1 = write state (cleared to 0 if a write underrun occurs). + bit 7: 1 = write data buffer ready for data. + */ + printf("Reading write handshake\n"); + return 0x1f; } + + return 0xff; } void IWM::write(int address, uint8_t input) { access(address); - printf("IWM w %d (%02x)\n", address&0xf); - switch(q_switches_) { - default: - break; + switch(state_ & (Q6 | Q7 | ENABLE)) { + default: break; - case 0xc0: // Write mode register. + case Q7|Q6: + /* + Write mode register: + + bit 0: 1 = latch mode (should be set in asynchronous mode). + bit 1: 0 = synchronous handshake protocol; 1 = asynchronous. + bit 2: 0 = 1-second on-board timer enable; 1 = timer disable. + bit 3: 0 = slow mode; 1 = fast mode. + bit 4: 0 = 7Mhz; 1 = 8Mhz (7 or 8 mHz clock descriptor). + bit 5: 1 = test mode; 0 = normal operation. + bit 6: 1 = MZ-reset. + bit 7: reserved for future expansion. + */ mode_ = input; + printf("IWM mode is now %02x\n", mode_); break; - case 0xd0: // Write data register. + case Q7|Q6|ENABLE: // Write data register. + printf("Data register write\n"); break; - } } +// MARK: - Switch access + void IWM::access(int address) { - switch(address & 0xf) { - default: - break; + // Keep a record of switch state; bits in state_ + // should correlate with the anonymous namespace constants + // defined at the top of this file — CA0, CA1, etc. + address &= 0xf; + const auto mask = 1 << (address >> 1); - case 0x8: q_switches_ &= ~0x20; break; - case 0x9: q_switches_ |= 0x20; break; - case 0xc: q_switches_ &= ~0x40; break; - case 0xd: q_switches_ |= 0x40; break; - case 0xe: q_switches_ &= ~0x80; break; - case 0xf: q_switches_ |= 0x80; break; + if(address & 1) { + state_ |= mask; + } else { + state_ &= ~mask; } } -void IWM::run_for(const Cycles cycles) { -} - void IWM::set_select(bool enabled) { - printf("IWM s %d\n", int(enabled)); + // Augment switch state with the value of the SEL line; + // it's active low, which is implicitly inverted here for + // consistency in the meaning of state_ bits. + if(!enabled) state_ |= 0x100; + else state_ &= ~0x100; +} + +// MARK: - Active logic + +void IWM::run_for(const Cycles cycles) { } diff --git a/Components/DiskII/IWM.hpp b/Components/DiskII/IWM.hpp index 03dde9ca9..df15b00c2 100644 --- a/Components/DiskII/IWM.hpp +++ b/Components/DiskII/IWM.hpp @@ -42,7 +42,7 @@ class IWM { bool read_write_ready_ = true; bool write_overran_ = false; - int q_switches_ = 0; + int state_ = 0; void access(int address); };