1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-11 04:28:58 +00:00
CLK/Components/DiskII/IWM.cpp
Thomas Harte 723137c0d4 With some time additions to the 6522, starts wiring in Macintosh audio.
The audio buffer is also the disk motor buffer, so this is preparatory to further disk work.
2019-06-01 14:39:40 -04:00

205 lines
5.2 KiB
C++

//
// IWM.cpp
// Clock Signal
//
// Created by Thomas Harte on 05/05/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#include "IWM.hpp"
#include <cstdio>
using namespace Apple;
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);
// 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 understanding:
//
// Q6 = 1, Q7 = 0 reads the status register. The meaning of the top 'SENSE' bit is then determined by
// the CA0,1,2 and SEL switches as described in Inside Macintosh, summarised above as RD/SENSE.
if(address&1) {
return 0xff;
}
switch(state_ & (Q6 | Q7 | ENABLE)) {
default:
printf("Invalid read\n");
return 0xff;
// "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 = 0x80;
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");
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);
switch(state_ & (Q6 | Q7 | ENABLE)) {
default: break;
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 Q7|Q6|ENABLE: // Write data register.
printf("Data register write\n");
break;
}
}
// MARK: - Switch access
void IWM::access(int address) {
// 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);
if(address & 1) {
state_ |= mask;
} else {
state_ &= ~mask;
}
}
void IWM::set_select(bool 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) {
}