// // SCSICard.cpp // Clock Signal // // Created by Thomas Harte on 22/08/2022. // Copyright © 2022 Thomas Harte. All rights reserved. // // Per the documentation around the GGLabs Apple II SCSI card clone: // // A 5380 is mapped to the first eight bytes of slot IO: // // $c0x0 R current SCSI data register // $c0x0 W output data register // $c0x1 R/W initiator command register // $c0x2 R/W mode select register // $c0x3 R/W target command register // $c0x4 R SCSI bus status // $c0x4 W select enable register // $c0x5 R bus and status register // $c0x6 R input data register // $c0x7 R reset parity and interrupts // (i.e. the 5380's standard registers in their usual order) // // The remaining eight are used for control functions: // // $c0x8 R/W PDMA/DACK // $c0x9 R SCSI device ID // $c0xa W memory bank select register // $c0xb W reset 5380 SCSI chip // $c0xc - [unused] // $c0xd W PDMA mode enable // $c0xe R read DRQ status through bit 7 // $c0xf - [unused] // // Further, per that card's schematic: // // BANK REGISTER: bit 0..3 ROM Addr, 4..6 RAM Addr, 7 RSVD // // Which relates to the description: // // The card is also equipped with 16K of ROM and 8K of RAM. // These are mapped in the $C800-$CFFF card memory using a banking // scheme. The $C0xA bank register selects the which bank of RAM // and ROM are mapped. RAM is always at $C800-$CBFF and ROM is // at $CC00-$CFFF. The boot code in the first 256 bytes of ROM // bank 0 is also mapped in the IOSEL space ($Cn00-$CnFF). // #include "SCSICard.hpp" #include using namespace Apple::II; ROM::Request SCSICard::rom_request() { return ROM::Request(ROM::Name::AppleIISCSICard); } // TODO: accept and supply real clock rate. SCSICard::SCSICard(ROM::Map &map, int clock_rate) : scsi_bus_(clock_rate), ncr5380_(scsi_bus_, clock_rate), storage_(scsi_bus_, 6) { // Grab a copy of the SCSI ROM. const auto rom = map.find(ROM::Name::AppleIISCSICard); if(rom == map.end()) { throw ROMMachine::Error::MissingROMs; } memcpy(rom_.data(), rom->second.data(), rom_.size()); // Set up initial banking. rom_pointer_ = rom_.data(); ram_pointer_ = ram_.data(); } void SCSICard::perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) { switch(select) { default: break; case Select::Device: if(is_read) { *value = rom_[address & 255]; } break; case Select::IO: address &= 0xf; switch(address) { case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: if(is_read) { *value = ncr5380_.read(address); } else { ncr5380_.write(address, *value); } break; case 0x9: if(is_read) { *value = uint8_t(ncr5380_.scsi_id()); } break; case 0x8: // DMA acknowledge. if(is_read) { *value = ncr5380_.dma_acknowledge(); } else { ncr5380_.dma_acknowledge(*value); } break; case 0xa: // RAM and ROM select. if(!is_read) { const auto rom_base = size_t((*value & 0x0f) << 10); const auto ram_base = size_t((*value & 0x70) << 6); rom_pointer_ = &rom_[rom_base]; ram_pointer_ = &ram_[ram_base]; } break; case 0xb: if(!is_read) { printf("TODO: NCR reset\n"); } break; case 0xd: if(!is_read) { printf("TODO: Enable PDMA\n"); } break; case 0xe: // DRQ in b7. if(is_read) { *value = ncr5380_.dma_request() ? 0x80 : 0x00; } break; default: printf("Unhandled: %04x %c %02x\n", address, is_read ? 'r' : 'w', *value); break; } break; case Select::C8Region: if(address & 0x400) { if(is_read) { *value = rom_pointer_[address & 0x3ff]; } } else { if(is_read) { *value = ram_pointer_[address & 0x3ff]; } else { ram_pointer_[address & 0x3ff] = *value; } } break; } } void SCSICard::set_storage_device(const std::shared_ptr &device) { storage_->set_storage(device); } void SCSICard::set_activity_observer(Activity::Observer *observer) { scsi_bus_.set_activity_observer(observer); }