diff --git a/Machines/PCCompatible/DMA.hpp b/Machines/PCCompatible/DMA.hpp index 3e9795bbc..73a4ff487 100644 --- a/Machines/PCCompatible/DMA.hpp +++ b/Machines/PCCompatible/DMA.hpp @@ -11,31 +11,47 @@ #include "../../Numeric/RegisterSizes.hpp" +#include "Memory.hpp" + namespace PCCompatible { +enum class AccessResult { + Accepted, + AcceptedWithEOP, + NotAccepted, +}; + class i8237 { public: void flip_flop_reset() { + printf("DMA: Flip flop reset\n"); next_access_low_ = true; } void mask_reset() { + printf("DMA: Mask reset\n"); for(auto &channel : channels_) { channel.mask = false; } } void master_reset() { + printf("DMA: Master reset\n"); flip_flop_reset(); for(auto &channel : channels_) { channel.mask = true; - channel.transfer_complete = true; + channel.transfer_complete = false; channel.request = false; } + + // This is a bit of a hack; DMA channel 0 is supposed to be linked to the PIT, + // performing DRAM refresh. It isn't yet. So hack this, and hack that. + channels_[0].transfer_complete = true; } template void write(uint8_t value) { + printf("DMA: Write %02x to %d\n", value, address); constexpr int channel = (address >> 1) & 3; constexpr bool is_count = address & 1; @@ -57,6 +73,7 @@ class i8237 { template uint8_t read() { + printf("DMA: Read %d\n", address); constexpr int channel = (address >> 1) & 3; constexpr bool is_count = address & 1; @@ -77,14 +94,17 @@ class i8237 { } void set_reset_mask(uint8_t value) { + printf("DMA: Set/reset mask %02x\n", value); channels_[value & 3].mask = value & 4; } void set_reset_request(uint8_t value) { + printf("DMA: Set/reset request %02x\n", value); channels_[value & 3].request = value & 4; } void set_mask(uint8_t value) { + printf("DMA: Set mask %02x\n", value); channels_[0].mask = value & 1; channels_[1].mask = value & 2; channels_[2].mask = value & 4; @@ -92,6 +112,7 @@ class i8237 { } void set_mode(uint8_t value) { + printf("DMA: Set mode %02x\n", value); channels_[value & 3].transfer = Channel::Transfer((value >> 2) & 3); channels_[value & 3].autoinitialise = value & 0x10; channels_[value & 3].address_decrement = value & 0x20; @@ -99,6 +120,7 @@ class i8237 { } void set_command(uint8_t value) { + printf("DMA: Set command %02x\n", value); enable_memory_to_memory_ = value & 0x01; enable_channel0_address_hold_ = value & 0x02; enable_controller_ = value & 0x04; @@ -124,38 +146,47 @@ class i8237 { for(auto &channel : channels_) { channel.transfer_complete = false; } + + printf("DMA: status is %02x\n", result); + return result; } // // Interface for reading/writing via DMA. // - static constexpr auto NotAvailable = uint32_t(~0); /// Provides the next target address for @c channel if performing either a write (if @c is_write is @c true) or read (otherwise). /// - /// @returns Either a 16-bit address or @c NotAvailable if the requested channel isn't set up to perform a read or write at present. - uint32_t access(size_t channel, bool is_write) { -// if(channels_[channel].transfer_complete) { -// return NotAvailable; -// } + /// @returns A combined address and @c AccessResult. + std::pair access(size_t channel, bool is_write) { if(is_write && channels_[channel].transfer != Channel::Transfer::Write) { - return NotAvailable; + return std::make_pair(0, AccessResult::NotAccepted); } if(!is_write && channels_[channel].transfer != Channel::Transfer::Read) { - return NotAvailable; + return std::make_pair(0, AccessResult::NotAccepted); } const auto address = channels_[channel].address.full; channels_[channel].address.full += channels_[channel].address_decrement ? -1 : 1; --channels_[channel].count.full; + + const bool was_complete = channels_[channel].transfer_complete; channels_[channel].transfer_complete = (channels_[channel].count.full == 0xffff); if(channels_[channel].transfer_complete) { // TODO: _something_ with mode. } - return address; + auto result = AccessResult::Accepted; + if(!was_complete && channels_[channel].transfer_complete) { + result = AccessResult::AcceptedWithEOP; + } + return std::make_pair(address, result); + } + + void set_complete(size_t channel) { + channels_[channel].transfer_complete = true; } private: @@ -227,6 +258,32 @@ class DMAPages { } }; +class DMA { + public: + i8237 controller; + DMAPages pages; + + // Memory is set posthoc to resolve a startup time. + void set_memory(Memory *memory) { + memory_ = memory; + } + + // TODO: this permits only 8-bit DMA. Fix that. + AccessResult write(size_t channel, uint8_t value) { + auto access = controller.access(channel, true); + if(access.second == AccessResult::NotAccepted) { + return access.second; + } + + const uint32_t address = uint32_t(pages.channel_page(channel) << 16) | access.first; + *memory_->at(address) = value; + return access.second; + } + + private: + Memory *memory_; +}; + } #endif /* DMA_hpp */ diff --git a/Machines/PCCompatible/KeyboardMapper.hpp b/Machines/PCCompatible/KeyboardMapper.hpp index 65f060a60..f942d5229 100644 --- a/Machines/PCCompatible/KeyboardMapper.hpp +++ b/Machines/PCCompatible/KeyboardMapper.hpp @@ -71,7 +71,7 @@ class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper case k::LeftShift: return 42; case k::Backslash: return 43; - case k::Z: return 55; + case k::Z: return 44; case k::X: return 45; case k::C: return 46; case k::V: return 47; diff --git a/Machines/PCCompatible/PCCompatible.cpp b/Machines/PCCompatible/PCCompatible.cpp index f72314c63..0a71e7a5f 100644 --- a/Machines/PCCompatible/PCCompatible.cpp +++ b/Machines/PCCompatible/PCCompatible.cpp @@ -49,33 +49,6 @@ namespace PCCompatible { //bool log = false; //std::string previous; -class DMA { - public: - i8237 controller; - DMAPages pages; - - // Memory is set posthoc to resolve a startup time. - void set_memory(Memory *memory) { - memory_ = memory; - } - - // TODO: this permits only 8-bit DMA. Fix that. - bool write(size_t channel, uint8_t value) { - auto address = controller.access(channel, true); - if(address == i8237::NotAvailable) { - return false; - } - - address |= uint32_t(pages.channel_page(channel) << 16); - *memory_->at(address) = value; - - return true; - } - - private: - Memory *memory_; -}; - class FloppyController { public: FloppyController(PIC &pic, DMA &dma) : pic_(pic), dma_(dma) { @@ -146,58 +119,75 @@ class FloppyController { status_.begin(decoder_); // Search for a matching sector. - const auto target = decoder_.geometry(); - bool found_sector = false; - for(auto &pair: drives_[decoder_.target().drive].sectors(decoder_.target().head)) { - if( - (pair.second.address.track == target.cylinder) && - (pair.second.address.sector == target.sector) && - (pair.second.address.side == target.head) && - (pair.second.size == target.size) - ) { - found_sector = true; - bool wrote_in_full = true; + auto target = decoder_.geometry(); +// bool found_sector = false; - printf("Writing data beginning: "); - for(int c = 0; c < 128 << target.size; c++) { - if(c < 8) printf("%02x ", pair.second.samples[0].data()[c]); + bool complete = false; + while(!complete) { + for(auto &pair: drives_[decoder_.target().drive].sectors(decoder_.target().head)) { + if( + (pair.second.address.track == target.cylinder) && + (pair.second.address.sector == target.sector) && + (pair.second.address.side == target.head) && + (pair.second.size == target.size) + ) { +// found_sector = true; +// bool wrote_in_full = true; - if(!dma_.write(2, pair.second.samples[0].data()[c])) { - printf("FDC: DMA not permitted\n"); - wrote_in_full = false; - break; + printf("Writing data beginning: "); + for(int c = 0; c < 128 << target.size; c++) { + if(c < 8) printf("%02x ", pair.second.samples[0].data()[c]); + + const auto access_result = dma_.write(2, pair.second.samples[0].data()[c]); + switch(access_result) { + default: break; + case AccessResult::NotAccepted: + printf("FDC: DMA not permitted\n"); +// wrote_in_full = false; + break; + case AccessResult::AcceptedWithEOP: + complete = true; + break; + } + if(access_result != AccessResult::Accepted) { + break; + } } - } - printf("\n"); - if(wrote_in_full) { - results_.serialise( - status_, - decoder_.geometry().cylinder, - decoder_.geometry().head, - decoder_.geometry().sector, - decoder_.geometry().size); - } else { - printf("FDC: didn't write in full\n"); - // TODO: Overrun, presumably? - } + ++target.sector; // TODO: multitrack? + printf("\n"); - break; +// if(wrote_in_full) { +// } else { +// printf("FDC: didn't write in full\n"); +// // TODO: Overrun, presumably? +// } + + break; + } } } - if(!found_sector) { - printf("FDC: sector not found\n"); - // TODO: there's more than this, I think. - status_.set(Intel::i8272::Status0::AbnormalTermination); - results_.serialise( - status_, - decoder_.geometry().cylinder, - decoder_.geometry().head, - decoder_.geometry().sector, - decoder_.geometry().size); - } + results_.serialise( + status_, + decoder_.geometry().cylinder, + decoder_.geometry().head, + decoder_.geometry().sector, + decoder_.geometry().size); +// if(!found_sector) { +// printf("FDC: sector not found\n"); +// // TODO: there's more than this, I think. +// status_.set(Intel::i8272::Status0::AbnormalTermination); +// results_.serialise( +// status_, +// decoder_.geometry().cylinder, +// decoder_.geometry().head, +// decoder_.geometry().sector, +// decoder_.geometry().size); +// } + + // TODO: what if head has changed? drives_[decoder_.target().drive].status = decoder_.drive_head(); drives_[decoder_.target().drive].raised_interrupt = true; pic_.apply_edge<6>(true);