mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-18 17:06:15 +00:00
174 lines
4.1 KiB
C++
174 lines
4.1 KiB
C++
//
|
|
// 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 <cstring>
|
|
|
|
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<Storage::MassStorage::MassStorageDevice> &device) {
|
|
storage_->set_storage(device);
|
|
}
|
|
|
|
void SCSICard::set_activity_observer(Activity::Observer *observer) {
|
|
scsi_bus_.set_activity_observer(observer);
|
|
}
|