mirror of
https://github.com/holub/mame
synced 2025-06-03 11:26:56 +03:00
nscsi: Add SCSI tape device based on SIMH tape image format (#11430)
This commit is contained in:
parent
86c6e83b4c
commit
5353a8df14
@ -2988,6 +2988,8 @@ if (BUSES["NSCSI"]~=null) then
|
||||
MAME_DIR .. "src/devices/bus/nscsi/s1410.h",
|
||||
MAME_DIR .. "src/devices/bus/nscsi/smoc501.cpp",
|
||||
MAME_DIR .. "src/devices/bus/nscsi/smoc501.h",
|
||||
MAME_DIR .. "src/devices/bus/nscsi/tape.cpp",
|
||||
MAME_DIR .. "src/devices/bus/nscsi/tape.h",
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -96,6 +96,7 @@ end
|
||||
MAME_DIR .. "src/lib/util/md5.h",
|
||||
MAME_DIR .. "src/lib/util/msdib.cpp",
|
||||
MAME_DIR .. "src/lib/util/msdib.h",
|
||||
MAME_DIR .. "src/lib/util/multibyte.h",
|
||||
MAME_DIR .. "src/lib/util/nanosvg.cpp",
|
||||
MAME_DIR .. "src/lib/util/nanosvg.h",
|
||||
MAME_DIR .. "src/lib/util/notifier.h",
|
||||
@ -117,8 +118,11 @@ end
|
||||
MAME_DIR .. "src/lib/util/server_https.hpp",
|
||||
MAME_DIR .. "src/lib/util/server_ws.hpp",
|
||||
MAME_DIR .. "src/lib/util/server_wss.hpp",
|
||||
MAME_DIR .. "src/lib/util/simh_tape_file.cpp",
|
||||
MAME_DIR .. "src/lib/util/simh_tape_file.h",
|
||||
MAME_DIR .. "src/lib/util/strformat.cpp",
|
||||
MAME_DIR .. "src/lib/util/strformat.h",
|
||||
MAME_DIR .. "src/lib/util/tape_file_interface.h",
|
||||
MAME_DIR .. "src/lib/util/timeconv.cpp",
|
||||
MAME_DIR .. "src/lib/util/timeconv.h",
|
||||
MAME_DIR .. "src/lib/util/unicode.cpp",
|
||||
|
@ -69,6 +69,8 @@ files {
|
||||
MAME_DIR .. "src/devices/imagedev/picture.h",
|
||||
MAME_DIR .. "src/devices/imagedev/printer.cpp",
|
||||
MAME_DIR .. "src/devices/imagedev/printer.h",
|
||||
MAME_DIR .. "src/devices/imagedev/simh_tape_image.cpp",
|
||||
MAME_DIR .. "src/devices/imagedev/simh_tape_image.h",
|
||||
MAME_DIR .. "src/devices/imagedev/snapquik.cpp",
|
||||
MAME_DIR .. "src/devices/imagedev/snapquik.h",
|
||||
MAME_DIR .. "src/devices/imagedev/wafadrive.cpp",
|
||||
|
@ -16,11 +16,13 @@
|
||||
#include "bus/nscsi/hd.h"
|
||||
#include "bus/nscsi/s1410.h"
|
||||
#include "bus/nscsi/smoc501.h"
|
||||
#include "bus/nscsi/tape.h"
|
||||
|
||||
void default_scsi_devices(device_slot_interface &device)
|
||||
{
|
||||
device.option_add("cdrom", NSCSI_CDROM);
|
||||
device.option_add("harddisk", NSCSI_HARDDISK);
|
||||
device.option_add("tape", NSCSI_TAPE);
|
||||
device.option_add("s1410", NSCSI_S1410);
|
||||
device.option_add("cw7501", CW7501);
|
||||
device.option_add("cdr4210", CDR4210);
|
||||
|
899
src/devices/bus/nscsi/tape.cpp
Normal file
899
src/devices/bus/nscsi/tape.cpp
Normal file
@ -0,0 +1,899 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
// best read together with SCSI-2 draft spec (rev 10L 7-SEP-93)
|
||||
// https://www.staff.uni-mainz.de/tacke/scsi/SCSI2.html
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
#include "tape.h"
|
||||
|
||||
#include "util/multibyte.h"
|
||||
#include "util/tape_file_interface.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
// #define VERBOSE LOG_GENERAL
|
||||
// #define LOG_OUTPUT_FUNC osd_printf_info
|
||||
#include "logmacro.h"
|
||||
|
||||
DEFINE_DEVICE_TYPE(NSCSI_TAPE, nscsi_tape_device, "scsi_tape", "SCSI tape");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// constants
|
||||
|
||||
static constexpr u32 TAPE_DEFAULT_FIXED_BLOCK_LEN = 512;
|
||||
|
||||
static constexpr int TAPE_RW_BUF_ID = 2;
|
||||
static constexpr int TAPE_PL_BUF_ID = 3;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// construction
|
||||
|
||||
nscsi_tape_device::nscsi_tape_device(const machine_config &config, device_type type, const char *tag, device_t *owner, u32 clock)
|
||||
: nscsi_full_device(config, type, tag, owner, clock)
|
||||
, m_image(*this, "image")
|
||||
, m_sequence_counter(0)
|
||||
, m_has_tape(false)
|
||||
, m_tape_changed(false)
|
||||
, m_fixed_block_len(TAPE_DEFAULT_FIXED_BLOCK_LEN)
|
||||
, m_rw_buf_size(m_fixed_block_len)
|
||||
, m_rw_pending(false)
|
||||
{
|
||||
}
|
||||
|
||||
nscsi_tape_device::nscsi_tape_device(const machine_config &config, const char *tag, device_t *owner, u32 clock)
|
||||
: nscsi_tape_device(config, NSCSI_TAPE, tag, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// device_t implementation
|
||||
|
||||
void nscsi_tape_device::device_add_mconfig(machine_config &config)
|
||||
{
|
||||
SIMH_TAPE_IMAGE(config, m_image).set_interface("tape");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// nscsi_full_device implementation
|
||||
|
||||
void nscsi_tape_device::device_start()
|
||||
{
|
||||
nscsi_full_device::device_start();
|
||||
m_sequence_counter = m_image->sequence_counter();
|
||||
m_has_tape = m_image->get_file();
|
||||
m_rw_buf = std::make_unique<u8[]>(m_rw_buf_size);
|
||||
save_item(NAME(m_sequence_counter));
|
||||
save_item(NAME(m_has_tape));
|
||||
save_item(NAME(m_tape_changed));
|
||||
save_item(NAME(m_fixed_block_len));
|
||||
save_item(NAME(m_rw_buf_size));
|
||||
save_pointer(NAME(m_rw_buf), m_rw_buf_size);
|
||||
save_item(NAME(m_rw_pending));
|
||||
save_item(NAME(m_rw_len));
|
||||
save_item(NAME(m_rw_blocks_num));
|
||||
save_item(NAME(m_rw_req_blocks_num));
|
||||
save_item(NAME(m_rw_req_block_len));
|
||||
save_item(NAME(m_rw_fixed_blocks));
|
||||
save_item(NAME(m_r_suppress_bad_len));
|
||||
save_item(NAME(m_pl_buf));
|
||||
save_item(NAME(m_pl_len));
|
||||
}
|
||||
|
||||
void nscsi_tape_device::device_reset()
|
||||
{
|
||||
nscsi_full_device::device_reset();
|
||||
m_sequence_counter = m_image->sequence_counter();
|
||||
m_has_tape = m_image->get_file();
|
||||
m_tape_changed = false;
|
||||
m_fixed_block_len = TAPE_DEFAULT_FIXED_BLOCK_LEN;
|
||||
m_rw_pending = false;
|
||||
}
|
||||
|
||||
void nscsi_tape_device::scsi_command()
|
||||
{
|
||||
const u8 cmd = scsi_cmdbuf[0];
|
||||
const u8 lun = get_lun(scsi_cmdbuf[1] >> 5); // LUN may be overridden by IDENTIFY, per SCSI-2 section 7.2.2
|
||||
switch (cmd) { // these commands must be handled here, before tape changing logic
|
||||
case SC_INQUIRY: return handle_inquiry(lun);
|
||||
case SC_REQUEST_SENSE: return handle_request_sense(lun);
|
||||
}
|
||||
if (m_sequence_counter != m_image->sequence_counter()) { // tape just changed
|
||||
m_sequence_counter = m_image->sequence_counter();
|
||||
const bool had_tape = m_has_tape;
|
||||
m_has_tape = m_image->get_file();
|
||||
m_tape_changed = true;
|
||||
m_rw_pending = false;
|
||||
if (had_tape) { // we report no tape to let initiator know old tape has been removed
|
||||
LOG("command %s (ignored)\n", command_names[cmd], cmd);
|
||||
return report_no_medium();
|
||||
}
|
||||
}
|
||||
if (m_tape_changed) { // tape changed earlier
|
||||
m_tape_changed = false;
|
||||
if (m_has_tape) { // we report tape changed to let initiator know new tape has been inserted
|
||||
LOG("command %s (ignored)\n", command_names[cmd], cmd);
|
||||
return report_medium_changed();
|
||||
}
|
||||
}
|
||||
if (lun) // error: we don't support LUNs other than 0 for other commands
|
||||
return report_bad_lun(cmd, lun);
|
||||
|
||||
assert(m_has_tape == (bool)m_image->get_file());
|
||||
switch (cmd) {
|
||||
case SC_MODE_SELECT_6: return handle_mode_select_6();
|
||||
case SC_MODE_SENSE_6: return handle_mode_sense_6();
|
||||
case SC_SEND_DIAGNOSTIC: return handle_send_diagnostic();
|
||||
case SC_TEST_UNIT_READY: return handle_test_unit_ready();
|
||||
case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: return handle_prevent_allow_medium_removal();
|
||||
case SC_ERASE: return handle_erase();
|
||||
case SC_LOAD_UNLOAD: return handle_load_unload();
|
||||
case SC_LOCATE: return handle_locate();
|
||||
case SC_READ_6: return handle_read_6();
|
||||
case SC_READ_BLOCK_LIMITS: return handle_read_block_limits();
|
||||
case SC_READ_POSITION: return handle_read_position();
|
||||
case SC_RELEASE_UNIT: return handle_release_unit();
|
||||
case SC_RESERVE_UNIT: return handle_reserve_unit();
|
||||
case SC_REWIND: return handle_rewind();
|
||||
case SC_SPACE: return handle_space();
|
||||
case SC_WRITE_6: return handle_write_6();
|
||||
case SC_WRITE_FILEMARKS: return handle_write_filemarks();
|
||||
// TODO: support more optional commands
|
||||
case SC_CHANGE_DEFINITION:
|
||||
case SC_COMPARE:
|
||||
case SC_COPY:
|
||||
case SC_COPY_AND_VERIFY:
|
||||
case SC_LOG_SELECT:
|
||||
case SC_LOG_SENSE:
|
||||
case SC_MODE_SELECT_10:
|
||||
case SC_MODE_SENSE_10:
|
||||
case SC_READ_BUFFER:
|
||||
case SC_READ_REVERSE:
|
||||
case SC_RECEIVE_DIAGNOSTIC_RESULTS:
|
||||
case SC_RECOVER_BUFFERED_DATA:
|
||||
case SC_VERIFY_6:
|
||||
case SC_WRITE_BUFFER:
|
||||
default:
|
||||
nscsi_full_device::scsi_command();
|
||||
}
|
||||
}
|
||||
|
||||
u8 nscsi_tape_device::scsi_get_data(int id, int pos)
|
||||
{
|
||||
switch (id) {
|
||||
// we're handling READ(6); pos = 0 .. (m_rw_req_blocks_num * m_rw_req_block_len - 1)
|
||||
case TAPE_RW_BUF_ID: {
|
||||
const int relative_pos = pos % m_rw_req_block_len;
|
||||
if (m_rw_pending && relative_pos == 0) { // we need to fill buffer by reading another block
|
||||
assert(m_rw_len == 0);
|
||||
continue_handling_read_6();
|
||||
}
|
||||
if (m_rw_len == 0) // buffer is still empty, so reading block failed; condition has already been reported; all we can do here is pretend to read 0 from buffer
|
||||
return 0;
|
||||
|
||||
m_rw_len--;
|
||||
return m_rw_buf[relative_pos];
|
||||
}
|
||||
default:
|
||||
return nscsi_full_device::scsi_get_data(id, pos);
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::scsi_put_data(int id, int pos, u8 data)
|
||||
{
|
||||
switch (id) {
|
||||
// we're handling WRITE(6); pos == 0 .. (m_rw_req_blocks_num * m_rw_req_block_len - 1)
|
||||
case TAPE_RW_BUF_ID: {
|
||||
if (m_rw_len == m_rw_req_block_len) // buffer is still full, so writing block failed; condition has already been reported; all we can do is pretend to write to buffer
|
||||
return;
|
||||
|
||||
const int relative_pos = pos % m_rw_req_block_len;
|
||||
m_rw_buf[relative_pos] = data;
|
||||
m_rw_len++;
|
||||
if (m_rw_pending && relative_pos == m_rw_req_block_len - 1) { // we need to empty buffer by writing another block
|
||||
assert(m_rw_len == m_rw_req_block_len);
|
||||
continue_handling_write_6();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// we're handling MODE SELECT(6); pos == 0 .. (m_pl_len - 1)
|
||||
case TAPE_PL_BUF_ID:
|
||||
m_pl_buf[pos] = data;
|
||||
if (pos == m_pl_len - 1)
|
||||
continue_handling_mode_select_6();
|
||||
return;
|
||||
|
||||
default:
|
||||
return nscsi_full_device::scsi_put_data(id, pos, data);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// command handling
|
||||
|
||||
void nscsi_tape_device::handle_inquiry(const u8 lun) // mandatory; SCSI-2 section 8.2.5
|
||||
{
|
||||
const bool vpd_enable = scsi_cmdbuf[1] & 0x01; // should we respond with vital product data
|
||||
const u8 page_code = scsi_cmdbuf[2];
|
||||
const u8 alloc_len = scsi_cmdbuf[4]; // allocation length
|
||||
LOG("command INQUIRY lun=%d vpd_enable=%d page_code=0x%02x alloc_len=%d\n", lun, vpd_enable, page_code, alloc_len);
|
||||
if ((scsi_cmdbuf[1] & 0x1e) || scsi_cmdbuf[3]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (vpd_enable || page_code) // error: we don't support vital product data or pages other than 0
|
||||
return report_bad_cdb_field();
|
||||
|
||||
assert(sizeof(scsi_cmdbuf) >= 36);
|
||||
memset(scsi_cmdbuf, 0, 36);
|
||||
scsi_cmdbuf[0] = (lun == 0) ? 0x01 : 0x7f; // we support tape device on LUN 0 only, per 7.5.3(a)
|
||||
scsi_cmdbuf[1] = 0x80; // we support removing tape
|
||||
scsi_cmdbuf[2] = 0x02; // we're compliant with SCSI-2 only
|
||||
scsi_cmdbuf[3] = 0x02; // we use SCSI-2 response format
|
||||
scsi_cmdbuf[4] = 32; // additional length
|
||||
strncpy((char *)&scsi_cmdbuf[8], "MAME", 8); // vendor
|
||||
strncpy((char *)&scsi_cmdbuf[16], "SCSI tape drive", 16); // product
|
||||
strncpy((char *)&scsi_cmdbuf[32], "1.0", 4); // revision
|
||||
for (u32 i = 8; i < 36; i++) {
|
||||
if (scsi_cmdbuf[i] == 0)
|
||||
scsi_cmdbuf[i] = ' '; // pad strings with spaces
|
||||
}
|
||||
scsi_data_in(SBUF_MAIN, std::min(36, (const int)alloc_len));
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_mode_select_6() // mandatory; SCSI-2 sections 8.2.8, 8.3.3, 10.3.3
|
||||
{
|
||||
const bool page_format = scsi_cmdbuf[1] & 0x10; // ignored; we always treat pages according to SCSI-2
|
||||
const bool save_pages = scsi_cmdbuf[1] & 0x01;
|
||||
const u8 pl_len = scsi_cmdbuf[4]; // parameter list length
|
||||
LOG("command MODE SELECT(6) page_format=%d save_pages=%d pl_len=%d\n", page_format, save_pages, pl_len);
|
||||
if ((scsi_cmdbuf[1] & 0x0e) || scsi_cmdbuf[2] || scsi_cmdbuf[3]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (save_pages) // error: we don't support saving pages
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (pl_len == 0) // success: we have nothing to do
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
m_pl_len = pl_len;
|
||||
scsi_data_out(TAPE_PL_BUF_ID, pl_len);
|
||||
// we continue in continue_handling_mode_select_6 through scsi_put_data
|
||||
}
|
||||
|
||||
void nscsi_tape_device::continue_handling_mode_select_6()
|
||||
{
|
||||
// this function is called by scsi_put_data when parameter list buffer is filled
|
||||
if (m_pl_len < 4) // error: truncated header
|
||||
return report_bad_pl_len();
|
||||
|
||||
const u8 buf_mode = (m_pl_buf[2] & 0x70) >> 4; // ignored; we don't support buffering
|
||||
const u8 speed = m_pl_buf[2] & 0x0f; // ignored; we're always fast
|
||||
const u8 bd_len = m_pl_buf[3]; // block descriptor length
|
||||
LOG(" buf_mode=0x%02x speed=0x%02x bd_len=%d\n", buf_mode, speed, bd_len);
|
||||
if (m_pl_len < 4 + bd_len) // error: truncated block descriptor
|
||||
return report_bad_pl_len();
|
||||
|
||||
if (bd_len && bd_len != 8) // error: we don't accept more than 1 block descriptor
|
||||
return report_bad_pl_field();
|
||||
|
||||
if (bd_len == 8) { // 1 block descriptor
|
||||
const u8 density_code = m_pl_buf[4];
|
||||
const u32 blocks_num = get_u24be(&m_pl_buf[5]); // number of blocks
|
||||
const u32 block_len = get_u24be(&m_pl_buf[9]); // block length
|
||||
LOG(" density_code=0x%02x blocks_num=%d block_len=%d\n", density_code, blocks_num, block_len);
|
||||
if (density_code) // error: we don't support changing density
|
||||
return report_bad_pl_field();
|
||||
|
||||
if (blocks_num) // error: we don't support changing block length for only some amount of blocks
|
||||
return report_bad_pl_field();
|
||||
|
||||
if (block_len == 0) // error: requested block length is bad
|
||||
return report_bad_pl_field();
|
||||
|
||||
m_fixed_block_len = block_len;
|
||||
if (m_fixed_block_len > m_rw_buf_size) {
|
||||
m_rw_buf_size = m_fixed_block_len;
|
||||
m_rw_buf = std::make_unique<u8[]>(m_rw_buf_size);
|
||||
}
|
||||
}
|
||||
u32 page_num = 0;
|
||||
u32 page_pos = 4 + bd_len;
|
||||
while (page_pos < m_pl_len) { // we accept any number of pages
|
||||
if (m_pl_len < page_pos + 2) // error: truncated page
|
||||
return report_bad_pl_len();
|
||||
|
||||
const u8 page_code = m_pl_buf[page_pos] & 0x3f;
|
||||
const u8 page_len = m_pl_buf[page_pos + 1];
|
||||
LOG(" page_num=%d page_code=0x%02x page_len=%d\n", page_num, page_code, page_len);
|
||||
if (m_pl_len < page_pos + 2 + page_len) // error: truncated page
|
||||
return report_bad_pl_len();
|
||||
|
||||
switch (page_code) {
|
||||
case SPC_MEDIUM_PARTITION_PAGE_1: { // TODO: test more software and see if these pages need to be accommodated
|
||||
if (page_len < 6 || page_len % 2) // error: malformed page
|
||||
return report_bad_pl_field();
|
||||
|
||||
const u8 map = m_pl_buf[page_pos + 2]; // "maximum additional partitions"
|
||||
const u8 apd = m_pl_buf[page_pos + 3]; // "additional partitions defined"
|
||||
const bool fdp = m_pl_buf[page_pos + 4] & 0x80; // "fixed data partitions"
|
||||
const bool sdp = m_pl_buf[page_pos + 4] & 0x40; // "select data partitions"
|
||||
const bool idp = m_pl_buf[page_pos + 4] & 0x20; // "initiator-defined partitions"
|
||||
const u8 psum = (m_pl_buf[page_pos + 4] & 0x18) >> 3; // "partition size unit of measure"
|
||||
const u8 mfr = m_pl_buf[page_pos + 5]; // "medium format recognition"
|
||||
LOG(" map=%d apd=%d fdp=%d sdp=%d idp=%d psum=0x%x mfr=0x%02x\n", map, apd, fdp, sdp, idp, psum, mfr);
|
||||
u32 psd_num = 0; // partition size descriptor number
|
||||
u32 psd_pos = 8; // partition size descriptor position
|
||||
while (psd_pos < page_len) {
|
||||
const u16 ps = get_u16be(&m_pl_buf[page_pos + psd_pos]); // "partition size"
|
||||
LOG(" psd_num=%d ps=%d\n", psd_num, ps);
|
||||
psd_num++;
|
||||
psd_pos += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG(" *** BAD PAGE\n");
|
||||
}
|
||||
page_num++;
|
||||
page_pos += 2 + page_len;
|
||||
}
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_mode_sense_6() // mandatory; SCSI-2 sections 8.2.10, 8.3.3, 10.3.3
|
||||
{
|
||||
const bool bd_disable = scsi_cmdbuf[1] & 0x80; // should we not respond with block descriptor
|
||||
const u8 page_control = scsi_cmdbuf[2] >> 6;
|
||||
const u8 page_code = scsi_cmdbuf[2] & 0x3f;
|
||||
LOG("command MODE SENSE(6) bd_disable=%d page_control=0x%02x page_code=0x%02x\n", bd_disable, page_control, page_code);
|
||||
if ((scsi_cmdbuf[1] & 0x17) || scsi_cmdbuf[3]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (page_code && page_code != SPC_RETURN_ALL_MODE_PAGES) // error: we don't support pages other than 0
|
||||
return report_bad_cdb_field();
|
||||
|
||||
// TODO: support more pages, such as SPC_DATA_COMPRESSION_PAGE, and SPC_DEVICE_CONFIGURATION_PAGE with BIS bit set to indicate we support reporting block addresses in response to READ POSITION
|
||||
if (page_control == SPC_SAVED_VALUES) // error: we don't support saving parameters
|
||||
return report_no_saving_params();
|
||||
|
||||
const u8 resp_len = bd_disable ? 4 : 12; // response length
|
||||
assert(sizeof(scsi_cmdbuf) >= resp_len);
|
||||
memset(scsi_cmdbuf, 0, resp_len);
|
||||
scsi_cmdbuf[0] = resp_len - 1; // mode data length does not include itself, per SCSI-2 section 8.3.3
|
||||
scsi_cmdbuf[2] = (m_has_tape && m_image->get_file()->is_read_only()) ? 0x80 : 0; // device-specific parameter
|
||||
scsi_cmdbuf[3] = bd_disable ? 0 : 8; // block descriptor length
|
||||
if (!bd_disable) { // respond with block descriptor
|
||||
scsi_cmdbuf[4] = m_has_tape ? m_image->get_file()->get_density_code() : 0; // density code
|
||||
put_u24be(&scsi_cmdbuf[9], m_fixed_block_len); // block length
|
||||
}
|
||||
scsi_data_in(SBUF_MAIN, resp_len);
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_send_diagnostic() // mandatory; SCSI-2 section 8.2.15
|
||||
{
|
||||
const bool page_format = scsi_cmdbuf[1] & 0x10;
|
||||
const bool self_test = scsi_cmdbuf[1] & 0x04;
|
||||
const bool device_offline = scsi_cmdbuf[1] & 0x02;
|
||||
const bool unit_offline = scsi_cmdbuf[1] & 0x01;
|
||||
const u16 pl_len = get_u16be(&scsi_cmdbuf[3]); // parameter list length
|
||||
LOG("command SEND DIAGNOSTIC page_format=%d self_test=%d device_offline=%d unit_offline=%d pl_len=%d\n", page_format, self_test, device_offline, unit_offline, pl_len);
|
||||
if ((scsi_cmdbuf[1] & 0x08) || scsi_cmdbuf[2]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (pl_len) // error: we don't support any parameters
|
||||
return report_bad_cdb_field();
|
||||
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_test_unit_ready() // mandatory; SCSI-2 section 8.2.16
|
||||
{
|
||||
LOG("command TEST UNIT READY\n");
|
||||
if ((scsi_cmdbuf[1] & 0x1f) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || scsi_cmdbuf[4]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_prevent_allow_medium_removal() // optional; SCSI-2 section 9.2.4
|
||||
{
|
||||
const bool prevent = scsi_cmdbuf[4] & 0x01; // should we prevent or allow removing tape
|
||||
LOG("command %s MEDIUM REMOVAL\n", prevent ? "PREVENT" : "ALLOW");
|
||||
if ((scsi_cmdbuf[1] & 0x1f) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || (scsi_cmdbuf[4] & 0xfe)) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (prevent) {
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
// TODO: prevent removing tape, once MAME supports it
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
else { // allow
|
||||
// TODO: allow removing tape, once MAME supports it
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_erase() // mandatory; SCSI-2 section 10.2.1
|
||||
{
|
||||
const bool immed = scsi_cmdbuf[1] & 0x02; // ignored; we don't support buffering
|
||||
const bool eom = scsi_cmdbuf[1] & 0x01; // should we erase to EOM instead of 1 block; "long"
|
||||
LOG("command ERASE immed=%d eom=%d\n", immed, eom);
|
||||
if ((scsi_cmdbuf[1] & 0x1c) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || scsi_cmdbuf[4]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
if (m_image->get_file()->is_read_only()) // error: tape is read-only
|
||||
return report_read_only();
|
||||
|
||||
try {
|
||||
m_image->get_file()->erase(eom);
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
catch (std::exception &e) { // error: writing to tape failed
|
||||
osd_printf_error("ERASE FAILURE: nscsi_tape_device::handle_erase: %s\n", e.what());
|
||||
return report_erase_failure();
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_load_unload() // optional; SCSI-2 section 10.2.2
|
||||
{
|
||||
const bool immed = scsi_cmdbuf[1] & 0x01; // ignored; we don't support buffering
|
||||
const bool eom = scsi_cmdbuf[4] & 0x04; // should we rewind to EOM instead of BOM on unload
|
||||
const bool retension = scsi_cmdbuf[4] & 0x02; // ignored; we're always tense
|
||||
const bool load = scsi_cmdbuf[4] & 0x01; // should we load or unload tape
|
||||
LOG("command %s immed=%d eom=%d retension=%d\n", load ? "LOAD" : "UNLOAD", immed, eom, retension);
|
||||
if ((scsi_cmdbuf[1] & 0x1e) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || (scsi_cmdbuf[4] & 0xf8)) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (load && eom) // error: invalid combination of command options
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (load) {
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
m_image->get_file()->rewind(false);
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
else { // unload
|
||||
if (m_has_tape) // rewind tape if it's there
|
||||
m_image->get_file()->rewind(eom);
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_locate() // optional; SCSI-2 section 10.2.3
|
||||
{
|
||||
const bool block_addr_type = scsi_cmdbuf[1] & 0x04; // ignored; we always use logical block addresses
|
||||
const bool change_partition = scsi_cmdbuf[1] & 0x02;
|
||||
const bool immed = scsi_cmdbuf[1] & 0x01; // ignored; we don't support buffering
|
||||
const u32 req_block_addr = get_u32be(&scsi_cmdbuf[3]); // requested block address to locate
|
||||
const u8 partition = scsi_cmdbuf[8];
|
||||
LOG("command LOCATE block_addr_type=%d change_partition=%d immed=%d req_block_addr=%d partition=0x%02x\n", block_addr_type, change_partition, immed, req_block_addr, partition);
|
||||
if ((scsi_cmdbuf[1] & 0x18) || scsi_cmdbuf[2] || scsi_cmdbuf[7]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (change_partition) // error: we don't support partitions other than 0
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
try {
|
||||
const auto status = m_image->get_file()->locate_block(req_block_addr);
|
||||
switch (status) {
|
||||
case tape_status::OK: // success: we located requested block
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
case tape_status::EOD: // error: we reached EOD
|
||||
case tape_status::EOD_EW: // error: we reached EOD and we're also between EW and EOM
|
||||
return report_eod(0, status == tape_status::EOD_EW);
|
||||
|
||||
case tape_status::EOM: // error: we reached EOM
|
||||
return report_eom(false);
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (std::exception &e) { // error: reading from tape failed
|
||||
osd_printf_error("READ FAILURE: nscsi_tape_device::handle_locate: %s\n", e.what());
|
||||
return report_read_failure();
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_read_6() // mandatory; SCSI-2 section 10.2.4
|
||||
{
|
||||
const bool suppress = scsi_cmdbuf[1] & 0x02; // should we suppress indicating incorrect length
|
||||
const bool fixed = scsi_cmdbuf[1] & 0x01; // should we read many fixed-length blocks instead of 1 variable-length block
|
||||
const u32 req_items_num = get_u24be(&scsi_cmdbuf[2]); // requested number of blocks or bytes to read; "transfer length"
|
||||
LOG("command READ(6) suppress=%d fixed=%d req_items_num=%d\n", suppress, fixed, req_items_num);
|
||||
if (scsi_cmdbuf[1] & 0x1c) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (fixed && suppress) // error: invalid combination of command options
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
if (req_items_num == 0) // success: we have nothing to do
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
if (!fixed && req_items_num > m_rw_buf_size) {
|
||||
m_rw_buf_size = req_items_num;
|
||||
m_rw_buf = std::make_unique<u8[]>(m_rw_buf_size);
|
||||
}
|
||||
m_rw_pending = true;
|
||||
m_rw_len = 0;
|
||||
m_rw_blocks_num = 0;
|
||||
m_rw_req_blocks_num = fixed ? req_items_num : 1;
|
||||
m_rw_req_block_len = fixed ? m_fixed_block_len : req_items_num;
|
||||
m_rw_fixed_blocks = fixed;
|
||||
m_r_suppress_bad_len = suppress;
|
||||
assert(m_rw_req_block_len <= m_rw_buf_size);
|
||||
scsi_data_in(TAPE_RW_BUF_ID, m_rw_req_blocks_num * m_rw_req_block_len);
|
||||
// we continue in continue_handling_read_6 through scsi_get_data
|
||||
}
|
||||
|
||||
void nscsi_tape_device::continue_handling_read_6()
|
||||
{
|
||||
// this function is called by scsi_get_data every time read/write buffer is emptied
|
||||
assert(m_has_tape);
|
||||
assert(m_rw_len == 0);
|
||||
try {
|
||||
const auto result = m_image->get_file()->read_block(m_rw_buf.get(), m_rw_buf_size);
|
||||
const auto status = result.first;
|
||||
const u32 block_len = result.second;
|
||||
switch (status) {
|
||||
case tape_status::OK: { // success: we read another block
|
||||
const bool over = block_len > m_rw_req_block_len;
|
||||
const bool under = block_len < m_rw_req_block_len;
|
||||
if (over || (under && !m_r_suppress_bad_len)) { // error: we read block of incorrect length
|
||||
m_rw_pending = false;
|
||||
return report_bad_len(over, m_rw_fixed_blocks ? (m_rw_req_blocks_num - m_rw_blocks_num) : (m_rw_req_block_len - block_len));
|
||||
}
|
||||
if (under && m_r_suppress_bad_len)
|
||||
LOG(" *** UNDERLENGTH\n");
|
||||
m_rw_len = block_len;
|
||||
m_rw_blocks_num++;
|
||||
if (m_rw_blocks_num == m_rw_req_blocks_num) { // success: we're done
|
||||
m_rw_pending = false;
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
return; // we should read some more blocks
|
||||
}
|
||||
case tape_status::FILEMARK: // error: we reached filemark
|
||||
case tape_status::FILEMARK_EW: // error: we reached filemark and we're also between EW and EOM
|
||||
m_rw_pending = false;
|
||||
assert(block_len == 0);
|
||||
return report_filemark(m_rw_fixed_blocks ? (m_rw_req_blocks_num - m_rw_blocks_num) : m_rw_req_block_len, status == tape_status::FILEMARK_EW);
|
||||
|
||||
case tape_status::EOD: // error: we reached EOD
|
||||
case tape_status::EOD_EW: // error: we reached EOD and we're also between EW and EOM
|
||||
m_rw_pending = false;
|
||||
assert(block_len == 0);
|
||||
return report_eod(m_rw_fixed_blocks ? (m_rw_req_blocks_num - m_rw_blocks_num) : m_rw_req_block_len, status == tape_status::EOD_EW);
|
||||
|
||||
case tape_status::EOM: // error: we reached EOM
|
||||
m_rw_pending = false;
|
||||
assert(block_len == 0);
|
||||
return report_eom(false, m_rw_fixed_blocks ? (m_rw_req_blocks_num - m_rw_blocks_num) : m_rw_req_block_len);
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (std::exception &e) { // error: reading from tape failed
|
||||
osd_printf_error("READ FAILURE: nscsi_tape_device::continue_handling_read_6: %s\n", e.what());
|
||||
return report_read_failure();
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_read_block_limits() // mandatory; SCSI-2 section 10.2.5
|
||||
{
|
||||
LOG("command READ BLOCK LIMITS\n");
|
||||
if ((scsi_cmdbuf[1] & 0x1f) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || scsi_cmdbuf[4]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
assert(sizeof(scsi_cmdbuf) >= 6);
|
||||
memset(scsi_cmdbuf, 0, 6);
|
||||
put_u24be(&scsi_cmdbuf[1], 0xffffff); // 16MB; "maximum block length limit"
|
||||
put_u16be(&scsi_cmdbuf[4], m_fixed_block_len); // "minimum block length limit"
|
||||
scsi_data_in(SBUF_MAIN, 6);
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_read_position() // optional; SCSI-2 section 10.2.6
|
||||
{
|
||||
const bool block_addr_type = scsi_cmdbuf[1] & 0x01; // ignored; we always use logical block addresses
|
||||
LOG("command READ POSITION block_addr_type=%d\n", block_addr_type);
|
||||
if ((scsi_cmdbuf[1] & 0x1e) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || scsi_cmdbuf[4] || scsi_cmdbuf[5] || scsi_cmdbuf[6] || scsi_cmdbuf[7] || scsi_cmdbuf[8]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
try {
|
||||
const auto result = m_image->get_file()->read_position();
|
||||
const auto status = result.first;
|
||||
const u32 block_addr = result.second;
|
||||
switch (status) {
|
||||
case tape_status::OK: // we're right after some block (at EOD, or at filemark, or at another block); block address is valid
|
||||
LOG(" block_addr=%d\n", block_addr);
|
||||
break;
|
||||
case tape_status::BOM: // we're at BOM; block address is valid and 0
|
||||
LOG(" BOM block_addr=%d\n", block_addr);
|
||||
assert(block_addr == 0);
|
||||
break;
|
||||
case tape_status::EW: // we're between EW and EOM; block address is valid
|
||||
LOG(" EW block_addr=%d\n", block_addr);
|
||||
break;
|
||||
case tape_status::UNKNOWN: // we're after EOD or inside some block; block address is not valid
|
||||
case tape_status::UNKNOWN_EW: // we're after EOD or inside some block and also between EW and EOM; block address is not valid
|
||||
LOG(" UNKNOWN%s block_addr=%d\n", status == tape_status::UNKNOWN_EW ? " EW" : " ", block_addr);
|
||||
assert(block_addr == 0);
|
||||
break;
|
||||
case tape_status::EOM: // we're at EOM; block address is not valid
|
||||
LOG(" EOM block_addr=%d\n", block_addr);
|
||||
assert(block_addr == 0);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
const bool bom = status == tape_status::BOM;
|
||||
const bool eom = status == tape_status::EW
|
||||
|| status == tape_status::UNKNOWN_EW
|
||||
|| status == tape_status::EOM;
|
||||
const bool bpu = status == tape_status::UNKNOWN
|
||||
|| status == tape_status::UNKNOWN_EW
|
||||
|| status == tape_status::EOM;
|
||||
assert(sizeof(scsi_cmdbuf) >= 20);
|
||||
memset(scsi_cmdbuf, 0, 20);
|
||||
scsi_cmdbuf[0] = (bom ? 0x80 : 0) // is position at BOM
|
||||
| (eom ? 0x40 : 0) // is position between EW and EOM
|
||||
| (bpu ? 0x04 : 0); // is next block address invalid; "block position unknown"
|
||||
put_u32be(&scsi_cmdbuf[4], block_addr); // address of next block to be read/written; "first block location"
|
||||
put_u32be(&scsi_cmdbuf[8], block_addr); // address of last buffered block; we don't support buffering, so we set it to next block address; "last block location"
|
||||
scsi_data_in(SBUF_MAIN, 20);
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
catch (std::exception &e) { // error: reading from tape failed
|
||||
osd_printf_error("READ FAILURE: nscsi_tape_device::handle_read_position: %s\n", e.what());
|
||||
return report_read_failure();
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_release_unit() // mandatory; SCSI-2 section 10.2.9
|
||||
{
|
||||
const bool tpr = scsi_cmdbuf[1] & 0x10; // "third-party release"
|
||||
const u8 tpdi = (scsi_cmdbuf[1] & 0x0e) >> 1; // "third-party device id"
|
||||
LOG("command RELEASE UNIT tpr=%d tpdi=%d\n", tpr, tpdi);
|
||||
if ((scsi_cmdbuf[1] & 0x01) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || scsi_cmdbuf[4]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
// TODO: release unit, once MAME supports multiple initiators
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_reserve_unit() // mandatory; SCSI-2 section 10.2.10
|
||||
{
|
||||
const bool tpr = scsi_cmdbuf[1] & 0x10; // "third-party release"
|
||||
const u8 tpdi = (scsi_cmdbuf[1] & 0x0e) >> 1; // "third-party device id"
|
||||
LOG("command RESERVE UNIT tpr=%d tpdi=%d\n", tpr, tpdi);
|
||||
if ((scsi_cmdbuf[1] & 0x01) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || scsi_cmdbuf[4]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
// TODO: reserve unit, once MAME supports multiple initiators
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_rewind() // mandatory; SCSI-2 section 10.2.11
|
||||
{
|
||||
const bool immed = scsi_cmdbuf[1] & 0x01; // ignored; we don't support buffering
|
||||
LOG("command REWIND immed=%d\n", immed);
|
||||
if ((scsi_cmdbuf[1] & 0x1e) || scsi_cmdbuf[2] || scsi_cmdbuf[3] || scsi_cmdbuf[4]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
m_image->get_file()->rewind(false);
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_space() // mandatory; SCSI-2 section 10.2.12
|
||||
{
|
||||
const u8 items_type = scsi_cmdbuf[1] & 0x07; // what type of items should we space over
|
||||
const u32 req_dir_items_num = get_s24be(&scsi_cmdbuf[2]); // requested direction and number of items to space over; "count"
|
||||
LOG("command SPACE items_type=0x%02x req_dir_items_num=%d\n", items_type, req_dir_items_num);
|
||||
if (scsi_cmdbuf[1] & 0x18) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (items_type != STC_BLOCKS && items_type != STC_FILEMARKS && items_type != STC_END_OF_DATA) // error: we don't support spacing over setmarks or sequential filemarks
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
try {
|
||||
if (items_type == STC_END_OF_DATA) { // spacing EOD needs special handling
|
||||
const auto status = m_image->get_file()->space_eod();
|
||||
switch (status) {
|
||||
case tape_status::OK: // success: we reached EOD
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
case tape_status::EOM: // error: we reached EOM
|
||||
return report_eom(false, 0, true);
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
if (req_dir_items_num == 0) // success: we have nothing to do
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
const bool marks = items_type == STC_FILEMARKS;
|
||||
const bool reverse = req_dir_items_num < 0;
|
||||
const u32 req_items_num = reverse ? -req_dir_items_num : req_dir_items_num;
|
||||
const auto result = marks ? (reverse ? m_image->get_file()->space_filemarks_reverse(req_items_num)
|
||||
: m_image->get_file()->space_filemarks(req_items_num))
|
||||
: (reverse ? m_image->get_file()->space_blocks_reverse(req_items_num)
|
||||
: m_image->get_file()->space_blocks(req_items_num));
|
||||
const auto status = result.first;
|
||||
const u32 items_num = result.second;
|
||||
switch (status) {
|
||||
case tape_status::OK: // success: we reached requested item
|
||||
assert(items_num == req_items_num);
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
case tape_status::BOM: // error: we reached BOM
|
||||
return report_bom(req_items_num - items_num);
|
||||
|
||||
case tape_status::FILEMARK: // error: we reached filemark
|
||||
case tape_status::FILEMARK_EW: // error: we reached filemark and we're also between EW and EOM
|
||||
return report_filemark(req_items_num - items_num, status == tape_status::FILEMARK_EW);
|
||||
|
||||
case tape_status::EOD: // error: we reached EOD
|
||||
case tape_status::EOD_EW: // error: we reached EOD and we're also between EW and EOM
|
||||
return report_eod(req_items_num - items_num, status == tape_status::EOD_EW);
|
||||
|
||||
case tape_status::EOM: // error: we reached EOM
|
||||
return report_eom(false, req_items_num - items_num);
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (std::exception &e) { // error: reading from tape failed
|
||||
osd_printf_error("READ FAILURE: nscsi_tape_device::handle_space: %s\n", e.what());
|
||||
return report_read_failure();
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_write_6() // mandatory; SCSI-2 section 10.2.14
|
||||
{
|
||||
const bool fixed = scsi_cmdbuf[1] & 0x01; // should we write many fixed-length blocks instead of 1 variable-length block
|
||||
const u32 req_items_num = get_u24be(&scsi_cmdbuf[2]); // requested number of blocks or bytes to write; "transfer length"
|
||||
LOG("command WRITE(6) fixed=%d req_items_num=%d\n", fixed, req_items_num);
|
||||
if (scsi_cmdbuf[1] & 0x1e) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
if (m_image->get_file()->is_read_only()) // error: tape is read-only
|
||||
return report_read_only();
|
||||
|
||||
if (req_items_num == 0) // success: we have nothing to do
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
if (!fixed && req_items_num > m_rw_buf_size) {
|
||||
m_rw_buf_size = req_items_num;
|
||||
m_rw_buf = std::make_unique<u8[]>(m_rw_buf_size);
|
||||
}
|
||||
m_rw_pending = true;
|
||||
m_rw_len = 0;
|
||||
m_rw_blocks_num = 0;
|
||||
m_rw_req_blocks_num = fixed ? req_items_num : 1;
|
||||
m_rw_req_block_len = fixed ? m_fixed_block_len : req_items_num;
|
||||
m_rw_fixed_blocks = fixed;
|
||||
assert(m_rw_req_block_len <= m_rw_buf_size);
|
||||
scsi_data_out(TAPE_RW_BUF_ID, m_rw_req_blocks_num * m_rw_req_block_len);
|
||||
// we continue in continue_handling_write_6 through scsi_put_data
|
||||
}
|
||||
|
||||
void nscsi_tape_device::continue_handling_write_6()
|
||||
{
|
||||
// this function is called by scsi_put_data every time read/write buffer is filled
|
||||
assert(m_has_tape);
|
||||
assert(m_rw_len == m_rw_req_block_len);
|
||||
try {
|
||||
const auto status = m_image->get_file()->write_block(m_rw_buf.get(), m_rw_req_block_len);
|
||||
switch (status) {
|
||||
case tape_status::OK: // success: we wrote another block
|
||||
m_rw_len = 0;
|
||||
m_rw_blocks_num++;
|
||||
if (m_rw_blocks_num == m_rw_req_blocks_num) { // success: we're done
|
||||
m_rw_pending = false;
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
return; // we should write some more blocks
|
||||
|
||||
case tape_status::EW: // success: we wrote another block, but we're between EW and EOM
|
||||
m_rw_len = 0;
|
||||
m_rw_blocks_num++;
|
||||
m_rw_pending = false;
|
||||
return report_ew(m_rw_fixed_blocks ? (m_rw_req_blocks_num - m_rw_blocks_num) : m_rw_req_block_len);
|
||||
|
||||
case tape_status::EOM: // error: we reached EOM
|
||||
m_rw_pending = false;
|
||||
return report_eom(true, m_rw_fixed_blocks ? (m_rw_req_blocks_num - m_rw_blocks_num) : m_rw_req_block_len);
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (std::exception &e) { // error: writing to tape failed
|
||||
osd_printf_error("WRITE FAILURE: nscsi_tape_device::continue_handling_write_6: %s\n", e.what());
|
||||
return report_write_failure();
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_tape_device::handle_write_filemarks() // mandatory; SCSI-2 section 10.2.15
|
||||
{
|
||||
const bool setmarks = scsi_cmdbuf[1] & 0x02; // should we write setmarks instead of filemarks
|
||||
const bool immed = scsi_cmdbuf[1] & 0x01; // ignored; we don't support buffering
|
||||
const u32 req_marks_num = get_u24be(&scsi_cmdbuf[2]); // requested number of marks to write; "transfer length"
|
||||
LOG("command WRITE FILEMARKS setmarks=%d immed=%d req_marks_num=%d\n", setmarks, immed, req_marks_num);
|
||||
if (scsi_cmdbuf[1] & 0x1c) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (setmarks) // error: we don't support writing setmarks
|
||||
return report_bad_cdb_field();
|
||||
|
||||
if (!m_has_tape) // error: no tape
|
||||
return report_no_medium();
|
||||
|
||||
if (m_image->get_file()->is_read_only()) // error: tape is read-only
|
||||
return report_read_only();
|
||||
|
||||
try {
|
||||
const auto status = m_image->get_file()->write_filemarks(req_marks_num);
|
||||
switch (status) {
|
||||
case tape_status::OK: // success: we wrote all filemarks
|
||||
return scsi_status_complete(SS_GOOD);
|
||||
|
||||
case tape_status::EW: // success: we wrote all filemarks, but we're between EW and EOM
|
||||
return report_ew();
|
||||
|
||||
case tape_status::EOM: // error: we reached EOM
|
||||
return report_eom(true, req_marks_num);
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (std::exception &e) { // error: writing to tape failed
|
||||
osd_printf_error("WRITE FAILURE: nscsi_tape_device::handle_write_filemarks: %s\n", e.what());
|
||||
return report_write_failure();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
83
src/devices/bus/nscsi/tape.h
Normal file
83
src/devices/bus/nscsi/tape.h
Normal file
@ -0,0 +1,83 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
#ifndef MAME_BUS_NSCSI_TAPE_H
|
||||
#define MAME_BUS_NSCSI_TAPE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imagedev/simh_tape_image.h"
|
||||
#include "machine/nscsi_bus.h"
|
||||
|
||||
DECLARE_DEVICE_TYPE(NSCSI_TAPE, nscsi_tape_device);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class nscsi_tape_device : public nscsi_full_device
|
||||
{
|
||||
public:
|
||||
// construction
|
||||
nscsi_tape_device(const machine_config &config, const char *tag, device_t *owner, u32 clock = 0);
|
||||
|
||||
protected:
|
||||
nscsi_tape_device(const machine_config &config, device_type type, const char *tag, device_t *owner, u32 clock);
|
||||
|
||||
// device_t implementation
|
||||
virtual void device_add_mconfig(machine_config &config) override;
|
||||
|
||||
// nscsi_full_device implementation
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void scsi_command() override;
|
||||
virtual u8 scsi_get_data(int id, int pos) override;
|
||||
virtual void scsi_put_data(int id, int pos, u8 data) override;
|
||||
|
||||
// command handling
|
||||
void handle_inquiry(const u8 lun);
|
||||
void handle_mode_select_6();
|
||||
void continue_handling_mode_select_6();
|
||||
void handle_mode_sense_6();
|
||||
void handle_send_diagnostic();
|
||||
void handle_test_unit_ready();
|
||||
void handle_prevent_allow_medium_removal();
|
||||
void handle_erase();
|
||||
void handle_load_unload();
|
||||
void handle_locate();
|
||||
void handle_read_6();
|
||||
void continue_handling_read_6();
|
||||
void handle_read_block_limits();
|
||||
void handle_read_position();
|
||||
void handle_release_unit();
|
||||
void handle_reserve_unit();
|
||||
void handle_rewind();
|
||||
void handle_space();
|
||||
void handle_write_6();
|
||||
void continue_handling_write_6();
|
||||
void handle_write_filemarks();
|
||||
|
||||
// basic state
|
||||
required_device<simh_tape_image_device> m_image; // tape image
|
||||
u32 m_sequence_counter; // tape image identifier
|
||||
bool m_has_tape; // is tape image file available; cached value of m_image->get_file()
|
||||
bool m_tape_changed; // should we report medium changed next time we receive command
|
||||
u32 m_fixed_block_len; // fixed-length block length
|
||||
|
||||
// state for READ and WRITE
|
||||
u32 m_rw_buf_size; // size of read/write buffer
|
||||
std::unique_ptr<u8[]> m_rw_buf; // read/write buffer
|
||||
bool m_rw_pending; // should we try to read/write additional blocks
|
||||
u32 m_rw_len; // length of valid data in read/write buffer
|
||||
u32 m_rw_blocks_num; // number of blocks read/written so far
|
||||
u32 m_rw_req_blocks_num; // requested number of blocks to read/write
|
||||
u32 m_rw_req_block_len; // requested block length
|
||||
bool m_rw_fixed_blocks; // should we read/write many fixed-length blocks instead of 1 variable-length block
|
||||
bool m_r_suppress_bad_len; // should we suppress indicating incorrect length for read blocks
|
||||
|
||||
// state for MODE SELECT
|
||||
u8 m_pl_buf[256]; // parameter list buffer
|
||||
u32 m_pl_len; // length of valid data in parameter list buffer
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // MAME_BUS_NSCSI_TAPE_H
|
85
src/devices/imagedev/simh_tape_image.cpp
Normal file
85
src/devices/imagedev/simh_tape_image.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
#include "simh_tape_image.h"
|
||||
|
||||
// #define VERBOSE LOG_GENERAL
|
||||
// #define LOG_OUTPUT_FUNC osd_printf_info
|
||||
#include "logmacro.h"
|
||||
|
||||
DEFINE_DEVICE_TYPE(SIMH_TAPE_IMAGE, simh_tape_image_device, "simh_tape_image", "SIMH tape image");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// construction
|
||||
|
||||
simh_tape_image_device::simh_tape_image_device(const machine_config &config, device_type type, const char *tag, device_t *owner, u32 clock)
|
||||
: microtape_image_device(config, type, tag, owner, clock)
|
||||
, m_file()
|
||||
, m_interface(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
simh_tape_image_device::simh_tape_image_device(const machine_config &config, const char *tag, device_t *owner, u32 clock)
|
||||
: simh_tape_image_device(config, SIMH_TAPE_IMAGE, tag, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// device_image_interface implementation
|
||||
|
||||
std::pair<std::error_condition, std::string> simh_tape_image_device::call_load()
|
||||
{
|
||||
LOG("simh_tape_image_device::call_load filename=%s\n", filename());
|
||||
try {
|
||||
m_file.reset(new simh_tape_file(image_core_file(), length(), is_readonly()));
|
||||
}
|
||||
catch (std::exception &e) { // error: loading tape failed
|
||||
return std::make_pair(image_error::INTERNAL, std::string(e.what()));
|
||||
}
|
||||
return std::make_pair(std::error_condition(), std::string());
|
||||
}
|
||||
|
||||
std::pair<std::error_condition, std::string> simh_tape_image_device::call_create(int format_type, util::option_resolution *format_options)
|
||||
{
|
||||
LOG("simh_tape_image_device::call_create filename=%s format_type=%d\n", filename(), format_type);
|
||||
try {
|
||||
u64 file_size = 62914560; // we default to 60MB; TODO: allow specifying tape image size, once MAME supports it
|
||||
m_file.reset(new simh_tape_file(image_core_file(), file_size, is_readonly(), true));
|
||||
}
|
||||
catch (std::exception &e) { // error: creating tape failed
|
||||
return std::make_pair(image_error::INTERNAL, std::string(e.what()));
|
||||
}
|
||||
return std::make_pair(std::error_condition(), std::string());
|
||||
}
|
||||
|
||||
void simh_tape_image_device::call_unload()
|
||||
{
|
||||
LOG("simh_tape_image_device::call_unload\n");
|
||||
m_file.reset();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// device_t implementation
|
||||
|
||||
void simh_tape_image_device::device_config_complete()
|
||||
{
|
||||
LOG("simh_tape_image_device::device_config_complete\n");
|
||||
add_format(image_brief_type_name(), image_type_name(), file_extensions(), ""); // TODO: not sure these strings are correct
|
||||
}
|
||||
|
||||
void simh_tape_image_device::device_start()
|
||||
{
|
||||
LOG("simh_tape_image_device::device_start\n");
|
||||
}
|
||||
|
||||
void simh_tape_image_device::device_stop()
|
||||
{
|
||||
LOG("simh_tape_image_device::device_stop\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
52
src/devices/imagedev/simh_tape_image.h
Normal file
52
src/devices/imagedev/simh_tape_image.h
Normal file
@ -0,0 +1,52 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
#ifndef MAME_DEVICES_IMAGEDEV_SIMH_TAPE_IMAGE_H
|
||||
#define MAME_DEVICES_IMAGEDEV_SIMH_TAPE_IMAGE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "magtape.h"
|
||||
|
||||
#include "util/simh_tape_file.h"
|
||||
|
||||
DECLARE_DEVICE_TYPE(SIMH_TAPE_IMAGE, simh_tape_image_device);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class simh_tape_image_device : public microtape_image_device
|
||||
{
|
||||
public:
|
||||
// construction
|
||||
simh_tape_image_device(const machine_config &config, const char *tag, device_t *owner, u32 clock = 0);
|
||||
|
||||
// device_image_interface implementation
|
||||
virtual const char *image_interface() const noexcept override { return m_interface; }
|
||||
virtual const char *file_extensions() const noexcept override { return "tap"; }
|
||||
virtual const char *image_type_name() const noexcept override { return "tape"; }
|
||||
virtual const char *image_brief_type_name() const noexcept override { return "tap"; }
|
||||
virtual std::pair<std::error_condition, std::string> call_load() override;
|
||||
virtual std::pair<std::error_condition, std::string> call_create(int format_type, util::option_resolution *format_options) override;
|
||||
virtual void call_unload() override;
|
||||
|
||||
// miscellaneous
|
||||
inline void set_interface(const char *interface) { m_interface = interface; }
|
||||
inline simh_tape_file *get_file() const { return m_file.get(); }
|
||||
|
||||
protected:
|
||||
// construction
|
||||
simh_tape_image_device(const machine_config &config, device_type type, const char *tag, device_t *owner, u32 clock);
|
||||
|
||||
// device_t implementation
|
||||
virtual void device_config_complete() override;
|
||||
virtual void device_start() override;
|
||||
virtual void device_stop() override;
|
||||
|
||||
// state
|
||||
std::unique_ptr<simh_tape_file> m_file; // tape image file
|
||||
const char *m_interface; // image interface
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // MAME_DEVICES_IMAGEDEV_SIMH_TAPE_IMAGE_H
|
@ -3,6 +3,11 @@
|
||||
#include "emu.h"
|
||||
#include "nscsi_bus.h"
|
||||
|
||||
#include "util/multibyte.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#define LOG_UNSUPPORTED (1U << 1)
|
||||
#define LOG_STATE (1U << 2)
|
||||
#define LOG_CONTROL (1U << 3)
|
||||
@ -617,16 +622,145 @@ void nscsi_full_device::scsi_data_out(int buf, int size)
|
||||
c->param2 = size;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void nscsi_full_device::set_sense_data(const u8 sense_key, const u16 sense_key_code, const sense_data data)
|
||||
{
|
||||
assert(sizeof(scsi_sense_buffer) >= 18);
|
||||
assert(sense_key <= 0x0f);
|
||||
memset(scsi_sense_buffer, 0, 18);
|
||||
scsi_sense_buffer[0] = (data.invalid ? 0 : 0x80) // even though SCSI-2 section 8.2.14 implies valid bit should always be set, other sections such as 10.2.12 disagree!
|
||||
| (data.deferred ? 0x71 : 0x70);
|
||||
scsi_sense_buffer[2] = (data.filemark ? 0x80 : 0)
|
||||
| (data.eom ? 0x40 : 0)
|
||||
| (data.bad_len ? 0x20 : 0) // "incorrect length indicator"
|
||||
| sense_key;
|
||||
put_s32be(&scsi_sense_buffer[3], data.info);
|
||||
scsi_sense_buffer[7] = 10; // additional sense length
|
||||
put_u16be(&scsi_sense_buffer[12], sense_key_code);
|
||||
}
|
||||
|
||||
void nscsi_full_device::sense(bool deferred, uint8_t key, uint8_t asc, uint8_t ascq)
|
||||
{
|
||||
memset(scsi_sense_buffer, 0, sizeof(scsi_sense_buffer));
|
||||
scsi_sense_buffer[0] = deferred ? 0x71 : 0x70;
|
||||
scsi_sense_buffer[2] = key;
|
||||
scsi_sense_buffer[7] = sizeof(scsi_sense_buffer) - 8;
|
||||
scsi_sense_buffer[12] = asc;
|
||||
scsi_sense_buffer[13] = ascq;
|
||||
set_sense_data(key, (asc << 8) | ascq, { .deferred = deferred });
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_condition(const u8 sense_key, const u16 sense_key_code, const sense_data data)
|
||||
{
|
||||
set_sense_data(sense_key, sense_key_code, data);
|
||||
scsi_status_complete(SS_CHECK_CONDITION);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_bad_lun(const u8 cmd, const u8 lun)
|
||||
{
|
||||
LOG("%s (0x%02x) lun=%d\n *** BAD LUN\n", command_names[cmd], cmd, lun);
|
||||
report_condition(SK_ILLEGAL_REQUEST, SKC_LOGICAL_UNIT_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_bad_cmd(const u8 cmd)
|
||||
{
|
||||
LOG("%s (0x%02x)\n *** BAD COMMAND\n", command_names[cmd], cmd);
|
||||
report_condition(SK_ILLEGAL_REQUEST, SKC_INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_filemark(const s32 info, const bool eom)
|
||||
{
|
||||
LOG(" *** FILEMARK info=%d\n", info);
|
||||
report_condition(SK_NO_SENSE, SKC_FILEMARK_DETECTED, { .filemark = true, .eom = eom, .info = info });
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_bom(const s32 info)
|
||||
{
|
||||
LOG(" *** BOM info=%d\n", info);
|
||||
report_condition(SK_NO_SENSE, SKC_BEGINNING_OF_PARTITION_MEDIUM_DETECTED, { .eom = true, .info = info });
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_ew(const s32 info)
|
||||
{
|
||||
LOG(" EW info=%d\n", info);
|
||||
report_condition(SK_NO_SENSE, SKC_END_OF_PARTITION_MEDIUM_DETECTED, { .eom = true, .info = info });
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_eod(const s32 info, const bool eom)
|
||||
{
|
||||
LOG(" *** EOD info=%d\n", info);
|
||||
report_condition(SK_BLANK_CHECK, SKC_END_OF_DATA_DETECTED, { .eom = eom, .info = info });
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_eom(const bool write, const s32 info, const bool invalid)
|
||||
{
|
||||
LOG(" *** EOM info=%d invalid=%d\n", info, invalid);
|
||||
report_condition(write ? SK_VOLUME_OVERFLOW : SK_MEDIUM_ERROR, SKC_END_OF_PARTITION_MEDIUM_DETECTED, { .invalid = invalid, .eom = true, .info = info });
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_bad_len(const bool over, const s32 info)
|
||||
{
|
||||
LOG(" *** %sLENGTH BLOCK info=%d\n", over ? "OVER" : "UNDER", info);
|
||||
report_condition(SK_ILLEGAL_REQUEST, SKC_NO_ADDITIONAL_SENSE_INFORMATION, { .bad_len = true, .info = info });
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_bad_cdb_field()
|
||||
{
|
||||
LOG(" *** BAD CDB FIELD\n");
|
||||
report_condition(SK_ILLEGAL_REQUEST, SKC_INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_bad_pl_field()
|
||||
{
|
||||
LOG(" *** BAD PARAMETER LIST FIELD\n");
|
||||
report_condition(SK_ILLEGAL_REQUEST, SKC_INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_bad_pl_len()
|
||||
{
|
||||
LOG(" *** BAD PARAMETER LIST LENGTH\n");
|
||||
report_condition(SK_ILLEGAL_REQUEST, SKC_PARAMETER_LIST_LENGTH_ERROR);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_no_saving_params()
|
||||
{
|
||||
LOG(" *** NO SAVING PARAMETERS\n");
|
||||
report_condition(SK_ILLEGAL_REQUEST, SKC_SAVING_PARAMETERS_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_no_medium()
|
||||
{
|
||||
LOG(" *** NO MEDIUM\n");
|
||||
report_condition(SK_NOT_READY, SKC_MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_medium_changed()
|
||||
{
|
||||
LOG(" MEDIUM CHANGED\n");
|
||||
report_condition(SK_UNIT_ATTENTION, SKC_NOT_READY_TO_READY_TRANSITION_MEDIUM_MAY_HAVE_CHANGED);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_read_only()
|
||||
{
|
||||
LOG(" *** READ ONLY\n");
|
||||
report_condition(SK_DATA_PROTECT, SKC_WRITE_PROTECTED);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_read_failure()
|
||||
{
|
||||
LOG(" *** READ FAILURE\n");
|
||||
report_condition(SK_MEDIUM_ERROR, SKC_UNRECOVERED_READ_ERROR);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_write_failure()
|
||||
{
|
||||
LOG(" *** WRITE FAILURE\n");
|
||||
report_condition(SK_MEDIUM_ERROR, SKC_WRITE_ERROR);
|
||||
}
|
||||
|
||||
void nscsi_full_device::report_erase_failure()
|
||||
{
|
||||
LOG(" *** ERASE FAILURE\n");
|
||||
report_condition(SK_MEDIUM_ERROR, SKC_ERASE_FAILURE);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void nscsi_full_device::scsi_unknown_command()
|
||||
{
|
||||
std::string txt = util::string_format("Unhandled command %s (%d):", command_names[scsi_cmdbuf[0]], scsi_cmdsize);
|
||||
@ -640,15 +774,15 @@ void nscsi_full_device::scsi_unknown_command()
|
||||
|
||||
void nscsi_full_device::scsi_command()
|
||||
{
|
||||
switch(scsi_cmdbuf[0]) {
|
||||
const u8 cmd = scsi_cmdbuf[0];
|
||||
const u8 lun = get_lun(scsi_cmdbuf[1] >> 5); // LUN may be overridden by IDENTIFY, per SCSI-2 section 7.2.2
|
||||
switch(cmd) {
|
||||
case SC_REZERO_UNIT:
|
||||
LOG("command REZERO UNIT\n");
|
||||
scsi_status_complete(SS_GOOD);
|
||||
break;
|
||||
case SC_REQUEST_SENSE:
|
||||
LOG("command REQUEST SENSE alloc=%d\n", scsi_cmdbuf[4]);
|
||||
scsi_data_in(SBUF_SENSE, scsi_cmdbuf[4] ? std::min(scsi_cmdbuf[4], u8(sizeof(scsi_sense_buffer))) : 4);
|
||||
scsi_status_complete(SS_GOOD);
|
||||
handle_request_sense(lun);
|
||||
break;
|
||||
default:
|
||||
scsi_unknown_command();
|
||||
@ -656,6 +790,18 @@ void nscsi_full_device::scsi_command()
|
||||
}
|
||||
}
|
||||
|
||||
void nscsi_full_device::handle_request_sense(const u8 lun) // mandatory; SCSI-2 section 8.2.14
|
||||
{
|
||||
const u8 alloc_len = scsi_cmdbuf[4]; // allocation length
|
||||
LOG("command REQUEST SENSE lun=%d alloc_len=%d\n", lun, alloc_len);
|
||||
if ((scsi_cmdbuf[1] & 0x1f) || scsi_cmdbuf[2] || scsi_cmdbuf[3]) // error: reserved bits set
|
||||
return report_bad_cdb_field();
|
||||
|
||||
assert(sizeof(scsi_sense_buffer) >= 18);
|
||||
scsi_data_in(SBUF_SENSE, std::min(18, (const int)alloc_len));
|
||||
scsi_status_complete(SS_GOOD);
|
||||
}
|
||||
|
||||
void nscsi_full_device::scsi_message()
|
||||
{
|
||||
if(scsi_cmdbuf[0] & 0x80) {
|
||||
|
@ -179,6 +179,304 @@ protected:
|
||||
SK_COMPLETED = 0x0f
|
||||
};
|
||||
|
||||
// SCSI SPACE type codes; SCSI-2 table 189
|
||||
enum {
|
||||
STC_BLOCKS = 0x00,
|
||||
STC_FILEMARKS = 0x01,
|
||||
STC_SEQUENTIAL_FILEMARKS = 0x02,
|
||||
STC_END_OF_DATA = 0x03,
|
||||
STC_SETMARKS = 0x04,
|
||||
STC_SEQUENTIAL_SETMARKS = 0x05
|
||||
};
|
||||
|
||||
// SCSI MODE SENSE page controls; SCSI-2 table 55
|
||||
enum {
|
||||
SPC_CURRENT_VALUES = 0x00,
|
||||
SPC_CHANGEABLE_VALUES = 0x01,
|
||||
SPC_DEFAULT_VALUES = 0x02,
|
||||
SPC_SAVED_VALUES = 0x03
|
||||
};
|
||||
|
||||
// SCSI MODE SELECT/SENSE page codes; SCSI-2 tables 95, 155, 199, 216, 267, 296, 324, 350, 363; extra SCSI-3 codes from "The SCSI Bench Reference"
|
||||
enum {
|
||||
SPC_VENDOR_SPECIFIC = 0x00,
|
||||
SPC_READ_WRITE_ERROR_RECOVERY_PAGE = 0x01,
|
||||
SPC_READ_ERROR_RECOVERY_PAGE = 0x01,
|
||||
SPC_DISCONNECT_RECONNECT_PAGE = 0x02,
|
||||
SPC_FORMAT_DEVICE_PAGE = 0x03,
|
||||
SPC_PARALLEL_PRINTER_INTERFACE_PAGE = 0x03,
|
||||
SPC_MEASUREMENT_UNITS_PAGE = 0x03,
|
||||
SPC_RIGID_DISK_GEOMETRY_PAGE = 0x04,
|
||||
SPC_SERIAL_PRINTER_INTERFACE_PAGE = 0x04,
|
||||
SPC_FLEXIBLE_DISK_PAGE = 0x05,
|
||||
SPC_PRINTER_OPTIONS_PAGE = 0x05,
|
||||
SPC_OPTICAL_MEMORY_PAGE = 0x06,
|
||||
SPC_VERIFY_ERROR_RECOVERY_PAGE = 0x07,
|
||||
SPC_CACHING_PAGE = 0x08,
|
||||
SPC_PERIPHERAL_DEVICE_PAGE = 0x09,
|
||||
SPC_CONTROL_MODE_PAGE = 0x0a,
|
||||
SPC_MEDIUM_TYPES_SUPPORTED_PAGE = 0x0b,
|
||||
SPC_NOTCH_AND_PARTITION_PAGE = 0x0c,
|
||||
SPC_DIRECT_ACCESS_POWER_CONDITION_PAGE = 0x0d, // direct access device only
|
||||
SPC_CD_ROM_PAGE = 0x0d,
|
||||
SPC_CD_ROM_AUDIO_CONTROL_PAGE = 0x0e,
|
||||
SPC_DATA_COMPRESSION_PAGE = 0x0f,
|
||||
SPC_XOR_CONTROL_MODE_PAGE = 0x10,
|
||||
SPC_DEVICE_CONFIGURATION_PAGE = 0x10,
|
||||
SPC_MEDIUM_PARTITION_PAGE_1 = 0x11,
|
||||
SPC_MEDIUM_PARTITION_PAGE_2 = 0x12,
|
||||
SPC_MEDIUM_PARTITION_PAGE_3 = 0x13,
|
||||
SPC_MEDIUM_PARTITION_PAGE_4 = 0x14,
|
||||
SPC_POWER_CONDITION_PAGE = 0x1a, // all other device types
|
||||
SPC_INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE = 0x1c,
|
||||
SPC_ELEMENT_ADDRESS_ASSIGNMENT_PAGE = 0x1d,
|
||||
SPC_TRANSPORT_GEOMETRY_PARAMETERS_PAGE = 0x1e,
|
||||
SPC_DEVICE_CAPABILITIES_PAGE = 0x1f,
|
||||
SPC_RETURN_ALL_MODE_PAGES = 0x3f
|
||||
};
|
||||
|
||||
// SCSI additional sense codes and additional sense code qualifiers, packaged together as additional qualified sense key codes; SCSI-2 table 71; extra SCSI-3 codes from "The SCSI Bench Reference"
|
||||
enum {
|
||||
SKC_NO_ADDITIONAL_SENSE_INFORMATION = 0x0000,
|
||||
SKC_FILEMARK_DETECTED = 0x0001,
|
||||
SKC_END_OF_PARTITION_MEDIUM_DETECTED = 0x0002,
|
||||
SKC_SETMARK_DETECTED = 0x0003,
|
||||
SKC_BEGINNING_OF_PARTITION_MEDIUM_DETECTED = 0x0004,
|
||||
SKC_END_OF_DATA_DETECTED = 0x0005,
|
||||
SKC_IO_PROCESS_TERMINATED = 0x0006,
|
||||
SKC_AUDIO_PLAY_OPERATION_IN_PROGRESS = 0x0011,
|
||||
SKC_AUDIO_PLAY_OPERATION_PAUSED = 0x0012,
|
||||
SKC_AUDIO_PLAY_OPERATION_SUCCESSFULLY_COMPLETED = 0x0013,
|
||||
SKC_AUDIO_PLAY_OPERATION_STOPPED_DUE_TO_ERROR = 0x0014,
|
||||
SKC_NO_CURRENT_AUDIO_STATUS_TO_RETURN = 0x0015,
|
||||
SKC_OPERATION_IN_PROGRESS = 0x0016,
|
||||
SKC_CLEANING_REQUESTED = 0x0017,
|
||||
SKC_NO_INDEX_SECTOR_SIGNAL = 0x0100,
|
||||
SKC_NO_SEEK_COMPLETE = 0x0200,
|
||||
SKC_PERIPHERAL_DEVICE_WRITE_FAULT = 0x0300,
|
||||
SKC_NO_WRITE_CURRENT = 0x0301,
|
||||
SKC_EXCESSIVE_WRITE_ERRORS = 0x0302,
|
||||
SKC_LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE = 0x0400,
|
||||
SKC_LOGICAL_UNIT_IS_IN_PROCESS_OF_BECOMING_READY = 0x0401,
|
||||
SKC_LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED = 0x0402,
|
||||
SKC_LOGICAL_UNIT_NOT_READY_MANUAL_INTERVENTION_REQUIRED = 0x0403,
|
||||
SKC_LOGICAL_UNIT_NOT_READY_FORMAT_IN_PROGRESS = 0x0404,
|
||||
SKC_LOGICAL_UNIT_NOT_READY_OPERATION_IN_PROGRESS = 0x0407,
|
||||
SKC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION = 0x0500,
|
||||
SKC_NO_REFERENCE_POSITION_FOUND = 0x0600,
|
||||
SKC_MULTIPLE_PERIPHERAL_DEVICES_SELECTED = 0x0700,
|
||||
SKC_LOGICAL_UNIT_COMMUNICATION_FAILURE = 0x0800,
|
||||
SKC_LOGICAL_UNIT_COMMUNICATION_TIME_OUT = 0x0801,
|
||||
SKC_LOGICAL_UNIT_COMMUNICATION_PARITY_ERROR = 0x0802,
|
||||
SKC_TRACK_FOLLOWING_ERROR = 0x0900,
|
||||
SKC_TRACKING_SERVO_FAILURE = 0x0901,
|
||||
SKC_FOCUS_SERVO_FAILURE = 0x0902,
|
||||
SKC_SPINDLE_SERVO_FAILURE = 0x0903,
|
||||
SKC_HEAD_SELECT_FAULT = 0x0904,
|
||||
SKC_ERROR_LOG_OVERFLOW = 0x0a00,
|
||||
SKC_WARNING = 0x0b00,
|
||||
SKC_WARNING_SPECIFIC_TEMPERATURE_EXCEEDED = 0x0b01,
|
||||
SKC_WRITE_ERROR = 0x0c00,
|
||||
SKC_WRITE_ERROR_RECOVERED_WITH_AUTO_REALLOCATION = 0x0c01,
|
||||
SKC_WRITE_ERROR_AUTO_REALLOCATION_FAILED = 0x0c02,
|
||||
SKC_WRITE_ERROR_RECOMMEND_REASSIGNMENT = 0x0c03,
|
||||
SKC_COMPRESSION_CHECK_MISCOMPARE_ERROR = 0x0c04,
|
||||
SKC_DATA_EXPANSION_OCCURRED_DURING_COMPRESSION = 0x0c05,
|
||||
SKC_BLOCK_NOT_COMPRESSABLE = 0x0c06,
|
||||
SKC_ID_CRC_OR_ECC_ERROR = 0x1000,
|
||||
SKC_UNRECOVERED_READ_ERROR = 0x1100,
|
||||
SKC_READ_RETRIES_EXHAUSTED = 0x1101,
|
||||
SKC_ERROR_TOO_LONG_TO_CORRECT = 0x1102,
|
||||
SKC_MULTIPLE_READ_ERRORS = 0x1103,
|
||||
SKC_UNRECOVERED_READ_ERROR_AUTO_REALLOCATE_FAILED = 0x1104,
|
||||
SKC_L_EC_UNCORRECTABLE_ERROR = 0x1105,
|
||||
SKC_CIRC_UNRECOVERED_ERROR = 0x1106,
|
||||
SKC_DATA_RESYNCHRONIZATION_ERROR = 0x1107,
|
||||
SKC_INCOMPLETE_BLOCK_READ = 0x1108,
|
||||
SKC_NO_GAP_FOUND = 0x1109,
|
||||
SKC_MISCORRECTED_ERROR = 0x110a,
|
||||
SKC_UNRECOVERED_READ_ERROR_RECOMMEND_REASSIGNMENT = 0x110b,
|
||||
SKC_UNRECOVERED_READ_ERROR_RECOMMEND_REWRITE = 0x110c,
|
||||
SKC_DECOMPRESSION_CRC_ERROR = 0x110d,
|
||||
SKC_CANNOT_DECOMPRESS_USING_DECLARED_ALGORITHM = 0x100e,
|
||||
SKC_ADDRESS_MARK_NOT_FOUND_FOR_ID_FIELD = 0x1200,
|
||||
SKC_ADDRESS_MARK_NOT_FOUND_FOR_DATA_FIELD = 0x1300,
|
||||
SKC_RECORDED_ENTITY_NOT_FOUND = 0x1400,
|
||||
SKC_RECORD_NOT_FOUND = 0x1401,
|
||||
SKC_FILEMARK_OR_SETMARK_NOT_FOUND = 0x1402,
|
||||
SKC_END_OF_DATA_NOT_FOUND = 0x1403,
|
||||
SKC_BLOCK_SEQUENCE_ERROR = 0x1404,
|
||||
SKC_RECORD_NOT_FOUND_RECOMMEND_REASSIGNMENT = 0x1405,
|
||||
SKC_RECORD_NOT_FOUND_DATA_AUTO_REALLOCATED = 0x1406,
|
||||
SKC_RANDOM_POSITIONING_ERROR = 0x1500,
|
||||
SKC_MECHANICAL_POSITIONING_ERROR = 0x1501,
|
||||
SKC_POSITIONING_ERROR_DETECTED_BY_READ_OF_MEDIUM = 0x1502,
|
||||
SKC_DATA_SYNCHRONIZATION_MARK_ERROR = 0x1600,
|
||||
SKC_DATA_SYNCHRONIZATION_ERROR_DATA_REWRITTEN = 0x1601,
|
||||
SKC_DATA_SYNCHRONIZATION_ERROR_RECOMMEND_REWRITE = 0x1602,
|
||||
SKC_DATA_SYNCHRONIZATION_ERROR_DATA_AUTO_REALLOCATED = 0x1603,
|
||||
SKC_DATA_SYNCHRONIZATION_ERROR_RECOMMEND_REASSIGNMENT = 0x1604,
|
||||
SKC_RECOVERED_DATA_WITH_NO_ERROR_CORRECTION_APPLIED = 0x1700,
|
||||
SKC_RECOVERED_DATA_WITH_RETRIES = 0x1701,
|
||||
SKC_RECOVERED_DATA_WITH_POSITIVE_HEAD_OFFSET = 0x1702,
|
||||
SKC_RECOVERED_DATA_WITH_NEGATIVE_HEAD_OFFSET = 0x1703,
|
||||
SKC_RECOVERED_DATA_WITH_RETRIES_AND_OR_CIRC_APPLIED = 0x1704,
|
||||
SKC_RECOVERED_DATA_USING_PREVIOUS_SECTOR_ID = 0x1705,
|
||||
SKC_RECOVERED_DATA_WITHOUT_ECC_DATA_AUTO_REALLOCATED = 0x1706,
|
||||
SKC_RECOVERED_DATA_WITHOUT_ECC_RECOMMEND_REASSIGNMENT = 0x1707,
|
||||
SKC_RECOVERED_DATA_WITHOUT_ECC_RECOMMEND_REWRITE = 0x1708,
|
||||
SKC_RECOVERED_DATA_WITHOUT_ECC_DATA_REWRITTEN = 0x1809,
|
||||
SKC_RECOVERED_DATA_WITH_ERROR_CORRECTION_APPLIED = 0x1800,
|
||||
SKC_RECOVERED_DATA_WITH_ERROR_CORRECTION_RETRIES_APPLIED = 0x1801,
|
||||
SKC_RECOVERED_DATA_DATA_AUTO_REALLOCATED = 0x1802,
|
||||
SKC_RECOVERED_DATA_WITH_CIRC = 0x1803,
|
||||
SKC_RECOVERED_DATA_WITH_L_EC = 0x1804,
|
||||
SKC_RECOVERED_DATA_RECOMMEND_REASSIGNMENT = 0x1805,
|
||||
SKC_RECOVERED_DATA_RECOMMEND_REWRITE = 0x1806,
|
||||
SKC_RECOVERED_DATA_WITH_ECC_DATA_REWRITTEN = 0x1807,
|
||||
SKC_DEFECT_LIST_ERROR = 0x1900,
|
||||
SKC_DEFECT_LIST_NOT_AVAILABLE = 0x1901,
|
||||
SKC_DEFECT_LIST_ERROR_IN_PRIMARY_LIST = 0x1902,
|
||||
SKC_DEFECT_LIST_ERROR_IN_GROWN_LIST = 0x1903,
|
||||
SKC_PARAMETER_LIST_LENGTH_ERROR = 0x1a00,
|
||||
SKC_SYNCHRONOUS_DATA_TRANSFER_ERROR = 0x1b00,
|
||||
SKC_DEFECT_LIST_NOT_FOUND = 0x1c00,
|
||||
SKC_PRIMARY_DEFECT_LIST_NOT_FOUND = 0x1c01,
|
||||
SKC_GROWN_DEFECT_LIST_NOT_FOUND = 0x1c02,
|
||||
SKC_MISCOMPARE_DURING_VERIFY_OPERATION = 0x1d00,
|
||||
SKC_RECOVERED_ID_WITH_ECC_CORRECTION = 0x1e00,
|
||||
SKC_PARTIAL_DEFECT_LIST_TRANSFER = 0x1f00,
|
||||
SKC_INVALID_COMMAND_OPERATION_CODE = 0x2000,
|
||||
SKC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE = 0x2100,
|
||||
SKC_INVALID_ELEMENT_ADDRESS = 0x2101,
|
||||
SKC_ILLEGAL_FUNCTION = 0x2200, // "should use 0x2000, 0x2400, or 0x2600"
|
||||
SKC_INVALID_FIELD_IN_CDB = 0x2400,
|
||||
SKC_LOGICAL_UNIT_NOT_SUPPORTED = 0x2500,
|
||||
SKC_INVALID_FIELD_IN_PARAMETER_LIST = 0x2600,
|
||||
SKC_PARAMETER_NOT_SUPPORTED = 0x2601,
|
||||
SKC_PARAMETER_VALUE_INVALID = 0x2602,
|
||||
SKC_THRESHOLD_PARAMETERS_NOT_SUPPORTED = 0x2603,
|
||||
SKC_INVALID_RELEASE_OF_ACTIVE_PERSISTENT_RESERVATION = 0x2604,
|
||||
SKC_WRITE_PROTECTED = 0x2700,
|
||||
SKC_NOT_READY_TO_READY_TRANSITION_MEDIUM_MAY_HAVE_CHANGED = 0x2800,
|
||||
SKC_IMPORT_OR_EXPORT_ELEMENT_ACCESSED = 0x2801,
|
||||
SKC_POWER_ON_RESET_OR_BUS_DEVICE_RESET_OCCURRED = 0x2900,
|
||||
SKC_POWER_ON_OCCURRED = 0x2901,
|
||||
SKC_SCSI_BUS_RESET_OCCURRED = 0x2902,
|
||||
SKC_SCSI_BUS_DEVICE_RESET_FUNCTION_OCCURRED = 0x2903,
|
||||
SKC_PARAMETERS_CHANGED = 0x2a00,
|
||||
SKC_MODE_PARAMETERS_CHANGED = 0x2a01,
|
||||
SKC_LOG_PARAMETERS_CHANGED = 0x2a02,
|
||||
SKC_RESERVATIONS_PREEMPTED = 0x2a03,
|
||||
SKC_COPY_CANNOT_EXECUTE_SINCE_HOST_CANNOT_DISCONNECT = 0x2b00,
|
||||
SKC_COMMAND_SEQUENCE_ERROR = 0x2c00,
|
||||
SKC_TOO_MANY_WINDOWS_SPECIFIED = 0x2c01,
|
||||
SKC_INVALID_COMBINATION_OF_WINDOWS_SPECIFIED = 0x2c02,
|
||||
SKC_OVERWRITE_ERROR_ON_UPDATE_IN_PLACE = 0x2d00,
|
||||
SKC_COMMANDS_CLEARED_BY_ANOTHER_INITIATOR = 0x2f00,
|
||||
SKC_INCOMPATIBLE_MEDIUM_INSTALLED = 0x3000,
|
||||
SKC_CANNOT_READ_MEDIUM_UNKNOWN_FORMAT = 0x3001,
|
||||
SKC_CANNOT_READ_MEDIUM_INCOMPATIBLE_FORMAT = 0x3002,
|
||||
SKC_CLEANING_CARTRIDGE_INSTALLED = 0x3003,
|
||||
SKC_CANNOT_WRITE_MEDIUM_UNKNOWN_FORMAT = 0x3004,
|
||||
SKC_CANNOT_WRITE_MEDIUM_INCOMPATIBLE_FORMAT = 0x3005,
|
||||
SKC_CANNOT_FORMAT_MEDIUM_INCOMPATIBLE_MEDIUM = 0x3006,
|
||||
SKC_CLEANING_FAILURE = 0x3007,
|
||||
SKC_MEDIUM_FORMAT_CORRUPTED = 0x3100,
|
||||
SKC_FORMAT_COMMAND_FAILED = 0x3101,
|
||||
SKC_NO_DEFECT_SPARE_LOCATION_AVAILABLE = 0x3200,
|
||||
SKC_DEFECT_LIST_UPDATE_FAILURE = 0x3201,
|
||||
SKC_TAPE_LENGTH_ERROR = 0x3300,
|
||||
SKC_RIBBON_INK_OR_TONER_FAILURE = 0x3600,
|
||||
SKC_ROUNDED_PARAMETER = 0x3700,
|
||||
SKC_SAVING_PARAMETERS_NOT_SUPPORTED = 0x3900,
|
||||
SKC_MEDIUM_NOT_PRESENT = 0x3a00,
|
||||
SKC_SEQUENTIAL_POSITIONING_ERROR = 0x3b00,
|
||||
SKC_TAPE_POSITION_ERROR_AT_BEGINNING_OF_MEDIUM = 0x3b01,
|
||||
SKC_TAPE_POSITION_ERROR_AT_END_OF_MEDIUM = 0x3b02,
|
||||
SKC_TAPE_OR_ELECTRONIC_VERTICAL_FORMS_UNIT_NOT_READY = 0x3b03,
|
||||
SKC_SLEW_FAILURE = 0x3b04,
|
||||
SKC_PAPER_JAM = 0x3b05,
|
||||
SKC_FAILED_TO_SENSE_TOP_OF_FORM = 0x3b06,
|
||||
SKC_FAILED_TO_SENSE_BOTTOM_OF_FORM = 0x3b07,
|
||||
SKC_REPOSITION_ERROR = 0x3b08,
|
||||
SKC_READ_PAST_END_OF_MEDIUM = 0x3b09,
|
||||
SKC_READ_PAST_BEGINNING_OF_MEDIUM = 0x3b0a,
|
||||
SKC_POSITION_PAST_END_OF_MEDIUM = 0x3b0b,
|
||||
SKC_POSITION_PAST_BEGINNING_OF_MEDIUM = 0x3b0c,
|
||||
SKC_MEDIUM_DESTINATION_ELEMENT_FULL = 0x3b0d,
|
||||
SKC_MEDIUM_SOURCE_ELEMENT_EMPTY = 0x3b0e,
|
||||
SKC_MEDIUM_MAGAZINE_NOT_ACCESSIBLE = 0x3b11,
|
||||
SKC_MEDIUM_MAGAZINE_REMOVED = 0x3b12,
|
||||
SKC_MEDIUM_MAGAZINE_INSERTED = 0x3b13,
|
||||
SKC_MEDIUM_MAGAZINE_LOCKED = 0x3b14,
|
||||
SKC_MEDIUM_MAGAZINE_UNLOCKED = 0x3b15,
|
||||
SKC_INVALID_BITS_IN_IDENTIFY_MESSAGE = 0x3d00,
|
||||
SKC_LOGICAL_UNIT_HAS_NOT_SELF_CONFIGURED_YET = 0x3e00,
|
||||
SKC_TARGET_OPERATING_CONDITIONS_HAVE_CHANGED = 0x3f00,
|
||||
SKC_MICROCODE_HAS_BEEN_CHANGED = 0x3f01,
|
||||
SKC_CHANGED_OPERATING_DEFINITION = 0x3f02,
|
||||
SKC_INQUIRY_DATA_HAS_CHANGED = 0x3f03,
|
||||
SKC_RAM_FAILURE = 0x4000, // "should use 0x40nn"
|
||||
SKC_DIAGNOSTIC_FAILURE_ON_COMPONENT_NN = 0x4000, // LSB is nn (0x80-0xff)
|
||||
SKC_DATA_PATH_FAILURE = 0x4100, // "should use 0x40nn"
|
||||
SKC_POWER_ON_OR_SELF_TEST_FAILURE = 0x4200, // "should use 0x40nn"
|
||||
SKC_MESSAGE_ERROR = 0x4300,
|
||||
SKC_INTERNAL_TARGET_FAILURE = 0x4400,
|
||||
SKC_SELECT_OR_RESELECT_FAILURE = 0x4500,
|
||||
SKC_UNSUCCESSFUL_SOFT_RESET = 0x4600,
|
||||
SKC_SCSI_PARITY_ERROR = 0x4700,
|
||||
SKC_INITIATOR_DETECTED_ERROR_MESSAGE_RECEIVED = 0x4800,
|
||||
SKC_INVALID_MESSAGE_ERROR = 0x4900,
|
||||
SKC_COMMAND_PHASE_ERROR = 0x4a00,
|
||||
SKC_DATA_PHASE_ERROR = 0x4b00,
|
||||
SKC_LOGICAL_UNIT_FAILED_SELF_CONFIGURATION = 0x4c00,
|
||||
SKC_TAGGED_OVERLAPPED_COMMANDS_NN = 0x4d00, // "queue tag"; LSB is nn
|
||||
SKC_OVERLAPPED_COMMANDS_ATTEMPTED = 0x4e00,
|
||||
SKC_WRITE_APPEND_ERROR = 0x5000,
|
||||
SKC_WRITE_APPEND_POSITION_ERROR = 0x5001,
|
||||
SKC_POSITION_ERROR_RELATED_TO_TIMING = 0x5002,
|
||||
SKC_ERASE_FAILURE = 0x5100,
|
||||
SKC_CARTRIDGE_FAULT = 0x5200,
|
||||
SKC_MEDIA_LOAD_OR_EJECT_FAILED = 0x5300,
|
||||
SKC_UNLOAD_TAPE_FAILURE = 0x5301,
|
||||
SKC_MEDIUM_REMOVAL_PREVENTED = 0x5302,
|
||||
SKC_SCSI_TO_HOST_SYSTEM_INTERFACE_FAILURE = 0x5400,
|
||||
SKC_SYSTEM_RESOURCE_FAILURE = 0x5500,
|
||||
SKC_SYSTEM_BUFFER_FULL = 0x5501,
|
||||
SKC_UNABLE_TO_RECOVER_TABLE_OF_CONTENTS = 0x5700,
|
||||
SKC_GENERATION_DOES_NOT_EXIST = 0x5800,
|
||||
SKC_UPDATED_BLOCK_READ = 0x5900,
|
||||
SKC_OPERATOR_REQUEST_OR_STATE_CHANGE_INPUT = 0x5a00, // "unspecified"
|
||||
SKC_OPERATOR_MEDIUM_REMOVAL_REQUEST = 0x5a01,
|
||||
SKC_OPERATOR_SELECTED_WRITE_PROTECT = 0x5a02,
|
||||
SKC_OPERATOR_SELECTED_WRITE_PERMIT = 0x5a03,
|
||||
SKC_LOG_EXCEPTION = 0x5b00,
|
||||
SKC_THRESHOLD_CONDITION_MET = 0x5b01,
|
||||
SKC_LOG_COUNTER_AT_MAXIMUM = 0x5b02,
|
||||
SKC_LOG_LIST_CODES_EXHAUSTED = 0x5b03,
|
||||
SKC_RPL_STATUS_CHANGE = 0x5c00,
|
||||
SKC_SPINDLES_SYNCHRONIZED = 0x5c01,
|
||||
SKC_SPINDLES_NOT_SYNCHRONIZED = 0x5c02,
|
||||
SKC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED = 0x5d00,
|
||||
SKC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED_FALSE = 0x5dff, // what?!
|
||||
SKC_LOW_POWER_CONDITION_ON = 0x5e00,
|
||||
SKC_IDLE_CONDITION_ACTIVATED_BY_TIMER = 0x5e01,
|
||||
SKC_STANDBY_CONDITION_ACTIVATED_BY_TIMER = 0x5e02,
|
||||
SKC_IDLE_CONDITION_ACTIVATED_BY_COMMAND = 0x5e03,
|
||||
SKC_STANDBY_CONDITION_ACTIVATED_BY_COMMAND = 0x5e04,
|
||||
SKC_LAMP_FAILURE = 0x6000,
|
||||
SKC_VIDEO_ACQUISITION_ERROR = 0x6100,
|
||||
SKC_UNABLE_TO_ACQUIRE_VIDEO = 0x6101,
|
||||
SKC_OUT_OF_FOCUS = 0x6102,
|
||||
SKC_SCAN_HEAD_POSITIONING_ERROR = 0x6200,
|
||||
SKC_END_OF_USER_AREA_ENCOUNTERED_ON_THIS_TRACK = 0x6300,
|
||||
SKC_ILLEGAL_MODE_FOR_THIS_TRACK = 0x6400,
|
||||
SKC_VOLTAGE_FAULT = 0x6500,
|
||||
SKC_DECOMPRESSION_EXCEPTION_SHORT_ALGORITHM_ID_OF_NN = 0x7000, // LSB is nn
|
||||
SKC_DECOMPRESSION_EXCEPTION_LONG_ALGORITHM_ID = 0x7100
|
||||
};
|
||||
|
||||
// SCSI addtional sense code qualifiers
|
||||
enum {
|
||||
SK_ASC_INVALID_FIELD_IN_CDB = 0x24,
|
||||
@ -356,12 +654,44 @@ protected:
|
||||
|
||||
TIMER_CALLBACK_MEMBER(update_tick);
|
||||
|
||||
void handle_request_sense(const u8 lun);
|
||||
void scsi_unknown_command();
|
||||
void scsi_status_complete(uint8_t st);
|
||||
void scsi_data_in(int buf, int size);
|
||||
void scsi_data_out(int buf, int size);
|
||||
|
||||
struct sense_data {
|
||||
bool invalid;
|
||||
bool deferred;
|
||||
bool filemark;
|
||||
bool eom;
|
||||
bool bad_len;
|
||||
u8 sense_key;
|
||||
s32 info;
|
||||
u16 sense_key_code;
|
||||
};
|
||||
void set_sense_data(const u8 sense_key, const u16 sense_key_code, const sense_data data = {});
|
||||
void sense(bool deferred, uint8_t key, uint8_t asc = 0, uint8_t ascq = 0);
|
||||
void report_condition(const u8 sense_key, const u16 sense_key_code, const sense_data data = {});
|
||||
void report_bad_lun(const u8 cmd, const u8 lun);
|
||||
void report_bad_cmd(const u8 cmd);
|
||||
void report_filemark(const s32 info = 0, const bool eom = false);
|
||||
void report_bom(const s32 info = 0);
|
||||
void report_ew(const s32 info = 0);
|
||||
void report_eod(const s32 info = 0, const bool eom = false);
|
||||
void report_eom(const bool write, const s32 info = 0, const bool invalid = 0);
|
||||
void report_bad_len(const bool over, const s32 info = 0);
|
||||
void report_bad_cdb_field();
|
||||
void report_bad_pl_field();
|
||||
void report_bad_pl_len();
|
||||
void report_no_saving_params();
|
||||
void report_no_medium();
|
||||
void report_medium_changed();
|
||||
void report_read_only();
|
||||
void report_read_failure();
|
||||
void report_write_failure();
|
||||
void report_erase_failure();
|
||||
|
||||
int get_lun(int def = 0);
|
||||
void bad_lun();
|
||||
|
||||
|
251
src/lib/util/multibyte.h
Normal file
251
src/lib/util/multibyte.h
Normal file
@ -0,0 +1,251 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
#ifndef MAME_LIB_UTIL_MULTIBYTE_H
|
||||
#define MAME_LIB_UTIL_MULTIBYTE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "coretmpl.h"
|
||||
#include "osdcomm.h"
|
||||
|
||||
using osd::u8;
|
||||
using osd::u16;
|
||||
using osd::u32;
|
||||
using osd::u64;
|
||||
using osd::s8;
|
||||
using osd::s16;
|
||||
using osd::s32;
|
||||
using osd::s64;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// unsigned big-endian
|
||||
|
||||
inline const u16 get_u16be(const u8 *const buf)
|
||||
{
|
||||
return ((const u16)buf[0] << 8)
|
||||
| ((const u16)buf[1] << 0);
|
||||
}
|
||||
|
||||
inline const u32 get_u24be(const u8 *const buf)
|
||||
{
|
||||
return ((const u32)buf[0] << 16)
|
||||
| ((const u32)buf[1] << 8)
|
||||
| ((const u32)buf[2] << 0);
|
||||
}
|
||||
|
||||
inline const u32 get_u32be(const u8 *const buf)
|
||||
{
|
||||
return ((const u32)buf[0] << 24)
|
||||
| ((const u32)buf[1] << 16)
|
||||
| ((const u32)buf[2] << 8)
|
||||
| ((const u32)buf[3] << 0);
|
||||
}
|
||||
|
||||
inline const u64 get_u64be(const u8 *const buf)
|
||||
{
|
||||
return ((const u64)buf[0] << 56)
|
||||
| ((const u64)buf[1] << 48)
|
||||
| ((const u64)buf[2] << 40)
|
||||
| ((const u64)buf[3] << 36)
|
||||
| ((const u64)buf[4] << 24)
|
||||
| ((const u64)buf[5] << 16)
|
||||
| ((const u64)buf[6] << 8)
|
||||
| ((const u64)buf[7] << 0);
|
||||
}
|
||||
|
||||
inline void put_u16be(u8 *buf, const u16 data)
|
||||
{
|
||||
buf[0] = data >> 8;
|
||||
buf[1] = data >> 0;
|
||||
}
|
||||
|
||||
inline void put_u24be(u8 *buf, const u32 data)
|
||||
{
|
||||
buf[0] = data >> 16;
|
||||
buf[1] = data >> 8;
|
||||
buf[2] = data >> 0;
|
||||
}
|
||||
|
||||
inline void put_u32be(u8 *buf, const u32 data)
|
||||
{
|
||||
buf[0] = data >> 24;
|
||||
buf[1] = data >> 16;
|
||||
buf[2] = data >> 8;
|
||||
buf[3] = data >> 0;
|
||||
}
|
||||
|
||||
inline void put_u64be(u8 *buf, const u64 data)
|
||||
{
|
||||
buf[0] = data >> 56;
|
||||
buf[1] = data >> 48;
|
||||
buf[2] = data >> 40;
|
||||
buf[3] = data >> 36;
|
||||
buf[4] = data >> 24;
|
||||
buf[5] = data >> 16;
|
||||
buf[6] = data >> 8;
|
||||
buf[7] = data >> 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// signed big-endian
|
||||
|
||||
inline const s16 get_s16be(const u8 *const buf)
|
||||
{
|
||||
return get_u16be(buf);
|
||||
}
|
||||
|
||||
inline const s32 get_s24be(const u8 *const buf)
|
||||
{
|
||||
return util::sext(get_u24be(buf), 24);
|
||||
}
|
||||
|
||||
inline const s32 get_s32be(const u8 *const buf)
|
||||
{
|
||||
return get_u32be(buf);
|
||||
}
|
||||
|
||||
inline const s64 get_s64be(const u8 *const buf)
|
||||
{
|
||||
return get_u64be(buf);
|
||||
}
|
||||
|
||||
inline void put_s16be(u8 *buf, const s16 data)
|
||||
{
|
||||
put_u16be(buf, data);
|
||||
}
|
||||
|
||||
inline void put_s24be(u8 *buf, const s32 data)
|
||||
{
|
||||
put_u24be(buf, data);
|
||||
}
|
||||
|
||||
inline void put_s32be(u8 *buf, const s32 data)
|
||||
{
|
||||
put_u32be(buf, data);
|
||||
}
|
||||
|
||||
inline void put_s64be(u8 *buf, const s64 data)
|
||||
{
|
||||
put_u64be(buf, data);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// unsigned little-endian
|
||||
|
||||
inline const u16 get_u16le(const u8 *const buf)
|
||||
{
|
||||
return ((const u16)buf[0] << 0)
|
||||
| ((const u16)buf[1] << 8);
|
||||
}
|
||||
|
||||
inline const u32 get_u24le(const u8 *const buf)
|
||||
{
|
||||
return ((const u32)buf[0] << 0)
|
||||
| ((const u32)buf[1] << 8)
|
||||
| ((const u32)buf[2] << 16);
|
||||
}
|
||||
|
||||
inline const u32 get_u32le(const u8 *const buf)
|
||||
{
|
||||
return ((const u32)buf[0] << 0)
|
||||
| ((const u32)buf[1] << 8)
|
||||
| ((const u32)buf[2] << 16)
|
||||
| ((const u32)buf[3] << 24);
|
||||
}
|
||||
|
||||
inline const u64 get_u64le(const u8 *const buf)
|
||||
{
|
||||
return ((const u64)buf[0] << 0)
|
||||
| ((const u64)buf[1] << 8)
|
||||
| ((const u64)buf[2] << 16)
|
||||
| ((const u64)buf[3] << 24)
|
||||
| ((const u64)buf[4] << 32)
|
||||
| ((const u64)buf[5] << 40)
|
||||
| ((const u64)buf[6] << 48)
|
||||
| ((const u64)buf[7] << 56);
|
||||
}
|
||||
|
||||
inline void put_u16le(u8 *buf, const u16 data)
|
||||
{
|
||||
buf[0] = data >> 0;
|
||||
buf[1] = data >> 8;
|
||||
}
|
||||
|
||||
inline void put_u24le(u8 *buf, const u32 data)
|
||||
{
|
||||
buf[0] = data >> 0;
|
||||
buf[1] = data >> 8;
|
||||
buf[2] = data >> 16;
|
||||
}
|
||||
|
||||
inline void put_u32le(u8 *buf, const u32 data)
|
||||
{
|
||||
buf[0] = data >> 0;
|
||||
buf[1] = data >> 8;
|
||||
buf[2] = data >> 16;
|
||||
buf[3] = data >> 24;
|
||||
}
|
||||
|
||||
inline void put_u64le(u8 *buf, const u64 data)
|
||||
{
|
||||
buf[0] = data >> 0;
|
||||
buf[1] = data >> 8;
|
||||
buf[2] = data >> 16;
|
||||
buf[3] = data >> 24;
|
||||
buf[4] = data >> 32;
|
||||
buf[5] = data >> 40;
|
||||
buf[6] = data >> 48;
|
||||
buf[7] = data >> 56;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// signed little-endian
|
||||
|
||||
inline const s16 get_s16le(const u8 *const buf)
|
||||
{
|
||||
return get_u16le(buf);
|
||||
}
|
||||
|
||||
inline const s32 get_s24le(const u8 *const buf)
|
||||
{
|
||||
return util::sext(get_u24le(buf), 24);
|
||||
}
|
||||
|
||||
inline const s32 get_s32le(const u8 *const buf)
|
||||
{
|
||||
return get_u32le(buf);
|
||||
}
|
||||
|
||||
inline const s64 get_s64le(const u8 *const buf)
|
||||
{
|
||||
return get_u64le(buf);
|
||||
}
|
||||
|
||||
inline void put_s16le(u8 *buf, const s16 data)
|
||||
{
|
||||
put_u16le(buf, data);
|
||||
}
|
||||
|
||||
inline void put_s24le(u8 *buf, const s32 data)
|
||||
{
|
||||
put_u24le(buf, data);
|
||||
}
|
||||
|
||||
inline void put_s32le(u8 *buf, const s32 data)
|
||||
{
|
||||
put_u32le(buf, data);
|
||||
}
|
||||
|
||||
inline void put_s64le(u8 *buf, const s64 data)
|
||||
{
|
||||
put_u64le(buf, data);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // MAME_LIB_UTIL_MULTIBYTE_H
|
625
src/lib/util/simh_tape_file.cpp
Normal file
625
src/lib/util/simh_tape_file.cpp
Normal file
@ -0,0 +1,625 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
// best read together with SIMH magtape spec (rev 17 Jan 2022)
|
||||
// http://simh.trailing-edge.com/docs/simh_magtape.pdf
|
||||
|
||||
#include "multibyte.h"
|
||||
#include "simh_tape_file.h"
|
||||
|
||||
#include "ioprocs.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// constants and helpers
|
||||
|
||||
enum class simh_marker : u32 {
|
||||
TAPE_MARK = 0x00000000, // filemark; TODO: SIMH doesn't define setmarks
|
||||
ERASE_GAP = 0xfffffffe,
|
||||
EOM = 0xffffffff
|
||||
};
|
||||
|
||||
inline const bool is_simh_marker_half_gap_forward(const simh_marker marker)
|
||||
{
|
||||
// this function is used when we're reading normally (from BOM to EOM); returns true for erase gap markers that have been half overwritten
|
||||
return (const u32)marker == 0xfffeffff;
|
||||
}
|
||||
|
||||
inline const bool is_simh_marker_half_gap_reverse(const simh_marker marker)
|
||||
{
|
||||
// this function is used when we're reading in reverse (from EOM to BOM); returns true for erase gap markers that have been half overwritten
|
||||
return (const u32)marker >= 0xffff0000 && (const u32)marker <= 0xfffffffd;
|
||||
}
|
||||
|
||||
inline const bool is_simh_marker_eod_forward(const simh_marker marker)
|
||||
{
|
||||
// this function is used when we're reading normally (from BOM to EOM); returns true for markers that we consider EOD
|
||||
return marker == simh_marker::ERASE_GAP
|
||||
|| is_simh_marker_half_gap_forward(marker)
|
||||
|| marker == simh_marker::EOM; // logical EOM
|
||||
}
|
||||
|
||||
inline const bool is_simh_marker_eod_reverse(const simh_marker marker)
|
||||
{
|
||||
// this function is used when we're reading in reverse (from EOM to BOM); returns true for markers that we consider EOD
|
||||
return marker == simh_marker::ERASE_GAP
|
||||
|| is_simh_marker_half_gap_reverse(marker)
|
||||
|| marker == simh_marker::EOM; // logical EOM
|
||||
}
|
||||
|
||||
enum class simh_marker_class : u8 {
|
||||
GOOD_DATA_RECORD = 0x0,
|
||||
PRIVATE_DATA_RECORD_1 = 0x1,
|
||||
PRIVATE_DATA_RECORD_2 = 0x2,
|
||||
PRIVATE_DATA_RECORD_3 = 0x3,
|
||||
PRIVATE_DATA_RECORD_4 = 0x4,
|
||||
PRIVATE_DATA_RECORD_5 = 0x5,
|
||||
PRIVATE_DATA_RECORD_6 = 0x6,
|
||||
PRIVATE_MARKER = 0x7,
|
||||
BAD_DATA_RECORD = 0x8,
|
||||
RESERVED_DATA_RECORD_9 = 0x9,
|
||||
RESERVED_DATA_RECORD_A = 0xa,
|
||||
RESERVED_DATA_RECORD_B = 0xb,
|
||||
RESERVED_DATA_RECORD_C = 0xc,
|
||||
RESERVED_DATA_RECORD_D = 0xd,
|
||||
TAPE_DESCRIPTION_DATA_RECORD = 0xe,
|
||||
RESERVED_MARKER = 0xf
|
||||
};
|
||||
|
||||
inline const simh_marker_class get_simh_marker_class(const simh_marker marker)
|
||||
{
|
||||
return (const simh_marker_class)((const u32)marker >> 28);
|
||||
}
|
||||
|
||||
inline const u32 get_simh_marker_value(const simh_marker marker)
|
||||
{
|
||||
return (const u32)marker & 0x0fffffff;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// construction
|
||||
|
||||
simh_tape_file::simh_tape_file(util::random_read_write &file, const u64 file_size, const bool read_only, const bool create)
|
||||
: m_file(file)
|
||||
, m_file_size(file_size)
|
||||
, m_read_only(read_only)
|
||||
, m_pos(0)
|
||||
{
|
||||
if (create) {
|
||||
if (read_only) // error: we cannot create read-only file
|
||||
throw std::runtime_error("read-only file");
|
||||
|
||||
write_byte_repeat(0, 0xff, m_file_size); // we assume simh_marker::EOM == 0xffffffff
|
||||
}
|
||||
}
|
||||
|
||||
simh_tape_file::~simh_tape_file()
|
||||
{
|
||||
m_file.finalize();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// internal operations
|
||||
|
||||
void simh_tape_file::raw_seek(const u64 pos) const
|
||||
{
|
||||
std::error_condition err = m_file.seek(pos, SEEK_SET);
|
||||
if (err) // error: we failed to seek to expected byte offset
|
||||
throw std::runtime_error(std::string("failed seek: ") + err.message());
|
||||
}
|
||||
|
||||
void simh_tape_file::raw_read(u8 *const buf, const u32 len) const
|
||||
{
|
||||
size_t actual_len;
|
||||
std::error_condition err = m_file.read(buf, len, actual_len);
|
||||
if (err || actual_len != len) // error: we failed to read expected number of bytes
|
||||
throw std::runtime_error(std::string("failed read: ") + (err ? err.message() : std::string("unexpected length")));
|
||||
}
|
||||
|
||||
void simh_tape_file::raw_write(const u8 *const buf, const u32 len) const
|
||||
{
|
||||
size_t actual_len;
|
||||
std::error_condition err = m_file.write(buf, len, actual_len);
|
||||
if (err || actual_len != len) // error: we failed to write expected number of bytes
|
||||
throw std::runtime_error(std::string("failed write: ") + (err ? err.message() : std::string("unexpected length")));
|
||||
}
|
||||
|
||||
void simh_tape_file::read_bytes(const u64 pos, u8 *const buf, const u32 len) const
|
||||
{
|
||||
raw_seek(pos);
|
||||
raw_read(buf, len);
|
||||
}
|
||||
|
||||
const u32 simh_tape_file::read_word(const u64 pos) const
|
||||
{
|
||||
const u32 tmp_len = 4;
|
||||
u8 tmp_buf[tmp_len];
|
||||
raw_seek(pos);
|
||||
raw_read(tmp_buf, tmp_len);
|
||||
return get_u32le(tmp_buf);
|
||||
}
|
||||
|
||||
void simh_tape_file::write_bytes(const u64 pos, const u8 *const buf, const u32 len) const
|
||||
{
|
||||
raw_seek(pos);
|
||||
raw_write(buf, len);
|
||||
}
|
||||
|
||||
void simh_tape_file::write_byte_repeat(const u64 pos, const u8 data, const u32 len) const
|
||||
{
|
||||
const u32 tmp_len = 4096;
|
||||
u8 tmp_buf[tmp_len];
|
||||
memset(tmp_buf, data, std::min(len, tmp_len));
|
||||
raw_seek(pos);
|
||||
for (u32 i = 0; i < len / tmp_len; i++)
|
||||
raw_write(tmp_buf, tmp_len);
|
||||
raw_write(tmp_buf, len % tmp_len);
|
||||
}
|
||||
|
||||
void simh_tape_file::write_word(const u64 pos, const u32 data) const
|
||||
{
|
||||
const u32 tmp_len = 4;
|
||||
u8 tmp_buf[tmp_len];
|
||||
put_u32le(tmp_buf, data);
|
||||
raw_seek(pos);
|
||||
raw_write(tmp_buf, tmp_len);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// position-preserving operations
|
||||
|
||||
const std::pair<const tape_status, const u32> simh_tape_file::read_position() const
|
||||
{
|
||||
// this module only keeps track of current tape position, therefore this function scans from BOM to find and return current block address, taking linear time; TODO: this module could be rewritten to also keep track of current block address, therefore enabling this function to only take constant time
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
if (m_pos == 0) // we're at BOM and next block is 0
|
||||
return std::pair(tape_status::BOM, 0);
|
||||
|
||||
if (m_pos == m_file_size) // we're at physical EOM and there is no next block
|
||||
return std::pair(tape_status::EOM, 0);
|
||||
|
||||
// we need to count how many blocks are between BOM and us
|
||||
u32 blocks_num = 0;
|
||||
u64 tmp_pos = 0;
|
||||
while (tmp_pos < m_pos) {
|
||||
if (tmp_pos + 4 > m_file_size) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(tmp_pos);
|
||||
if (marker == simh_marker::TAPE_MARK) { // we skip filemarks
|
||||
tmp_pos += 4;
|
||||
continue;
|
||||
}
|
||||
if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
|
||||
return std::pair(is_ew() ? tape_status::UNKNOWN_EW : tape_status::UNKNOWN, 0);
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
tmp_pos += 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
|
||||
if (tmp_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
// we counted another block
|
||||
tmp_pos += read_len;
|
||||
blocks_num++;
|
||||
break;
|
||||
default: // we try to skip other blocks
|
||||
if (tmp_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
tmp_pos += read_len;
|
||||
}
|
||||
}
|
||||
if (m_pos < tmp_pos) // we're inside some block
|
||||
return std::pair(is_ew() ? tape_status::UNKNOWN_EW : tape_status::UNKNOWN, 0);
|
||||
|
||||
// we're right after some block (at EOD, or at filemark, or at another block)
|
||||
assert(tmp_pos == m_pos);
|
||||
return std::pair(is_ew() ? tape_status::EW : tape_status::OK, blocks_num);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// non-destructive operations
|
||||
|
||||
void simh_tape_file::rewind(const bool eom)
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
m_pos = eom ? m_file_size : 0;
|
||||
}
|
||||
|
||||
const tape_status simh_tape_file::locate_block(const u32 req_block_addr)
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
u32 blocks_num = 0;
|
||||
m_pos = 0;
|
||||
while (m_pos < m_file_size) {
|
||||
if (m_pos + 4 > m_file_size) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(m_pos);
|
||||
if (marker == simh_marker::TAPE_MARK) { // we skip filemarks
|
||||
m_pos += 4;
|
||||
continue;
|
||||
}
|
||||
if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
|
||||
return is_ew() ? tape_status::EOD_EW : tape_status::EOD;
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
m_pos += 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
if (blocks_num == req_block_addr) // success: we located requested block
|
||||
return tape_status::OK;
|
||||
|
||||
m_pos += read_len;
|
||||
blocks_num++;
|
||||
break;
|
||||
default: // we try to skip other blocks
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos += read_len;
|
||||
}
|
||||
}
|
||||
// error: we reached physical EOM
|
||||
assert(m_pos == m_file_size);
|
||||
return tape_status::EOM;
|
||||
}
|
||||
|
||||
const tape_status simh_tape_file::space_eod()
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
while (m_pos < m_file_size) {
|
||||
if (m_pos + 4 > m_file_size) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(m_pos);
|
||||
if (marker == simh_marker::TAPE_MARK) { // we skip filemarks
|
||||
m_pos += 4;
|
||||
continue;
|
||||
}
|
||||
if (is_simh_marker_eod_forward(marker)) // success: we reached EOD
|
||||
return tape_status::OK;
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
m_pos += 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to skip all blocks
|
||||
default:
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos += read_len;
|
||||
}
|
||||
}
|
||||
// error: we reached physical EOM
|
||||
assert(m_pos == m_file_size);
|
||||
return tape_status::EOM;
|
||||
}
|
||||
|
||||
const std::pair<const tape_status, const u32> simh_tape_file::space_blocks(const u32 req_blocks_num)
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
assert(req_blocks_num > 0);
|
||||
u32 blocks_num = 0;
|
||||
while (m_pos < m_file_size) {
|
||||
if (m_pos + 4 > m_file_size) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(m_pos);
|
||||
if (marker == simh_marker::TAPE_MARK) { // error: we reached filemark
|
||||
m_pos += 4;
|
||||
return std::pair(is_ew() ? tape_status::FILEMARK_EW : tape_status::FILEMARK, blocks_num);
|
||||
}
|
||||
if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
|
||||
return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, blocks_num);
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
m_pos += 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos += read_len;
|
||||
blocks_num++;
|
||||
if (blocks_num == req_blocks_num) // success: we're done
|
||||
return std::pair(tape_status::OK, blocks_num);
|
||||
|
||||
break;
|
||||
default: // we try to skip other blocks
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos += read_len;
|
||||
}
|
||||
}
|
||||
// error: we reached physical EOM
|
||||
assert(m_pos == m_file_size);
|
||||
return std::pair(tape_status::EOM, blocks_num);
|
||||
}
|
||||
|
||||
const std::pair<const tape_status, const u32> simh_tape_file::space_filemarks(const u32 req_filemarks_num, const bool setmarks, const bool sequential)
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
assert(req_filemarks_num > 0);
|
||||
assert(!setmarks); // TODO: SIMH doesn't define setmarks
|
||||
assert(!sequential); // TODO: support spacing over sequential filemarks, once we have good way to test it
|
||||
u32 filemarks_num = 0;
|
||||
while (m_pos < m_file_size) {
|
||||
if (m_pos + 4 > m_file_size) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(m_pos);
|
||||
if (marker == simh_marker::TAPE_MARK) { // we count filemarks
|
||||
m_pos += 4;
|
||||
filemarks_num++;
|
||||
if (filemarks_num == req_filemarks_num) // success: we're done
|
||||
return std::pair(tape_status::OK, filemarks_num);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
|
||||
return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, filemarks_num);
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
m_pos += 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to skip all blocks
|
||||
default:
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos += read_len;
|
||||
}
|
||||
}
|
||||
// error: we reached physical EOM
|
||||
assert(m_pos == m_file_size);
|
||||
return std::pair(tape_status::EOM, filemarks_num);
|
||||
}
|
||||
|
||||
const std::pair<const tape_status, const u32> simh_tape_file::space_blocks_reverse(const u32 req_blocks_num)
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
assert(req_blocks_num > 0);
|
||||
u32 blocks_num = 0;
|
||||
while (m_pos > 0) {
|
||||
if (m_pos - 4 < 0) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(m_pos - 4);
|
||||
if (marker == simh_marker::TAPE_MARK) { // error: we reached filemark
|
||||
m_pos -= 4;
|
||||
return std::pair(is_ew() ? tape_status::FILEMARK_EW : tape_status::FILEMARK, blocks_num);
|
||||
}
|
||||
if (is_simh_marker_eod_reverse(marker)) // error: we reached EOD
|
||||
return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, blocks_num);
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
m_pos -= 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
|
||||
if (m_pos - read_len < 0) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos -= read_len;
|
||||
blocks_num++;
|
||||
if (blocks_num == req_blocks_num) // success: we're done
|
||||
return std::pair(tape_status::OK, blocks_num);
|
||||
|
||||
break;
|
||||
default: // we try to skip other blocks
|
||||
if (m_pos - read_len < 0) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos -= read_len;
|
||||
}
|
||||
}
|
||||
// error: we reached BOM
|
||||
assert(m_pos == 0);
|
||||
return std::pair(tape_status::BOM, blocks_num);
|
||||
}
|
||||
|
||||
const std::pair<const tape_status, const u32> simh_tape_file::space_filemarks_reverse(const u32 req_filemarks_num, const bool setmarks, const bool sequential)
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
assert(req_filemarks_num > 0);
|
||||
assert(!setmarks); // TODO: SIMH doesn't define setmarks
|
||||
assert(!sequential); // TODO: support spacing over sequential filemarks, once we have good way to test it
|
||||
u32 filemarks_num = 0;
|
||||
while (m_pos > 0) {
|
||||
if (m_pos - 4 < 0) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(m_pos - 4);
|
||||
if (marker == simh_marker::TAPE_MARK) { // we count filemarks
|
||||
m_pos -= 4;
|
||||
filemarks_num++;
|
||||
if (filemarks_num == req_filemarks_num) // success: we're done
|
||||
return std::pair(tape_status::OK, filemarks_num);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (is_simh_marker_eod_reverse(marker)) // error: we reached EOD
|
||||
return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, filemarks_num);
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
m_pos -= 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to skip all blocks
|
||||
default:
|
||||
if (m_pos - read_len < 0) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos -= read_len;
|
||||
}
|
||||
}
|
||||
// error: we reached BOM
|
||||
assert(m_pos == 0);
|
||||
return std::pair(tape_status::BOM, filemarks_num);
|
||||
}
|
||||
|
||||
const std::pair<const tape_status, const u32> simh_tape_file::read_block(u8 *buf, const u32 buf_size)
|
||||
{
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
while (m_pos < m_file_size) {
|
||||
if (m_pos + 4 > m_file_size) // error: truncated marker
|
||||
throw std::runtime_error("truncated marker");
|
||||
|
||||
const simh_marker marker = (const simh_marker)read_word(m_pos);
|
||||
if (marker == simh_marker::TAPE_MARK) { // error: we reached filemark
|
||||
m_pos += 4;
|
||||
return std::pair(is_ew() ? tape_status::FILEMARK_EW : tape_status::FILEMARK, 0);
|
||||
}
|
||||
if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
|
||||
return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, 0);
|
||||
|
||||
const simh_marker_class marker_class = get_simh_marker_class(marker);
|
||||
const u32 block_len = get_simh_marker_value(marker);
|
||||
const u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 read_len = 4 + block_len + pad_len + 4;
|
||||
switch (marker_class) {
|
||||
case simh_marker_class::PRIVATE_MARKER: // we skip other markers
|
||||
case simh_marker_class::RESERVED_MARKER:
|
||||
m_pos += 4;
|
||||
break;
|
||||
case simh_marker_class::GOOD_DATA_RECORD: // we try to read data blocks
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
if (block_len > buf_size) // error: block length too big
|
||||
throw std::runtime_error("block length too big");
|
||||
|
||||
// success: we read another block
|
||||
read_bytes(m_pos + 4, buf, block_len);
|
||||
m_pos += read_len;
|
||||
return std::pair(tape_status::OK, block_len);
|
||||
|
||||
default: // we try to skip other blocks
|
||||
if (m_pos + read_len > m_file_size) // error: truncated block
|
||||
throw std::runtime_error("truncated block");
|
||||
|
||||
m_pos += read_len;
|
||||
}
|
||||
}
|
||||
// error: we reached physical EOM
|
||||
assert(m_pos == m_file_size);
|
||||
return std::pair(tape_status::EOM, 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// destructive operations
|
||||
|
||||
void simh_tape_file::erase(const bool eom)
|
||||
{
|
||||
assert(!m_read_only);
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
const u32 write_len = m_file_size - m_pos; // we always erase entire remainder of tape
|
||||
write_byte_repeat(m_pos, 0xff, write_len); // we assume simh_marker::EOM == 0xffffffff
|
||||
m_pos += write_len;
|
||||
}
|
||||
|
||||
const tape_status simh_tape_file::write_block(const u8 *const buf, const u32 req_block_len)
|
||||
{
|
||||
assert(!m_read_only);
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
const u32 pad_len = req_block_len % 2; // pad odd-length blocks with 1 byte
|
||||
const u32 write_len = 4 + req_block_len + pad_len + 4;
|
||||
if (m_pos + write_len >= m_file_size) // error: we reached physical EOM
|
||||
return tape_status::EOM;
|
||||
|
||||
write_word(m_pos, req_block_len);
|
||||
write_bytes(m_pos + 4, buf, req_block_len);
|
||||
write_byte_repeat(m_pos + 4 + req_block_len, 0, pad_len);
|
||||
write_word(m_pos + 4 + req_block_len + pad_len, req_block_len);
|
||||
m_pos += write_len;
|
||||
return is_ew() ? tape_status::EW : tape_status::OK; // success: we wrote another block
|
||||
}
|
||||
|
||||
const tape_status simh_tape_file::write_filemarks(const u32 req_filemarks_num, const bool setmarks)
|
||||
{
|
||||
assert(!m_read_only);
|
||||
assert(m_pos <= m_file_size);
|
||||
assert(m_pos % 2 == 0);
|
||||
assert(!setmarks); // TODO: SIMH doesn't define setmarks
|
||||
const u32 write_len = req_filemarks_num * 4;
|
||||
if (m_pos + write_len >= m_file_size) // error: we reached physical EOM
|
||||
return tape_status::EOM;
|
||||
|
||||
for (u32 i = 0; i < write_len; i += 4)
|
||||
write_word(m_pos + i, (const u32)simh_marker::TAPE_MARK);
|
||||
m_pos += write_len;
|
||||
return is_ew() ? tape_status::EW : tape_status::OK; // success: we wrote all filemarks
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
65
src/lib/util/simh_tape_file.h
Normal file
65
src/lib/util/simh_tape_file.h
Normal file
@ -0,0 +1,65 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
#ifndef MAME_LIB_UTIL_SIMH_TAPE_FILE_H
|
||||
#define MAME_LIB_UTIL_SIMH_TAPE_FILE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tape_file_interface.h"
|
||||
|
||||
#include "utilfwd.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class simh_tape_file : public tape_file_interface
|
||||
{
|
||||
public:
|
||||
// construction and destruction
|
||||
simh_tape_file(util::random_read_write &file, const u64 file_size, const bool read_only, const bool create = false);
|
||||
virtual ~simh_tape_file();
|
||||
|
||||
// position-preserving operations
|
||||
virtual const bool is_read_only() const override { return m_read_only; }
|
||||
virtual const bool is_ew() const override { return m_pos + 32768 >= m_file_size; } // 32KB from EOM; TODO: ANSI says EW should be 10ft from EOM regardless of density
|
||||
virtual const u8 get_density_code() const override { return 0; } // TODO: SIMH doesn't define density
|
||||
virtual const std::pair<const tape_status, const u32> read_position() const override;
|
||||
|
||||
// non-destructive operations
|
||||
virtual void rewind(const bool eom) override;
|
||||
virtual const tape_status locate_block(const u32 req_block_addr) override;
|
||||
virtual const tape_status space_eod() override;
|
||||
virtual const std::pair<const tape_status, const u32> space_blocks(const u32 req_blocks_num) override;
|
||||
virtual const std::pair<const tape_status, const u32> space_blocks_reverse(const u32 req_blocks_num) override;
|
||||
virtual const std::pair<const tape_status, const u32> space_filemarks(const u32 req_marks_num, const bool setmarks = false, const bool sequential = false) override;
|
||||
virtual const std::pair<const tape_status, const u32> space_filemarks_reverse(const u32 req_marks_num, const bool setmarks = false, const bool sequential = false) override;
|
||||
virtual const std::pair<const tape_status, const u32> read_block(u8 *const buf, const u32 buf_size) override;
|
||||
|
||||
// destructive operations
|
||||
virtual void erase(const bool eom) override;
|
||||
virtual const tape_status write_block(const u8 *const buf, const u32 len) override;
|
||||
virtual const tape_status write_filemarks(const u32 req_marks_num, const bool setmarks = false) override;
|
||||
|
||||
protected:
|
||||
// internal operations
|
||||
void raw_seek(const u64 pos) const;
|
||||
void raw_read(u8 *const buf, const u32 len) const;
|
||||
void raw_write(const u8 *const buf, const u32 len) const;
|
||||
void read_bytes(const u64 pos, u8 *const buf, const u32 len) const;
|
||||
const u32 read_word(const u64 pos) const;
|
||||
void write_bytes(const u64 pos, const u8 *const buf, const u32 len) const;
|
||||
void write_byte_repeat(const u64 pos, const u8 data, const u32 len) const;
|
||||
void write_word(const u64 pos, const u32 data) const;
|
||||
|
||||
// state
|
||||
util::random_read_write &m_file; // tape image file
|
||||
u64 m_file_size; // size of tape image file
|
||||
bool m_read_only; // should we disallow destructive operations on tape image
|
||||
u64 m_pos; // tape position
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // MAME_LIB_UTIL_SIMH_TAPE_FILE_H
|
58
src/lib/util/tape_file_interface.h
Normal file
58
src/lib/util/tape_file_interface.h
Normal file
@ -0,0 +1,58 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mietek Bak
|
||||
|
||||
#ifndef MAME_LIB_UTIL_TAPE_FILE_INTERFACE_H
|
||||
#define MAME_LIB_UTIL_TAPE_FILE_INTERFACE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum class tape_status : u8 {
|
||||
OK, // oll korrekt
|
||||
BOM, // beginning of medium
|
||||
EW, // early warning
|
||||
FILEMARK, // filemark
|
||||
FILEMARK_EW, // filemark and early warning
|
||||
SETMARK, // setmark
|
||||
SETMARK_EW, // setmark and early warning
|
||||
EOD, // end of data
|
||||
EOD_EW, // end of data and early warning
|
||||
UNKNOWN, // unknown
|
||||
UNKNOWN_EW, // unknown and early warning
|
||||
EOM // end of medium
|
||||
};
|
||||
|
||||
class tape_file_interface
|
||||
{
|
||||
public:
|
||||
// construction
|
||||
virtual ~tape_file_interface() {}
|
||||
|
||||
// position-preserving operations
|
||||
virtual const bool is_read_only() const = 0;
|
||||
virtual const bool is_ew() const = 0;
|
||||
virtual const u8 get_density_code() const = 0;
|
||||
virtual const std::pair<const tape_status, const u32> read_position() const = 0;
|
||||
|
||||
// non-destructive operations
|
||||
virtual void rewind(const bool eom) = 0;
|
||||
virtual const tape_status locate_block(const u32 req_block_addr) = 0;
|
||||
virtual const tape_status space_eod() = 0;
|
||||
virtual const std::pair<const tape_status, const u32> space_blocks(const u32 req_blocks_num) = 0;
|
||||
virtual const std::pair<const tape_status, const u32> space_filemarks(const u32 req_marks_num, const bool setmarks = false, const bool sequential = false) = 0;
|
||||
virtual const std::pair<const tape_status, const u32> space_blocks_reverse(const u32 req_blocks_num) = 0;
|
||||
virtual const std::pair<const tape_status, const u32> space_filemarks_reverse(const u32 req_marks_num, const bool setmarks = false, const bool sequential = false) = 0;
|
||||
virtual const std::pair<const tape_status, const u32> read_block(u8 *const buf, const u32 buf_size) = 0;
|
||||
|
||||
// destructive operations
|
||||
virtual void erase(const bool eom) = 0;
|
||||
virtual const tape_status write_block(const u8 *const buf, const u32 len) = 0;
|
||||
virtual const tape_status write_filemarks(const u32 req_marks_num, const bool setmarks = false) = 0;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // MAME_LIB_UTIL_TAPE_FILE_INTERFACE_H
|
Loading…
Reference in New Issue
Block a user