diff --git a/Components/8530/z8530.cpp b/Components/8530/z8530.cpp index 4d52363e0..d758fc84b 100644 --- a/Components/8530/z8530.cpp +++ b/Components/8530/z8530.cpp @@ -17,14 +17,57 @@ void z8530::reset() { } bool z8530::get_interrupt_line() { - // TODO. - return false; + return + (master_interrupt_control_ & 0x8) && + ( + channels_[0].get_interrupt_line() || + channels_[1].get_interrupt_line() + ); } std::uint8_t z8530::read(int address) { - const auto result = channels_[address & 1].read(address & 2, pointer_); - pointer_ = 0; - return result; + if(address & 2) { + // Read data register for channel + return 0x00; + } else { + // Read control register for channel. + uint8_t result = 0; + + switch(pointer_) { + default: + result = channels_[address & 1].read(address & 2, pointer_); + break; + + case 2: // Handled non-symmetrically between channels. + if(address & 1) { + LOG("[SCC] Unimplemented: register 2 status bits"); + } else { + result = interrupt_vector_; + + // Modify the vector if permitted. +// if(master_interrupt_control_ & 1) { + for(int port = 0; port < 2; ++port) { + // TODO: the logic below assumes that DCD is the only implemented interrupt. Fix. + if(channels_[port].get_interrupt_line()) { + const uint8_t shift = 1 + 3*((master_interrupt_control_ & 0x10) >> 4); + const uint8_t mask = uint8_t(~(7 << shift)); + result = uint8_t( + (result & mask) | + ((1 | ((port == 1) ? 4 : 0)) << shift) + ); + break; + } + } +// } + } + break; + } + + pointer_ = 0; + return result; + } + + return 0x00; } void z8530::write(int address, std::uint8_t value) { @@ -47,6 +90,7 @@ void z8530::write(int address, std::uint8_t value) { case 9: // Master interrupt and reset register; also shared between both channels. LOG("[SCC] TODO: master interrupt and reset register " << PADHEX(2) << int(value)); + master_interrupt_control_ = value; break; } @@ -68,15 +112,31 @@ void z8530::write(int address, std::uint8_t value) { } } +void z8530::set_dcd(int port, bool level) { + channels_[port].set_dcd(level); +} + +// MARK: - Channel implementations + uint8_t z8530::Channel::read(bool data, uint8_t pointer) { // If this is a data read, just return it. if(data) { return data_; } else { - LOG("[SCC] Unrecognised control read from register " << int(pointer)); + // Otherwise, this is a control read... + switch(pointer) { + default: + LOG("[SCC] Unrecognised control read from register " << int(pointer)); + return 0x00; + + case 0: + return dcd_ ? 0x8 : 0x0; + + case 0xf: + return external_interrupt_status_; + } } - // Otherwise, this is a control read... return 0x00; } @@ -109,7 +169,9 @@ void z8530::Channel::write(bool data, uint8_t pointer, uint8_t value) { switch((value >> 3)&7) { default: /* Do nothing. */ break; case 2: - LOG("[SCC] TODO: reset ext/status interrupts."); +// LOG("[SCC] reset ext/status interrupts."); + external_status_interrupt_ = false; + external_interrupt_status_ = 0; break; case 3: LOG("[SCC] TODO: send abort (SDLC)."); @@ -130,7 +192,7 @@ void z8530::Channel::write(bool data, uint8_t pointer, uint8_t value) { break; case 0x1: // Write register 1 — Transmit/Receive Interrupt and Data Transfer Mode Definition. - transfer_interrupt_mask_ = value; + interrupt_mask_ = value; break; case 0x4: // Write register 4 — Transmit/Receive Miscellaneous Parameters and Modes. @@ -172,12 +234,24 @@ void z8530::Channel::write(bool data, uint8_t pointer, uint8_t value) { break; case 0xf: // Write register 15 — External/Status Interrupt Control. - interrupt_mask_ = value; + external_interrupt_mask_ = value; break; } } } -void z8530::set_dcd(int port, bool level) { - // TODO. +void z8530::Channel::set_dcd(bool level) { + if(dcd_ == level) return; + dcd_ = level; + + if(external_interrupt_mask_ & 0x8) { + external_status_interrupt_ = true; + external_interrupt_status_ |= 0x8; + } +} + +bool z8530::Channel::get_interrupt_line() { + return + (interrupt_mask_ & 1) && external_status_interrupt_; + // TODO: other potential causes of an interrupt. } diff --git a/Components/8530/z8530.hpp b/Components/8530/z8530.hpp index 991782652..01307dce5 100644 --- a/Components/8530/z8530.hpp +++ b/Components/8530/z8530.hpp @@ -45,6 +45,8 @@ class z8530 { public: uint8_t read(bool data, uint8_t pointer); void write(bool data, uint8_t pointer, uint8_t value); + void set_dcd(bool level); + bool get_interrupt_line(); private: uint8_t data_ = 0xff; @@ -63,13 +65,20 @@ class z8530 { int clock_rate_multiplier_ = 1; - uint8_t transfer_interrupt_mask_ = 0; // i.e. Write Register 0x1. - uint8_t interrupt_mask_ = 0; // i.e. Write Register 0xf. + uint8_t interrupt_mask_ = 0; // i.e. Write Register 0x1. + + uint8_t external_interrupt_mask_ = 0; // i.e. Write Register 0xf. + bool external_status_interrupt_ = false; + uint8_t external_interrupt_status_ = 0; bool dcd_ = false; } channels_[2]; + uint8_t pointer_ = 0; + uint8_t interrupt_vector_ = 0; + + uint8_t master_interrupt_control_ = 0; }; } diff --git a/Components/DiskII/IWM.cpp b/Components/DiskII/IWM.cpp index bf94fc118..3487cccf9 100644 --- a/Components/DiskII/IWM.cpp +++ b/Components/DiskII/IWM.cpp @@ -8,7 +8,7 @@ #include "IWM.hpp" -#include +#include "../../Outputs/Log.hpp" using namespace Apple; @@ -50,7 +50,7 @@ uint8_t IWM::read(int address) { switch(state_ & (Q6 | Q7 | ENABLE)) { default: - printf("Invalid read\n"); + LOG("[IWM] Invalid read\n"); return 0xff; // "Read all 1s". @@ -62,10 +62,9 @@ uint8_t IWM::read(int address) { const auto result = data_register_; if(data_register_ & 0x80) { - printf("[%02x] ", data_register_); data_register_ = 0; } - printf("Reading data register\n"); + LOG("Reading data register: " << PADHEX(2) << int(result)); return result; } @@ -82,13 +81,13 @@ uint8_t IWM::read(int address) { (/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 (including [%d][%c%c%c%c] ", active_drive_, (state_ & CA2) ? '2' : '-', (state_ & CA1) ? '1' : '-', (state_ & CA0) ? '0' : '-', (state_ & SEL) ? 'S' : '-'); + LOG("Reading status (including [" << active_drive_ << "][" << ((state_ & CA2) ? '2' : '-') << ((state_ & CA1) ? '1' : '-') << ((state_ & CA0) ? '0' : '-') << ((state_ & SEL) ? 'S' : '-') << "]"); // Determine the SENSE input. uint8_t sense = 0x00; switch(state_ & (CA2 | CA1 | CA0 | SEL)) { default: - printf("unknown)\n"); + LOG("unknown)"); break; // 4 = step finished (0 = done) @@ -102,7 +101,7 @@ uint8_t IWM::read(int address) { // break; case SEL: // Disk in place. - printf("disk in place)\n"); + LOG("disk in place)"); sense = drives_[active_drive_] && drives_[active_drive_]->has_disk() ? 0x00 : 0x80; break; @@ -111,24 +110,24 @@ uint8_t IWM::read(int address) { // break; // case CA0|SEL: // Disk locked (i.e. write-protect tab). - printf("disk locked)\n"); + LOG("disk locked)"); // sense = drives_[active_drive_] && drives_[active_drive_]->get_is_read_only() ? 0x00 : 0x80; sense = drives_[active_drive_] && drives_[active_drive_]->get_is_read_only() ? 0x80 : 0x00; break; case CA1: // Disk motor running. - printf("disk motor running)\n"); + LOG("disk motor running)"); sense = drives_[active_drive_] && drives_[active_drive_]->get_motor_on() ? 0x00 : 0x80; break; case CA1|SEL: // Head at track 0. - printf("head at track 0)\n"); + LOG("head at track 0)"); sense = drives_[active_drive_] && drives_[active_drive_]->get_is_track_zero() ? 0x00 : 0x80; break; case CA1|CA0|SEL: // Tachometer (?) sense = drives_[active_drive_] && drives_[active_drive_]->get_tachometer() ? 0x00 : 0x80; - printf("tachometer [%02x])\n", sense); + LOG("tachometer " << PADHEX(2) << int(sense) << ")"); break; // case CA2: // Read data, lower head. @@ -140,13 +139,13 @@ uint8_t IWM::read(int address) { // break; // case CA2|CA1: // Single- or double-sided drive. - printf("single- or double-sided drive)\n"); + LOG("single- or double-sided drive)"); sense = drives_[active_drive_] && (drives_[active_drive_]->get_head_count() == 1) ? 0x00 : 0x80; break; case CA2|CA1|CA0: // Drive installed. (per the Mac Plus ROM) case CA2|CA1|CA0|SEL: // Drive installed. (per Inside Macintosh) - printf("drive installed)\n"); + LOG("drive installed)"); sense = drives_[active_drive_] ? 0x00 : 0x80; break; } @@ -165,7 +164,7 @@ uint8_t IWM::read(int address) { 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"); + LOG("Reading write handshake"); return 0x1f | 0x80 | 0x40; } @@ -200,11 +199,11 @@ void IWM::write(int address, uint8_t input) { case 0x10: bit_length_ = Cycles(32); break; // slow mode, 8Mhz case 0x18: bit_length_ = Cycles(16); break; // fast mode, 8Mhz } - printf("IWM mode is now %02x\n", mode_); + LOG("IWM mode is now " << PADHEX(2) << int(mode_)); break; case Q7|Q6|ENABLE: // Write data register. - printf("Data register write\n"); + LOG("Data register write\n"); break; } } @@ -237,23 +236,23 @@ void IWM::access(int address) { if(!(address & 1)) { switch(state_ & (CA1 | CA0 | SEL)) { default: - printf("Unhandled LSTRB\n"); + LOG("Unhandled LSTRB"); break; case 0: - printf("LSTRB Set stepping direction: %d\n", state_ & CA2); + LOG("LSTRB Set stepping direction: " << int(state_ & CA2)); break; case CA0: - printf("LSTRB Step\n"); + LOG("LSTRB Step"); break; case CA1: - printf("LSTRB Motor on\n"); + LOG("LSTRB Motor on"); break; case CA1|CA0: - printf("LSTRB Eject disk\n"); + LOG("LSTRB Eject disk"); break; } } diff --git a/Inputs/QuadratureMouse/QuadratureMouse.hpp b/Inputs/QuadratureMouse/QuadratureMouse.hpp index ab6592bda..5ec839eb0 100644 --- a/Inputs/QuadratureMouse/QuadratureMouse.hpp +++ b/Inputs/QuadratureMouse/QuadratureMouse.hpp @@ -64,9 +64,9 @@ class QuadratureMouse: public Mouse { secondaries_[axis] = primaries_[axis]; if(axis_value > 0) { -- axes_[axis]; + secondaries_[axis] ^= 1; } else { ++ axes_[axis]; - secondaries_[axis] ^= 1; } } } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 0d0627c32..7b8a512ec 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -139,7 +139,7 @@ template class ConcreteMachin via_clock_ += cycle.length; via_.run_for(via_clock_.divide(HalfCycles(10))); - // The keyboard also has a clock, albeit a very slow one. + // The keyboard also has a clock, albeit a very slow one — 100,000 cycles/second. // Its clock and data lines are connected to the VIA. keyboard_clock_ += cycle.length; const auto keyboard_ticks = keyboard_clock_.divide(HalfCycles(CLOCK_RATE / 100000)); @@ -149,13 +149,13 @@ template class ConcreteMachin via_.set_control_line_input(MOS::MOS6522::Port::B, MOS::MOS6522::Line::One, keyboard_.get_clock()); } - // Feed mouse inputs within at most 100 cycles of each other. + // Feed mouse inputs within at most 1250 cycles of each other. time_since_mouse_update_ += cycle.length; - const auto mouse_ticks = keyboard_clock_.divide(HalfCycles(200)); + const auto mouse_ticks = time_since_mouse_update_.divide(HalfCycles(2500)); if(mouse_ticks > HalfCycles(0)) { mouse_.prepare_step(); - scc_.set_dcd(0, mouse_.get_channel(0) & 1); - scc_.set_dcd(1, mouse_.get_channel(1) & 1); + scc_.set_dcd(0, mouse_.get_channel(1) & 1); + scc_.set_dcd(1, mouse_.get_channel(0) & 1); } // TODO: SCC should be clocked at a divide-by-two, if and when it actually has @@ -176,11 +176,18 @@ template class ConcreteMachin via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !video_.vsync()); // Update interrupt input. TODO: move this into a VIA/etc delegate callback? - mc68000_.set_interrupt_level( - (via_.get_interrupt_line() ? 1 : 0) | - (scc_.get_interrupt_line() ? 2 : 0) - /* TODO: to emulate a programmer's switch: have it set bit 2 when pressed. */ - ); + if(scc_.get_interrupt_line()) { + mc68000_.set_interrupt_level(2); + } else if(via_.get_interrupt_line()) { + mc68000_.set_interrupt_level(1); + } else { + mc68000_.set_interrupt_level(0); + } +// mc68000_.set_interrupt_level( +// (via_.get_interrupt_line() ? 1 : 0) | +// (scc_.get_interrupt_line() ? 2 : 0) +// /* TODO: to emulate a programmer's switch: have it set bit 2 when pressed. */ +// ); // A null cycle leaves nothing else to do. if(cycle.operation) { @@ -444,12 +451,13 @@ template class ConcreteMachin return 0x00; // TODO: b7 = SCC wait/request case Port::B: - return + return uint8_t( (mouse_.get_button_mask() & 1) ? 0x00 : 0x08 | - ((mouse_.get_channel(1) & 2) << 3) | - ((mouse_.get_channel(0) & 2) << 4) | + ((mouse_.get_channel(0) & 2) << 3) | + ((mouse_.get_channel(1) & 2) << 4) | (clock_.get_data() ? 0x02 : 0x00) | - (video_.is_outputting() ? 0x00 : 0x40); + (video_.is_outputting() ? 0x00 : 0x40) + ); } } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 9c2dca9ce..cdac581d4 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -69,7 +69,7 @@ mouse_machine(); if(mouse_machine) { @synchronized(self) { - mouse_machine->get_mouse().move(int(deltaX), int(deltaY)); + mouse_machine->get_mouse().move(int(deltaX), -int(deltaY)); } } } diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 7d3cc5ef8..f09e3026d 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -218,7 +218,7 @@ template void Proces #ifdef LOG_TRACE // should_log = true; if(should_log) { - printf("\nInterrupt\n"); + printf("\n\nInterrupt\n\n"); } #endif active_program_ = nullptr; @@ -287,8 +287,9 @@ template void Proces } #ifdef LOG_TRACE + should_log |= (program_counter_.full - 4) == 0x401A84; //4176b6 - should_log |= (program_counter_.full - 4) == 0x418A0A;//0x41806A;//180A2; +// should_log |= (program_counter_.full - 4) == 0x418A0A;//0x41806A;//180A2; // should_log = ((program_counter_.full - 4) >= 0x417D9E) && ((program_counter_.full - 4) <= 0x419D96); #endif @@ -1885,7 +1886,7 @@ template void Proces // on the data bus as the accepted interrupt number during the interrupt // acknowledge cycle, with the low bit set since a real 68000 uses the lower // data strobe to collect the corresponding vector byte. - accepted_interrupt_level_ = interrupt_level_ = bus_interrupt_level_; + accepted_interrupt_level_ = interrupt_level_ = bus_interrupt_level_; effective_address_[0].full = 1 | uint32_t(accepted_interrupt_level_ << 1); // Recede the program counter to where it would have been were there no