Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ if (EMSCRIPTEN)
endif()

option(DPPC_BUILD_PPC_TESTS "Build PowerPC tests" OFF)
option(DPPC_BUILD_SCSI_TESTS "Build SCSI tests" OFF)
option(DPPC_BUILD_BENCHMARKS "Build benchmarking programs" OFF)

option(DPPC_68K_DEBUGGER "Enable 68k debugging" OFF)
Expand Down Expand Up @@ -231,4 +232,14 @@ if (DPPC_BUILD_PPC_TESTS)
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
endif()

if (DPPC_BUILD_SCSI_TESTS)
file(GLOB SCSI_TEST_SOURCES "${PROJECT_SOURCE_DIR}/devices/common/scsi/test/*.cpp")
add_executable(testscsi ${SCSI_TEST_SOURCES}
"${PROJECT_SOURCE_DIR}/devices/common/scsi/scsiparseutils.cpp")
target_include_directories(testscsi PRIVATE
"${PROJECT_SOURCE_DIR}"
"${PROJECT_SOURCE_DIR}/thirdparty/loguru/")
target_link_libraries(testscsi PRIVATE loguru ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
endif()

install (TARGETS dingusppc DESTINATION ${PROJECT_SOURCE_DIR}/build)
13 changes: 11 additions & 2 deletions devices/common/scsi/scsicdrom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.

#include <devices/common/scsi/scsi.h>
#include <devices/common/scsi/scsicdrom.h>
#include <devices/common/scsi/scsiparseutils.h>
#include <devices/deviceregistry.h>
#include <loguru.hpp>
#include <machines/machineproperties.h>
Expand Down Expand Up @@ -233,8 +234,16 @@ void ScsiCdrom::mode_select_6(uint8_t param_len)
std::memset(&this->data_buf[0], 0, 512);

this->post_xfer_action = [this]() {
// TODO: parse the received mode parameter list here
LOG_F(INFO, "Mode Select: received mode parameter list");
ModeSelectData parsed;
parse_mode_select_6(this->data_buf, this->incoming_size,
this->name.c_str(), parsed);

// TODO: if the block descriptor requests 512-byte blocks (parsed.bd_block_len == 512),
// we should switch block_size so subsequent READs use 512-byte LBA addressing.
// This requires BlockStorageDevice::set_block_size() to also update block_size
// (currently it only sets raw_blk_size), and size_blocks must be recalculated.
// Classic Mac OS drivers may not need this (they subdivide 2048-byte sectors
// internally), but some drivers may depend on it.
};

this->switch_phase(ScsiPhase::DATA_OUT);
Expand Down
5 changes: 4 additions & 1 deletion devices/common/scsi/scsihd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <core/timermanager.h>
#include <devices/common/scsi/scsi.h>
#include <devices/common/scsi/scsihd.h>
#include <devices/common/scsi/scsiparseutils.h>
#include <devices/deviceregistry.h>
#include <loguru.hpp>
#include <machines/machineproperties.h>
Expand Down Expand Up @@ -183,7 +184,9 @@ void ScsiHardDisk::mode_select_6(uint8_t param_len) {
std::memset(&this->data_buf[0], 0xDD, this->sector_size);

this->post_xfer_action = [this]() {
// TODO: parse the received mode parameter list here
ModeSelectData parsed;
parse_mode_select_6(this->data_buf, this->incoming_size,
this->name.c_str(), parsed);
};

this->switch_phase(ScsiPhase::DATA_OUT);
Expand Down
98 changes: 98 additions & 0 deletions devices/common/scsi/scsiparseutils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-26 The DingusPPC Development Team
(See CREDITS.MD for more details)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/** @file SCSI Mode Select parameter list parsing utilities. */

#include <devices/common/scsi/scsiparseutils.h>
#include <loguru.hpp>

#include <cstring>

int parse_mode_select_6(const uint8_t* data, int data_len, const char* dev_name,
ModeSelectData& result)
{
std::memset(&result, 0, sizeof(result));

// Need at least the 4-byte mode parameter header
if (data_len < 4) {
if (dev_name)
LOG_F(WARNING, "%s: MODE SELECT parameter list too short (%d bytes)",
dev_name, data_len);
return -1;
}

// Parse the mode parameter header (4 bytes for MODE SELECT(6))
// Byte 0: mode data length (reserved for MODE SELECT, should be 0)
result.medium_type = data[1];
result.device_param = data[2];
result.block_desc_len = data[3];

if (dev_name)
LOG_F(INFO, "%s: MODE SELECT medium_type=%d, dev_param=0x%02X, bdl=%d",
dev_name, result.medium_type, result.device_param, result.block_desc_len);

// Validate block descriptor length fits in the data
if (4 + result.block_desc_len > data_len) {
if (dev_name)
LOG_F(WARNING, "%s: MODE SELECT block descriptor overflows data",
dev_name);
return -1;
}

// Parse block descriptor if present
if (result.block_desc_len >= 8) {
result.bd_num_blocks = (data[5] << 16) | (data[6] << 8) | data[7];
result.bd_block_len = (data[9] << 16) | (data[10] << 8) | data[11];
if (dev_name && result.bd_num_blocks)
LOG_F(INFO, "%s: MODE SELECT block descriptor: num_blocks=%d, block_len=%d",
dev_name, result.bd_num_blocks, result.bd_block_len);
}

// Parse mode pages
int offset = 4 + result.block_desc_len;
result.num_pages = 0;

while (offset + 1 < data_len) {
uint8_t page_code = data[offset] & 0x3F;
uint8_t page_len = data[offset + 1];

if (offset + 2 + page_len > data_len) {
if (dev_name)
LOG_F(WARNING, "%s: MODE SELECT page 0x%02X truncated "
"(need %d bytes at offset %d, only %d available)",
dev_name, page_code, page_len, offset + 2, data_len - offset - 2);
return -1;
}

if (result.num_pages < ModeSelectData::MAX_PAGES) {
result.pages[result.num_pages].page_code = page_code;
result.pages[result.num_pages].page_len = page_len;
result.pages[result.num_pages].data_offset = offset + 2;
result.num_pages++;
}

if (dev_name)
LOG_F(INFO, "%s: MODE SELECT page 0x%02X, length %d",
dev_name, page_code, page_len);

offset += 2 + page_len;
}

return result.num_pages;
}
60 changes: 60 additions & 0 deletions devices/common/scsi/scsiparseutils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-26 The DingusPPC Development Team
(See CREDITS.MD for more details)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/** @file SCSI Mode Select parameter list parsing utilities. */

#ifndef SCSI_PARSE_UTILS_H
#define SCSI_PARSE_UTILS_H

#include <cinttypes>

/** Result of parsing a mode page entry. */
struct ModePageParsed {
uint8_t page_code;
uint8_t page_len;
int data_offset; // byte offset to page data (past page_code + page_len)
};

/** Parsed MODE SELECT(6) parameter list. */
struct ModeSelectData {
uint8_t medium_type;
uint8_t device_param;
uint8_t block_desc_len;

// Block descriptor fields (valid when block_desc_len >= 8)
uint32_t bd_num_blocks;
uint32_t bd_block_len;

int num_pages;
static const int MAX_PAGES = 16;
ModePageParsed pages[MAX_PAGES];
};

/** Parse a MODE SELECT(6) parameter list.
*
* @param data Pointer to the parameter list data.
* @param data_len Length of the parameter list in bytes.
* @param dev_name Device name for log messages (may be nullptr to suppress logging).
* @param result Parsed result output.
* @return Number of pages parsed, or -1 on structural error.
*/
int parse_mode_select_6(const uint8_t* data, int data_len, const char* dev_name,
ModeSelectData& result);

#endif // SCSI_PARSE_UTILS_H
Loading
Loading