1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Merge pull request #1232 from TomHarte/8237TC

Improves 8237 TC/EOP support enough for some multisector reads
This commit is contained in:
Thomas Harte 2023-12-02 22:24:58 -05:00 committed by GitHub
commit 596267f62d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 128 additions and 81 deletions

View File

@ -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 <int address>
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 <int address>
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<uint16_t, AccessResult> 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 */

View File

@ -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;

View File

@ -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);