Move C++ code into cpp/ dir (#941)

- Moved C++ code to cpp/ from src/raspberrypi
- Related updates to Makefile, easyinstall.sh, and the github build rules
- Removed the native X68k C code in src/x68k from the repo
This commit is contained in:
Daniel Markstedt
2022-10-25 12:59:30 -07:00
committed by GitHub
parent 4b109a70b0
commit 08194af424
184 changed files with 11 additions and 4920 deletions
+735
View File
@@ -0,0 +1,735 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
//
// XM6i
// Copyright (C) 2010-2015 isaki@NetBSD.org
// Copyright (C) 2010 Y.Sugahara
//
// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
//---------------------------------------------------------------------------
#include "rascsi_exceptions.h"
#include "dispatcher.h"
#include "scsi_command_util.h"
#include "disk.h"
using namespace scsi_defs;
using namespace scsi_command_util;
const unordered_map<uint32_t, uint32_t> Disk::shift_counts = { { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 } };
Disk::Disk(PbDeviceType type, int lun) : StorageDevice(type, lun)
{
// REZERO implementation is identical with Seek
dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Seek);
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
// REASSIGN BLOCKS implementation is identical with Seek
dispatcher.Add(scsi_command::eCmdReassign, "ReassignBlocks", &Disk::Seek);
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &Disk::Read6);
dispatcher.Add(scsi_command::eCmdWrite6, "Write6", &Disk::Write6);
dispatcher.Add(scsi_command::eCmdSeek6, "Seek6", &Disk::Seek6);
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &Disk::StartStopUnit);
dispatcher.Add(scsi_command::eCmdRemoval, "PreventAllowMediumRemoval", &Disk::PreventAllowMediumRemoval);
dispatcher.Add(scsi_command::eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10);
dispatcher.Add(scsi_command::eCmdRead10, "Read10", &Disk::Read10);
dispatcher.Add(scsi_command::eCmdWrite10, "Write10", &Disk::Write10);
dispatcher.Add(scsi_command::eCmdReadLong10, "ReadLong10", &Disk::ReadWriteLong10);
dispatcher.Add(scsi_command::eCmdWriteLong10, "WriteLong10", &Disk::ReadWriteLong10);
dispatcher.Add(scsi_command::eCmdWriteLong16, "WriteLong16", &Disk::ReadWriteLong16);
dispatcher.Add(scsi_command::eCmdSeek10, "Seek10", &Disk::Seek10);
dispatcher.Add(scsi_command::eCmdVerify10, "Verify10", &Disk::Verify10);
dispatcher.Add(scsi_command::eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache);
dispatcher.Add(scsi_command::eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache);
dispatcher.Add(scsi_command::eCmdReadDefectData10, "ReadDefectData10", &Disk::ReadDefectData10);
dispatcher.Add(scsi_command::eCmdRead16, "Read16", &Disk::Read16);
dispatcher.Add(scsi_command::eCmdWrite16, "Write16", &Disk::Write16);
dispatcher.Add(scsi_command::eCmdVerify16, "Verify16", &Disk::Verify16);
dispatcher.Add(scsi_command::eCmdReadCapacity16_ReadLong16, "ReadCapacity16/ReadLong16", &Disk::ReadCapacity16_ReadLong16);
}
Disk::~Disk()
{
// Save disk cache, only if ready
if (IsReady() && cache != nullptr) {
cache->Save();
}
}
bool Disk::Dispatch(scsi_command cmd)
{
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
if (IsMediumChanged()) {
assert(IsRemovable());
SetMediumChanged(false);
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
}
// The superclass handles the less specific commands
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
}
void Disk::SetUpCache(off_t image_offset, bool raw)
{
cache = make_unique<DiskCache>(GetFilename(), size_shift_count, (uint32_t)GetBlockCount(), image_offset);
cache->SetRawMode(raw);
}
void Disk::ResizeCache(const string& path, bool raw)
{
cache.reset(new DiskCache(path, size_shift_count, (uint32_t)GetBlockCount()));
cache->SetRawMode(raw);
}
void Disk::FlushCache()
{
if (cache != nullptr) {
cache->Save();
}
}
void Disk::FormatUnit()
{
CheckReady();
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
if ((ctrl->cmd[1] & 0x10) != 0 && ctrl->cmd[4] != 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
EnterStatusPhase();
}
void Disk::Read(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = Read(ctrl->cmd, controller->GetBuffer(), start);
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, controller->GetLength())
// Set next block
ctrl->next = start + 1;
EnterDataInPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::ReadWriteLong10()
{
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(ctrl->cmd, 7) != 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
ValidateBlockAddress(RW10);
EnterStatusPhase();
}
void Disk::ReadWriteLong16()
{
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(ctrl->cmd, 12) != 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
ValidateBlockAddress(RW16);
EnterStatusPhase();
}
void Disk::Write(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = WriteCheck(start);
// Set next block
ctrl->next = start + 1;
EnterDataOutPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::Verify(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
// if BytChk=0
if ((ctrl->cmd[1] & 0x02) == 0) {
Seek();
return;
}
// Test reading
ctrl->length = Read(ctrl->cmd, controller->GetBuffer(), start);
// Set next block
ctrl->next = start + 1;
EnterDataOutPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::StartStopUnit()
{
const bool start = ctrl->cmd[4] & 0x01;
const bool load = ctrl->cmd[4] & 0x02;
if (load) {
LOGTRACE(start ? "Loading medium" : "Ejecting medium")
}
else {
LOGTRACE(start ? "Starting unit" : "Stopping unit")
SetStopped(!start);
}
if (!start) {
// Look at the eject bit and eject if necessary
if (load) {
if (IsLocked()) {
// Cannot be ejected because it is locked
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
}
// Eject
if (!Eject(false)) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
}
}
else {
FlushCache();
}
}
EnterStatusPhase();
}
void Disk::PreventAllowMediumRemoval()
{
CheckReady();
const bool lock = ctrl->cmd[4] & 0x01;
LOGTRACE(lock ? "Locking medium" : "Unlocking medium")
SetLocked(lock);
EnterStatusPhase();
}
void Disk::SynchronizeCache()
{
FlushCache();
EnterStatusPhase();
}
void Disk::ReadDefectData10()
{
const size_t allocation_length = min((size_t)GetInt16(ctrl->cmd, 7), (size_t)4);
// The defect list is empty
fill_n(controller->GetBuffer().begin(), allocation_length, 0);
ctrl->length = (int)allocation_length;
EnterDataInPhase();
}
bool Disk::Eject(bool force)
{
const bool status = super::Eject(force);
if (status) {
FlushCache();
cache.reset();
// The image file for this drive is not in use anymore
UnreserveFile();
}
return status;
}
int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
{
// Get length, clear buffer
const auto length = (int)min(buf.size(), (size_t)cdb[4]);
fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER
if (IsProtected()) {
buf[2] = 0x80;
}
// Basic information
int size = 4;
// Add block descriptor if DBD is 0
if ((cdb[1] & 0x08) == 0) {
// Mode parameter header, block descriptor length
buf[3] = 0x08;
// Only if ready
if (IsReady()) {
// Short LBA mode parameter block descriptor (number of blocks and block length)
SetInt32(buf, 4, (uint32_t)GetBlockCount());
SetInt32(buf, 8, GetSectorSizeInBytes());
}
size = 12;
}
size = AddModePages(cdb, buf, size, length, 255);
buf[0] = (BYTE)size;
return size;
}
int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
{
// Get length, clear buffer
const auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER
if (IsProtected()) {
buf[3] = 0x80;
}
// Basic Information
int size = 8;
// Add block descriptor if DBD is 0, only if ready
if ((cdb[1] & 0x08) == 0 && IsReady()) {
uint64_t disk_blocks = GetBlockCount();
uint32_t disk_size = GetSectorSizeInBytes();
// Check LLBAA for short or long block descriptor
if ((cdb[1] & 0x10) == 0 || disk_blocks <= 0xFFFFFFFF) {
// Mode parameter header, block descriptor length
buf[7] = 0x08;
// Short LBA mode parameter block descriptor (number of blocks and block length)
SetInt32(buf, 8, (uint32_t)disk_blocks);
SetInt32(buf, 12, disk_size);
size = 16;
}
else {
// Mode parameter header, LONGLBA
buf[4] = 0x01;
// Mode parameter header, block descriptor length
buf[7] = 0x10;
// Long LBA mode parameter block descriptor (number of blocks and block length)
SetInt64(buf, 8, disk_blocks);
SetInt32(buf, 20, disk_size);
size = 24;
}
}
size = AddModePages(cdb, buf, size, length, 65535);
SetInt16(buf, 0, size);
return size;
}
void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
// Page code 1 (read-write error recovery)
if (page == 0x01 || page == 0x3f) {
AddErrorPage(pages, changeable);
}
// Page code 3 (format device)
if (page == 0x03 || page == 0x3f) {
AddFormatPage(pages, changeable);
}
// Page code 4 (rigid drive page)
if (page == 0x04 || page == 0x3f) {
AddDrivePage(pages, changeable);
}
// Page code 8 (caching)
if (page == 0x08 || page == 0x3f) {
AddCachePage(pages, changeable);
}
// Page (vendor special)
AddVendorPage(pages, page, changeable);
}
void Disk::AddErrorPage(map<int, vector<byte>>& pages, bool) const
{
vector<byte> buf(12);
// Retry count is 0, limit time uses internal default value
pages[1] = buf;
}
void Disk::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
{
vector<byte> buf(24);
// No changeable area
if (changeable) {
pages[3] = buf;
return;
}
if (IsReady()) {
// Set the number of tracks in one zone to 8
buf[0x03] = (byte)0x08;
// Set sector/track to 25
SetInt16(buf, 0x0a, 25);
// Set the number of bytes in the physical sector
SetInt16(buf, 0x0c, 1 << size_shift_count);
// Interleave 1
SetInt16(buf, 0x0e, 1);
// Track skew factor 11
SetInt16(buf, 0x10, 11);
// Cylinder skew factor 20
SetInt16(buf, 0x12, 20);
}
buf[20] = IsRemovable() ? (byte)0x20 : (byte)0x00;
// Hard-sectored
buf[20] |= (byte)0x40;
pages[3] = buf;
}
void Disk::AddDrivePage(map<int, vector<byte>>& pages, bool changeable) const
{
vector<byte> buf(24);
// No changeable area
if (changeable) {
pages[4] = buf;
return;
}
if (IsReady()) {
// Set the number of cylinders (total number of blocks
// divided by 25 sectors/track and 8 heads)
uint64_t cylinders = GetBlockCount();
cylinders >>= 3;
cylinders /= 25;
SetInt32(buf, 0x01, (uint32_t)cylinders);
// Fix the head at 8
buf[0x05] = (byte)0x8;
// Medium rotation rate 7200
SetInt16(buf, 0x14, 7200);
}
pages[4] = buf;
}
void Disk::AddCachePage(map<int, vector<byte>>& pages, bool changeable) const
{
vector<byte> buf(12);
// No changeable area
if (changeable) {
pages[8] = buf;
return;
}
// Only read cache is valid
// Disable pre-fetch transfer length
SetInt16(buf, 0x04, -1);
// Maximum pre-fetch
SetInt16(buf, 0x08, -1);
// Maximum pre-fetch ceiling
SetInt16(buf, 0x0a, -1);
pages[8] = buf;
}
int Disk::Read(const vector<int>&, vector<BYTE>& buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= GetBlockCount()) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// leave it to the cache
if (!cache->ReadSector(buf, (uint32_t)block)) {
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
}
return GetSectorSizeInBytes();
}
int Disk::WriteCheck(uint64_t block)
{
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= GetBlockCount()) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Error if write protected
if (IsProtected()) {
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
}
return GetSectorSizeInBytes();
}
void Disk::Write(const vector<int>&, const vector<BYTE>& buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= GetBlockCount()) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
// Error if write protected
if (IsProtected()) {
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
}
// Leave it to the cache
if (!cache->WriteSector(buf, (uint32_t)block)) {
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
}
}
void Disk::Seek()
{
CheckReady();
EnterStatusPhase();
}
void Disk::Seek6()
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK6)) {
CheckReady();
}
EnterStatusPhase();
}
void Disk::Seek10()
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK10)) {
CheckReady();
}
EnterStatusPhase();
}
void Disk::ReadCapacity10()
{
CheckReady();
if (GetBlockCount() == 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
vector<BYTE>& buf = controller->GetBuffer();
// Create end of logical block address (blocks-1)
uint64_t capacity = GetBlockCount() - 1;
// If the capacity exceeds 32 bit, -1 must be returned and the client has to use READ CAPACITY(16)
if (capacity > 4294967295) {
capacity = -1;
}
SetInt32(buf, 0, (uint32_t)capacity);
// Create block length (1 << size)
SetInt32(buf, 4, 1 << size_shift_count);
// the size
ctrl->length = 8;
EnterDataInPhase();
}
void Disk::ReadCapacity16()
{
CheckReady();
if (GetBlockCount() == 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
vector<BYTE>& buf = controller->GetBuffer();
// Create end of logical block address (blocks-1)
SetInt64(buf, 0, GetBlockCount() - 1);
// Create block length (1 << size)
SetInt32(buf, 8, 1 << size_shift_count);
buf[12] = 0;
// Logical blocks per physical block: not reported (1 or more)
buf[13] = 0;
// the size
ctrl->length = 14;
EnterDataInPhase();
}
void Disk::ReadCapacity16_ReadLong16()
{
// The service action determines the actual command
switch (ctrl->cmd[1] & 0x1f) {
case 0x10:
ReadCapacity16();
break;
case 0x11:
ReadWriteLong16();
break;
default:
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
break;
}
}
//---------------------------------------------------------------------------
//
// Check/Get start sector and sector count for a READ/WRITE or READ/WRITE LONG operation
//
//---------------------------------------------------------------------------
void Disk::ValidateBlockAddress(access_mode mode) const
{
const uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
if (block > GetBlockCount()) {
LOGTRACE("%s", ("Capacity of " + to_string(GetBlockCount()) + " block(s) exceeded: Trying to access block "
+ to_string(block)).c_str())
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
}
bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mode mode) const
{
if (mode == RW6 || mode == SEEK6) {
start = GetInt24(ctrl->cmd, 1);
count = ctrl->cmd[4];
if (!count) {
count= 0x100;
}
}
else {
start = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
if (mode == RW16) {
count = GetInt32(ctrl->cmd, 10);
}
else if (mode != SEEK6 && mode != SEEK10) {
count = GetInt16(ctrl->cmd, 7);
}
else {
count = 0;
}
}
LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count)
// Check capacity
if (uint64_t capacity = GetBlockCount(); !capacity || start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str())
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
// Do not process 0 blocks
if (!count && (mode != SEEK6 && mode != SEEK10)) {
return false;
}
return true;
}
uint32_t Disk::CalculateShiftCount(uint32_t size_in_bytes)
{
const auto& it = shift_counts.find(size_in_bytes);
return it != shift_counts.end() ? it->second : 0;
}
uint32_t Disk::GetSectorSizeInBytes() const
{
return size_shift_count ? 1 << size_shift_count : 0;
}
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
{
DeviceFactory device_factory;
if (const auto& sizes = device_factory.GetSectorSizes(GetType());
!sizes.empty() && sizes.find(size_in_bytes) == sizes.end()) {
throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
}
size_shift_count = CalculateShiftCount(size_in_bytes);
assert(size_shift_count);
}
uint32_t Disk::GetConfiguredSectorSize() const
{
return configured_sector_size;
}
bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t configured_size)
{
if (unordered_set<uint32_t> sizes = device_factory.GetSectorSizes(GetType());
sizes.find(configured_size) == sizes.end()) {
return false;
}
configured_sector_size = configured_size;
return true;
}