summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author fulivi <fulivi@users.noreply.github.com>2019-01-12 18:57:39 +0100
committer fulivi <fulivi@users.noreply.github.com>2019-01-12 18:57:39 +0100
commitc6dff30d7a815d318029a30baf0f85a538dc883e (patch)
treedb6b8093ca8d0c77efbffc177ff6f7a4db8b471e
parent933ab296274761c8ba422fc23952aad7b7f2e0dd (diff)
hp9825: added HLE of HP9885 floppy drive. Extended HPI format to
handle single-sided disks.
-rw-r--r--scripts/src/bus.lua2
-rw-r--r--src/devices/bus/hp9845_io/98032.cpp2
-rw-r--r--src/devices/bus/hp9845_io/hp9885.cpp974
-rw-r--r--src/devices/bus/hp9845_io/hp9885.h136
-rw-r--r--src/lib/formats/hpi_dsk.cpp94
-rw-r--r--src/lib/formats/hpi_dsk.h3
-rw-r--r--src/mame/drivers/hp9825.cpp14
7 files changed, 1203 insertions, 22 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index 9b1d4afafea..f4645310c55 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -3511,6 +3511,8 @@ if (BUSES["HP9845_IO"]~=null) then
MAME_DIR .. "src/devices/bus/hp9845_io/98034.h",
MAME_DIR .. "src/devices/bus/hp9845_io/98035.cpp",
MAME_DIR .. "src/devices/bus/hp9845_io/98035.h",
+ MAME_DIR .. "src/devices/bus/hp9845_io/hp9885.cpp",
+ MAME_DIR .. "src/devices/bus/hp9845_io/hp9885.h",
}
end
diff --git a/src/devices/bus/hp9845_io/98032.cpp b/src/devices/bus/hp9845_io/98032.cpp
index c4838331b87..0209ef7a3d4 100644
--- a/src/devices/bus/hp9845_io/98032.cpp
+++ b/src/devices/bus/hp9845_io/98032.cpp
@@ -13,6 +13,7 @@
#include "emu.h"
#include "98032.h"
+#include "hp9885.h"
// Debugging
#define VERBOSE 0
@@ -353,6 +354,7 @@ hp98032_gpio_slot_device::hp98032_gpio_slot_device(const machine_config &mconfig
{
option_reset();
option_add("loopback" , HP98032_GPIO_LOOPBACK);
+ option_add("hp9885" , HP9885);
set_default_option(nullptr);
set_fixed(false);
}
diff --git a/src/devices/bus/hp9845_io/hp9885.cpp b/src/devices/bus/hp9845_io/hp9885.cpp
new file mode 100644
index 00000000000..e4a2fc9f33c
--- /dev/null
+++ b/src/devices/bus/hp9845_io/hp9885.cpp
@@ -0,0 +1,974 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ hp9885.cpp
+
+ HP9885M 8" floppy drive
+ =======================
+
+ This driver implements HLE of HP9885 floppy drive.
+ The HP9885M is a single-disk 8" floppy drive. It connects to
+ main system through a HP98032 GPIO module. The controller in a
+ HP9885M can drive 3 more external HP9885S drives. The "M" or "S"
+ in the name stand for master and slave, respectively.
+ The enclosure of HP9885M contains the floppy drive, the controller
+ electronics and the power supply whereas the HP9885S only has the
+ drive and the power supply. A master unit interfaces to slave units
+ through a standard daisy-chained Shugart bus.
+ The controller is based on a HP Nanoprocessor with a 2 kB FW ROM.
+ Unfortunately no dumps are available of the ROM, AFAIK, so the HLE
+ is needed.
+ The HP9885 supports a single disk format having these characteristics:
+ - Single side
+ - HP MMFM modulation
+ - 30 256-byte sectors per track
+ - 77 tracks (only 67 are actually used)
+ - 360 RPM
+ - A total capacity of 514560 bytes per disk.
+
+ Summary of the command words I identified
+ =========================================
+
+ *READ*
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----+-----+-----------------------------------+
+ | 0 X|Unit#| Sector count |
+ +-----+-----+-----------------------------------+
+
+ Bit 14 selects tighter margin for data reading (not emulated here)
+
+ *WRITE*
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----+-----+-----------------------------------+
+ | 1 0|Unit#| Sector count |
+ +-----+-----+-----------------------------------+
+
+ *SEEK*
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----+-----+--------------------+--------------+
+ | 1 1|Unit#| Track # [0..76] |Sector#[0..29]|
+ +-----+-----+--------------------+--------------+
+
+ *FORMAT TRACK* (not implemented yet)
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----+-----+--------------------+--------------+
+ | 1 1|Unit#| Track # [0..76] | 0x1e |
+ +-----+-----+--------------------+--------------+
+
+ *STEP IN*
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----+-----+--------------------+--------------+
+ | 1 1|Unit#| 0x7c | 0x1f |
+ +-----+-----+--------------------+--------------+
+
+ *ERASE TRACK* (not implemented yet)
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----+-----+--------------------+--------------+
+ | 1 1|Unit#| 0x7d | 0x1f |
+ +-----+-----+--------------------+--------------+
+
+ *READ STATUS*
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----+-----+--------------------+--------------+
+ | 1 1|Unit#| 0x7f | 0x1f |
+ +-----+-----+--------------------+--------------+
+
+ This is the structure of the status word:
+
+ +-----------------------------------------------+
+ |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|
+ +-----------------------+--+--+--+--+--+--+-----+
+ | Error code (0=OK) |--|XC|SC|NR|WP|DC|Unit#|
+ +-----------------------+--+--+--+--+--+--+-----+
+
+ DC: Disk changed
+ WP: Write protection
+ NR: Not ready
+ SC: Seek completed
+ XC: Transfer completed
+
+ Usage on HP9825
+ ===============
+
+ -slot0 98032_gpio -slot0:98032_gpio:gpio hp9885 -flop <floppy image>
+
+ Usage on HP9845
+ ===============
+
+ -slot0 98032_gpio -slot0:98032_gpio:gpio hp9885 -rom1 massd -flop <floppy image>
+
+ Note on floppy images
+ =====================
+
+ Images can be in two formats: MFI & HPI.
+ A pre-formatted image must be used as formatting is not supported.
+ It's not possible to format images for two reasons: the format
+ command is not implemented yet and no dump of the disk system tape
+ cartridge (HP part no. 09885-90035) is available.
+ The latter is needed during format operation to create the so-called
+ bootstraps on disk, which in turn are needed by 9825 systems.
+ A pre-formatted image (9825_empty.hpi) is available here:
+ http://www.hpmuseum.net/software/9825_discs.zip
+
+ TODO
+ ====
+
+ + Implement missing commands
+ + PRESET
+
+ Acknowledgments
+ ===============
+
+ Thanks to Dyke Shaffer for publishing a lot of HP internal docs
+ and source files regarding the HP9885.
+
+ Fun fact: data I/O between disk and 98032 module is not buffered
+ as there is no RAM in the controller. DMA must be used to keep
+ data flow at disk speed and avoid underruns. The nominal disk
+ data rate is one word every 32 µs.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "hp9885.h"
+#include "formats/hpi_dsk.h"
+
+// Debugging
+#include "logmacro.h"
+#define LOG_TIMER_MASK (LOG_GENERAL << 1)
+#define LOG_TIMER(...) LOGMASKED(LOG_TIMER_MASK, __VA_ARGS__)
+#define LOG_HS_MASK (LOG_TIMER_MASK << 1)
+#define LOG_HS(...) LOGMASKED(LOG_HS_MASK, __VA_ARGS__)
+#define LOG_HEAD_MASK (LOG_HS_MASK << 1)
+#define LOG_HEAD(...) LOGMASKED(LOG_HEAD_MASK, __VA_ARGS__)
+#define LOG_DISK_MASK (LOG_HEAD_MASK << 1)
+#define LOG_DISK(...) LOGMASKED(LOG_DISK_MASK, __VA_ARGS__)
+
+#undef VERBOSE
+//#define VERBOSE (LOG_GENERAL | LOG_HS_MASK | LOG_HEAD_MASK)
+#define VERBOSE 0
+
+// Bit manipulation
+namespace {
+ template<typename T> constexpr T BIT_MASK(unsigned n)
+ {
+ return (T)1U << n;
+ }
+
+ template<typename T> void BIT_CLR(T& w , unsigned n)
+ {
+ w &= ~BIT_MASK<T>(n);
+ }
+
+ template<typename T> void BIT_SET(T& w , unsigned n)
+ {
+ w |= BIT_MASK<T>(n);
+ }
+}
+
+// device type definition
+DEFINE_DEVICE_TYPE(HP9885, hp9885_device, "hp9885" , "HP9885 floppy drive")
+
+// Timers
+enum {
+ FSM_TMR_ID,
+ HEAD_TMR_ID,
+ BIT_BYTE_TMR_ID
+};
+
+// Constants
+constexpr unsigned MAX_TRACK = 76; // Maximum valid track
+constexpr unsigned MAX_SECTOR = 29; // Maximum valid sector
+constexpr unsigned UNKNOWN_TRACK= 0xff; // Current track unknown
+constexpr unsigned STEP_MS = 8; // Step time (ms)
+constexpr unsigned SETTLING_MS = 8; // Settling time (ms)
+constexpr unsigned HEAD_TO_MS = 415; // Head unload timeout (ms)
+constexpr unsigned HD_SETTLE_MS = 50; // Head settling time (ms)
+constexpr uint16_t PASSWORD = 0xae87; // "Password" to enable commands
+constexpr unsigned HALF_CELL_US = 1; // Half bit cell duration (µs)
+constexpr unsigned STATUS_DELAY_US = 100; // Status delay (µs)
+constexpr unsigned MISSED_ID_REVS = 2; // Disk rotations to stop ID search
+
+// Bits in status word
+constexpr unsigned STS_DISK_CHANGED = 2; // Disk changed
+constexpr unsigned STS_WRITE_PROTECT= 3; // Write protection
+constexpr unsigned STS_NOT_RDY = 4; // Drive not ready
+constexpr unsigned STS_SEEK_COMPLETE= 5; // Seek completed
+constexpr unsigned STS_XFER_COMPLETE= 6; // Data transfer completed
+
+// Error codes
+enum : unsigned {
+ ERR_NONE = 0,
+ ERR_NOT_POWERED = 1,
+ ERR_DOOR_OPEN = 2,
+ ERR_NO_DISK = 3,
+ ERR_WR_DISABLED = 4,
+ ERR_ID_ERROR = 5,
+ ERR_TRACK_ERROR = 6,
+ ERR_CRC_ERROR = 7,
+ ERR_HW_FAILURE = 8
+};
+
+hp9885_device::hp9885_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : hp98032_gpio_card_device(mconfig , HP9885 , tag , owner , clock)
+ , m_drive_connector{*this , "floppy"}
+{
+}
+
+hp9885_device::~hp9885_device()
+{
+}
+
+uint16_t hp9885_device::get_jumpers() const
+{
+ return hp98032_gpio_slot_device::JUMPER_2 |
+ hp98032_gpio_slot_device::JUMPER_7 |
+ hp98032_gpio_slot_device::JUMPER_8 |
+ hp98032_gpio_slot_device::JUMPER_B |
+ hp98032_gpio_slot_device::JUMPER_E |
+ hp98032_gpio_slot_device::JUMPER_F;
+}
+
+uint16_t hp9885_device::input_r() const
+{
+ uint16_t tmp = m_input;
+ if (m_obf) {
+ tmp |= m_output;
+ LOG_HS("DATA OUT %04x\n" , tmp);
+ }
+ return tmp;
+}
+
+uint8_t hp9885_device::ext_status_r() const
+{
+ return 0;
+}
+
+void hp9885_device::output_w(uint16_t data)
+{
+ m_input = data;
+}
+
+void hp9885_device::ext_control_w(uint8_t data)
+{
+ LOG_HS("EXT CTRL %u\n" , data);
+ if (BIT(data , 0) &&
+ !BIT(m_status , STS_XFER_COMPLETE) &&
+ (m_op == OP_READ || m_op == OP_WRITE)) {
+ // CTL0 terminates current data transfer
+ LOG("xfer terminated\n");
+ BIT_SET(m_status , STS_XFER_COMPLETE);
+ // Prepare to output status
+ set_output();
+ }
+}
+
+WRITE_LINE_MEMBER(hp9885_device::pctl_w)
+{
+ m_pctl = state;
+ if (m_pctl) {
+ if (!m_outputting) {
+ set_ibf(true);
+ }
+ } else {
+ LOG_HS("DATA IN %04x\n" , m_input);
+ new_word();
+ }
+ update_busy();
+}
+
+WRITE_LINE_MEMBER(hp9885_device::io_w)
+{
+ LOG_HS("I/O = %d\n" , state);
+}
+
+WRITE_LINE_MEMBER(hp9885_device::preset_w)
+{
+ LOG("PRESET = %d\n" , state);
+}
+
+static const floppy_format_type hp9885_floppy_formats[] = {
+ FLOPPY_MFI_FORMAT,
+ FLOPPY_HPI_FORMAT,
+ nullptr
+};
+
+void hp9885_device::device_add_mconfig(machine_config &config)
+{
+ FLOPPY_CONNECTOR(config , "floppy" , "8ssdd" , FLOPPY_8_SSDD , true , hp9885_floppy_formats).set_fixed(true);
+}
+
+void hp9885_device::device_start()
+{
+ save_item(NAME(m_input));
+ save_item(NAME(m_output));
+ save_item(NAME(m_status));
+ save_item(NAME(m_fsm_state));
+ save_item(NAME(m_head_state));
+ save_item(NAME(m_op));
+ save_item(NAME(m_pctl));
+ save_item(NAME(m_ibf));
+ save_item(NAME(m_obf));
+ save_item(NAME(m_outputting));
+ save_item(NAME(m_had_transition));
+ save_item(NAME(m_dskchg));
+ save_item(NAME(m_track));
+ save_item(NAME(m_seek_track));
+ save_item(NAME(m_seek_sector));
+ save_item(NAME(m_sector_cnt));
+ save_item(NAME(m_word_cnt));
+ save_item(NAME(m_rev_cnt));
+ save_item(NAME(m_am_detector));
+ save_item(NAME(m_crc));
+
+ m_fsm_timer = timer_alloc(FSM_TMR_ID);
+ m_head_timer = timer_alloc(HEAD_TMR_ID);
+ m_bit_byte_timer = timer_alloc(BIT_BYTE_TMR_ID);
+
+ m_drive = m_drive_connector->get_device();
+
+ m_drive->setup_ready_cb(floppy_image_device::ready_cb(&hp9885_device::floppy_ready_cb , this));
+ m_drive->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp9885_device::floppy_index_cb , this));
+}
+
+void hp9885_device::device_reset()
+{
+ eir_w(0);
+ psts_w(1);
+ m_dskchg = true;
+ m_obf = false;
+ recalibrate();
+ m_seek_track = 0;
+ m_seek_sector = 0;
+ m_fsm_state = FSM_RECALIBRATING;
+ set_state(FSM_IDLE);
+}
+
+void hp9885_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ LOG_TIMER("Tmr %.06f ID %d FSM %d HD %d\n" , machine().time().as_double() , id , m_fsm_state , m_head_state);
+
+ switch (id) {
+ case FSM_TMR_ID:
+ do_FSM();
+ break;
+
+ case HEAD_TMR_ID:
+ if (m_head_state == HEAD_SETTLING) {
+ LOG_HEAD("%.06f Head loaded\n" , machine().time().as_double());
+ m_head_state = HEAD_LOADED;
+ // Trigger actions to be done on head loading
+ do_FSM();
+ m_head_timer->adjust(attotime::from_msec(HEAD_TO_MS - HD_SETTLE_MS));
+ } else {
+ LOG_HEAD("%.06f Head unloaded\n" , machine().time().as_double());
+ m_head_state = HEAD_UNLOADED;
+ }
+ break;
+
+ case BIT_BYTE_TMR_ID:
+ {
+ switch (m_fsm_state) {
+ case FSM_WAIT_ID_AM:
+ case FSM_WAIT_DATA_AM:
+ {
+ attotime edge;
+ attotime tm;
+ edge = m_drive->get_next_transition(m_pll.ctime);
+ bool half_bit = m_pll.feed_read_data(tm , edge , attotime::never);
+ m_am_detector <<= 1;
+ m_am_detector |= half_bit;
+ if (m_am_detector == 0x55552a54) {
+ // ID AM
+ // CDCDCDCDCDCDCDCD
+ // 0 0 0 0 1 1 1 0
+ // 0 1 1 1 0 0 0 0
+ LOG_DISK("Got ID AM\n");
+ preset_crc();
+ m_word_cnt = 2;
+ set_state(FSM_RD_ID);
+ } else if (m_am_detector == 0x55552a44) {
+ // DATA AM
+ // CDCDCDCDCDCDCDCD
+ // 0 0 0 0 1 0 1 0
+ // 0 1 1 1 0 0 0 0
+ LOG_DISK("Got Data AM\n");
+ if (m_fsm_state == FSM_WAIT_DATA_AM) {
+ m_rev_cnt = 0;
+ if (BIT(m_status , STS_XFER_COMPLETE)) {
+ output_status();
+ return;
+ } else {
+ preset_crc();
+ m_word_cnt = 129;
+ if (m_op == OP_READ) {
+ set_state(FSM_RD_DATA);
+ } else {
+ set_state(FSM_WR_DATA);
+ m_pll.start_writing(m_pll.ctime);
+ m_had_transition = false;
+ wr_word(m_input);
+ set_ibf(false);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FSM_RD_ID:
+ {
+ // This is needed when state is switched to one of the AM waiting states
+ m_am_detector = 0;
+ auto word = rd_word();
+ m_word_cnt--;
+ LOG_DISK("W %04x C %u\n" , word , m_word_cnt);
+ if (m_word_cnt && word != ((m_seek_sector << 8) | m_track)) {
+ set_state(FSM_WAIT_ID_AM);
+ } else if (m_word_cnt == 0) {
+ if (m_crc) {
+ LOG_DISK("Wrong CRC in ID\n");
+ set_state(FSM_WAIT_ID_AM);
+ } else {
+ LOG_DISK("Sector found\n");
+ set_state(FSM_WAIT_DATA_AM);
+ }
+ }
+ }
+ break;
+
+ case FSM_RD_DATA:
+ {
+ auto word = rd_word();
+ m_word_cnt--;
+ LOG_DISK("W %04x C %u\n" , word , m_word_cnt);
+ if (m_word_cnt >= 1) {
+ if (!BIT(m_status , STS_XFER_COMPLETE)) {
+ m_output = word;
+ m_obf = true;
+ update_busy();
+ }
+ } else if (m_word_cnt == 0) {
+ if (m_crc) {
+ LOG_DISK("Wrong CRC in data\n");
+ }
+ // Move to next sector
+ adv_sector();
+ if (BIT(m_status , STS_XFER_COMPLETE) || m_sector_cnt == 0) {
+ BIT_SET(m_status , STS_XFER_COMPLETE);
+ output_status();
+ } else {
+ set_state(FSM_POSITIONING);
+ do_FSM();
+ }
+ return;
+ }
+ }
+ break;
+
+ case FSM_WR_DATA:
+ {
+ m_word_cnt--;
+ if (m_word_cnt > 1) {
+ if (BIT(m_status , STS_XFER_COMPLETE)) {
+ wr_word(0);
+ } else {
+ wr_word(m_input);
+ if (m_word_cnt > 2) {
+ set_ibf(false);
+ }
+ }
+ } else if (m_word_cnt == 1) {
+ wr_word(m_crc);
+ } else {
+ m_pll.stop_writing(m_drive , m_pll.ctime);
+ // Move to next sector
+ adv_sector();
+ if (BIT(m_status , STS_XFER_COMPLETE) || m_sector_cnt == 0) {
+ BIT_SET(m_status , STS_XFER_COMPLETE);
+ output_status();
+ } else {
+ set_ibf(false);
+ set_state(FSM_POSITIONING);
+ do_FSM();
+ }
+ return;
+ }
+ }
+ break;
+
+ default:
+ LOG("Invalid FSM state %d\n" , m_fsm_state);
+ set_state(FSM_IDLE);
+ return;
+ }
+ timer.adjust(m_pll.ctime - machine().time());
+ }
+ break;
+ }
+}
+
+void hp9885_device::floppy_ready_cb(floppy_image_device *floppy , int state)
+{
+ LOG("ready %d\n" , state);
+ if (state) {
+ // drive not ready
+ m_dskchg = true;
+ }
+}
+
+void hp9885_device::floppy_index_cb(floppy_image_device *floppy , int state)
+{
+ if (state && m_rev_cnt && --m_rev_cnt == 0) {
+ // Sector not found
+ LOG("Sector not found\n");
+ stop_rdwr();
+ set_error(ERR_ID_ERROR);
+ output_status(true);
+ }
+}
+
+void hp9885_device::set_state(int new_state)
+{
+ if (m_fsm_state != new_state) {
+ LOG("%.06f FSM %d->%d\n" , machine().time().as_double() , m_fsm_state , new_state);
+ m_fsm_state = new_state;
+ if (m_fsm_state == FSM_IDLE) {
+ m_op = OP_NONE;
+ m_outputting = false;
+ set_ibf(false);
+ m_fsm_timer->reset();
+ stop_rdwr();
+ m_rev_cnt = 0;
+ }
+ }
+}
+
+void hp9885_device::init_status(unsigned unit_no)
+{
+ m_status = unit_no & 3;
+ if (unit_no == 0) {
+ if (m_drive->ready_r()) {
+ BIT_SET(m_status , STS_NOT_RDY);
+ }
+ if (m_dskchg) {
+ BIT_SET(m_status , STS_DISK_CHANGED);
+ }
+ if (m_drive->wpt_r()) {
+ BIT_SET(m_status , STS_WRITE_PROTECT);
+ }
+ } else {
+ // Units 1,2,3 are not present
+ BIT_SET(m_status , STS_NOT_RDY);
+ }
+}
+
+void hp9885_device::encode_error(bool writing)
+{
+ if (m_status & 3) {
+ set_error(ERR_NOT_POWERED);
+ } else if (writing && BIT(m_status , STS_WRITE_PROTECT)) {
+ set_error(ERR_WR_DISABLED);
+ } else if (BIT(m_status , STS_NOT_RDY)) {
+ set_error(ERR_NO_DISK);
+ }
+}
+
+void hp9885_device::set_error(unsigned error_code)
+{
+ m_status = (m_status & 0xff) | (error_code << 8);
+ if (error_code != ERR_NONE) {
+ LOG_HS("EIR 1\n");
+ eir_w(1);
+ psts_w(0);
+ } else {
+ LOG_HS("EIR 0\n");
+ eir_w(0);
+ psts_w(1);
+ }
+}
+
+void hp9885_device::new_word()
+{
+ unsigned unit_no = (m_input >> 12) & 3;
+
+ switch (m_fsm_state) {
+ case FSM_IDLE:
+ if (m_input == PASSWORD) {
+ LOG("Got PW\n");
+ set_state(FSM_GOT_PW);
+ } else {
+ LOG("Wrong sequence\n");
+ // TODO:
+ // It probably does nothing IRL
+ }
+ set_ibf(false);
+ break;
+
+ case FSM_GOT_PW:
+ // Decode new command
+ switch (m_input & 0xc000) {
+ case 0x0000:
+ case 0x4000:
+ // Read
+ init_status(unit_no);
+ if (!BIT(m_status , STS_NOT_RDY)) {
+ m_sector_cnt = m_input & 0x0fff;
+ LOG("Read %u sectors @%u:%u\n" , m_sector_cnt , m_seek_track , m_seek_sector);
+ m_op = OP_READ;
+ set_state(FSM_POSITIONING);
+ if (load_head()) {
+ m_fsm_timer->adjust(attotime::zero);
+ }
+ set_output();
+ } else {
+ encode_error(false);
+ output_status();
+ }
+ break;
+
+ case 0x8000:
+ // Write
+ init_status(unit_no);
+ if (!BIT(m_status , STS_NOT_RDY) && !BIT(m_status , STS_WRITE_PROTECT)) {
+ m_sector_cnt = m_input & 0x0fff;
+ LOG("Write %u sectors @%u:%u\n" , m_sector_cnt , m_seek_track , m_seek_sector);
+ m_op = OP_WRITE;
+ set_state(FSM_POSITIONING);
+ if (load_head()) {
+ m_fsm_timer->adjust(attotime::zero);
+ }
+ set_ibf(false);
+ } else {
+ encode_error(true);
+ output_status(true);
+ }
+ break;
+
+ case 0xc000:
+ {
+ // Seek & other commands
+ uint8_t track_no = (m_input >> 5) & 0x7f;
+ uint8_t sect_no = m_input & 0x1f;
+ if (sect_no == 0x1e) {
+ // Format
+ LOG("Format\n");
+ // TODO:
+ } else if (sect_no == 0x1f) {
+ switch (track_no) {
+ case 0x7c:
+ // Step in
+ init_status(unit_no);
+ if (!BIT(m_status , STS_NOT_RDY)) {
+ LOG("Step in\n");
+ m_seek_track = m_track + 1;
+ m_op = OP_STEP_IN;
+ set_state(FSM_SEEKING);
+ if (load_head()) {
+ m_fsm_timer->adjust(attotime::zero);
+ }
+ } else {
+ encode_error(false);
+ output_status();
+ }
+ break;
+
+ case 0x7d:
+ // Write all track
+ LOG("Write all track\n");
+ // TODO:
+ break;
+
+ case 0x7f:
+ // Read status
+ init_status(unit_no);
+ if (!BIT(m_status , STS_NOT_RDY) && BIT(m_status , STS_DISK_CHANGED)) {
+ LOG("Get status DSKCHG\n");
+ m_op = OP_GET_STATUS;
+ set_state(FSM_POSITIONING);
+ if (load_head()) {
+ m_fsm_timer->adjust(attotime::zero);
+ }
+ } else {
+ LOG("Get status !DSKCHG\n");
+ encode_error(false);
+ output_status();
+ }
+ break;
+
+ default:
+ LOG("Unknown command %02x\n" , track_no);
+ }
+ } else {
+ // Plain seek
+ LOG("Seek to %u:%u\n" , track_no , sect_no);
+ m_seek_track = track_no;
+ m_seek_sector = sect_no;
+ set_state(FSM_IDLE);
+ }
+ }
+ break;
+ }
+ break;
+
+ case FSM_RD_STATUS1:
+ set_error(ERR_NONE);
+ set_state(FSM_RD_STATUS2);
+ m_outputting = false;
+ m_obf = false;
+ break;
+
+ case FSM_RD_STATUS2:
+ set_state(FSM_IDLE);
+ break;
+
+ case FSM_RD_DATA:
+ m_obf = false;
+ break;
+
+ default:
+ if (m_op != OP_WRITE) {
+ LOG("Got data in state %d!\n" , m_fsm_state);
+ }
+ }
+}
+
+void hp9885_device::do_FSM()
+{
+ switch (m_fsm_state) {
+ case FSM_RECALIBRATING:
+ // Keep head loaded
+ load_head();
+ if (m_drive->trk00_r()) {
+ one_step(true);
+ } else {
+ set_state(FSM_POSITIONING);
+ m_fsm_timer->adjust(attotime::from_msec(SETTLING_MS));
+ m_track = 0;
+ }
+ break;
+
+ case FSM_SETTLING:
+ if (m_op == OP_READ || m_op == OP_WRITE) {
+ // Set seek complete
+ BIT_SET(m_status , STS_SEEK_COMPLETE);
+ if (m_sector_cnt--) {
+ m_rev_cnt = MISSED_ID_REVS;
+ set_state(FSM_WAIT_ID_AM);
+ start_rd();
+ } else {
+ output_status();
+ }
+ } else if (m_op == OP_STEP_IN) {
+ // Step IN
+ // Set seek complete
+ BIT_SET(m_status , STS_SEEK_COMPLETE);
+ output_status();
+ } else {
+ // Get status
+ output_status();
+ }
+ break;
+
+ case FSM_POSITIONING:
+ case FSM_SEEKING:
+ // Keep head loaded
+ load_head();
+ // Need recalibration?
+ if (m_track == UNKNOWN_TRACK) {
+ set_state(FSM_RECALIBRATING);
+ m_fsm_timer->adjust(attotime::zero);
+ } else if (m_seek_track != m_track) {
+ set_state(FSM_SEEKING);
+ one_step(m_seek_track < m_track);
+ } else {
+ if (m_fsm_state == FSM_SEEKING) {
+ m_fsm_timer->adjust(attotime::from_msec(SETTLING_MS));
+ } else {
+ m_fsm_timer->adjust(attotime::zero);
+ }
+ set_state(FSM_SETTLING);
+ }
+ break;
+
+ case FSM_STATUS_DELAY:
+ output_status();
+ break;
+
+ default:
+ LOG("Invalid state=%d\n" , m_fsm_state);
+ set_state(FSM_IDLE);
+ }
+ update_busy();
+}
+
+bool hp9885_device::load_head()
+{
+ m_dskchg = false;
+
+ switch (m_head_state) {
+ case HEAD_UNLOADED:
+ case HEAD_SETTLING:
+ LOG_HEAD("%.06f Loading head..\n" , machine().time().as_double());
+ m_head_state = HEAD_SETTLING;
+ m_head_timer->adjust(attotime::from_msec(HD_SETTLE_MS));
+ return false;
+
+ case HEAD_LOADED:
+ LOG_HEAD("%.06f Keep head loaded\n" , machine().time().as_double());
+ m_head_timer->adjust(attotime::from_msec(HEAD_TO_MS));
+ return true;
+
+ default:
+ LOG("Invalid head state %d\n" , m_head_state);
+ m_head_state = HEAD_UNLOADED;
+ return false;
+ }
+}
+
+void hp9885_device::recalibrate()
+{
+ m_track = UNKNOWN_TRACK;
+}
+
+void hp9885_device::one_step(bool outward)
+{
+ if (outward) {
+ if (m_track > 0) {
+ m_track--;
+ }
+ } else {
+ if (m_track < MAX_TRACK) {
+ m_track++;
+ }
+ }
+ LOG_HEAD("%.06f Step to trk %u\n" , machine().time().as_double() , m_track);
+ m_drive->dir_w(outward);
+ m_drive->stp_w(0);
+ m_drive->stp_w(1);
+ m_fsm_timer->adjust(attotime::from_msec(STEP_MS));
+}
+
+void hp9885_device::adv_sector()
+{
+ if (++m_seek_sector > MAX_SECTOR) {
+ m_seek_sector = 0;
+ if (m_seek_track < MAX_TRACK) {
+ m_seek_track++;
+ }
+ }
+}
+
+void hp9885_device::start_rd()
+{
+ m_pll.set_clock(attotime::from_usec(HALF_CELL_US));
+ m_pll.read_reset(machine().time());
+ m_bit_byte_timer->adjust(attotime::zero);
+ m_am_detector = 0;
+}
+
+void hp9885_device::stop_rdwr()
+{
+ m_bit_byte_timer->reset();
+}
+
+uint16_t hp9885_device::rd_word()
+{
+ uint16_t word = 0;
+ for (unsigned i = 0; i < 16; ++i) {
+ attotime edge;
+ attotime tm;
+ edge = m_drive->get_next_transition(m_pll.ctime);
+ // Read & discard clock bit
+ m_pll.feed_read_data(tm , edge , attotime::never);
+ edge = m_drive->get_next_transition(m_pll.ctime);
+ bool data_bit = m_pll.feed_read_data(tm , edge , attotime::never);
+ word >>= 1;
+ if (data_bit) {
+ BIT_SET(word , 15);
+ }
+ update_crc(data_bit);
+ }
+ return word;
+}
+
+void hp9885_device::wr_word(uint16_t word)
+{
+ for (unsigned i = 0; i < 16; ++i) {
+ bool data_bit = BIT(word , i);
+ bool clock_bit = !data_bit && !m_had_transition;
+ m_had_transition = data_bit || clock_bit;
+ attotime dummy;
+
+ m_pll.write_next_bit(clock_bit , dummy , nullptr , attotime::never);
+ m_pll.write_next_bit(data_bit , dummy , nullptr , attotime::never);
+ update_crc(data_bit);
+ }
+ m_pll.commit(m_drive , m_pll.ctime);
+}
+
+void hp9885_device::preset_crc()
+{
+ m_crc = ~0;
+}
+
+void hp9885_device::update_crc(bool bit)
+{
+ bool crc_x15 = BIT(m_crc , 0);
+ m_crc >>= 1;
+ if (bit ^ crc_x15) {
+ m_crc ^= 0x8408;
+ }
+}
+
+void hp9885_device::set_ibf(bool state)
+{
+ m_ibf = state;
+ update_busy();
+}
+
+void hp9885_device::set_output()
+{
+ m_outputting = true;
+ m_obf = false;
+ set_ibf(false);
+}
+
+void hp9885_device::output_status(bool delayed)
+{
+ stop_rdwr();
+ if (delayed) {
+ set_output();
+ set_state(FSM_STATUS_DELAY);
+ m_fsm_timer->adjust(attotime::from_usec(STATUS_DELAY_US));
+ } else {
+ set_state(FSM_RD_STATUS1);
+ m_outputting = true;
+ m_obf = true;
+ // Set status in output buffer
+ m_output = m_status;
+ set_ibf(false);
+ }
+}
+
+void hp9885_device::update_busy()
+{
+ bool busy = (!m_outputting && m_ibf) || (m_outputting && m_pctl && m_obf);
+ LOG_HS("PCTL %d BUSY %d OUT %d IBF %d OBF %d\n" , m_pctl , busy , m_outputting , m_ibf , m_obf);
+ pflg_w(!busy);
+}
diff --git a/src/devices/bus/hp9845_io/hp9885.h b/src/devices/bus/hp9845_io/hp9885.h
new file mode 100644
index 00000000000..edb705c2504
--- /dev/null
+++ b/src/devices/bus/hp9845_io/hp9885.h
@@ -0,0 +1,136 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ hp9885.h
+
+ HP9885M 8" floppy drive
+
+*********************************************************************/
+
+#ifndef MAME_BUS_HP9845_IO_HP9885_H
+#define MAME_BUS_HP9845_IO_HP9885_H
+
+#pragma once
+
+#include "98032.h"
+#include "imagedev/floppy.h"
+#include "machine/fdc_pll.h"
+
+class hp9885_device : public hp98032_gpio_card_device
+{
+public:
+ hp9885_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ virtual ~hp9885_device();
+
+ // hp98032_gpio_card_device overrides
+ virtual uint16_t get_jumpers() const override;
+ virtual uint16_t input_r() const override;
+ virtual uint8_t ext_status_r() const override;
+ virtual void output_w(uint16_t data) override;
+ virtual void ext_control_w(uint8_t data) override;
+ virtual DECLARE_WRITE_LINE_MEMBER(pctl_w) override;
+ virtual DECLARE_WRITE_LINE_MEMBER(io_w) override;
+ virtual DECLARE_WRITE_LINE_MEMBER(preset_w) override;
+
+protected:
+ // device-level overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ // FSM states
+ enum {
+ FSM_IDLE,
+ FSM_RECALIBRATING,
+ FSM_SETTLING,
+ FSM_GOT_PW,
+ FSM_POSITIONING,
+ FSM_SEEKING,
+ FSM_STATUS_DELAY,
+ FSM_RD_STATUS1,
+ FSM_RD_STATUS2,
+ FSM_WAIT_ID_AM,
+ FSM_RD_ID,
+ FSM_WAIT_DATA_AM,
+ FSM_RD_DATA,
+ FSM_WR_DATA
+ };
+
+ // Head states
+ enum {
+ HEAD_UNLOADED,
+ HEAD_SETTLING,
+ HEAD_LOADED
+ };
+
+ // Operations
+ enum {
+ OP_NONE,
+ OP_READ,
+ OP_WRITE,
+ OP_STEP_IN,
+ OP_GET_STATUS
+ };
+
+ required_device<floppy_connector> m_drive_connector;
+ floppy_image_device *m_drive;
+ uint16_t m_input;
+ uint16_t m_output;
+ uint16_t m_status;
+ int m_fsm_state;
+ int m_head_state;
+ int m_op;
+ bool m_pctl;
+ bool m_ibf;
+ bool m_obf;
+ bool m_outputting;
+ bool m_had_transition;
+ bool m_dskchg;
+ unsigned m_track;
+ unsigned m_seek_track;
+ unsigned m_seek_sector;
+ unsigned m_sector_cnt;
+ unsigned m_word_cnt;
+ unsigned m_rev_cnt;
+ uint32_t m_am_detector;
+ uint16_t m_crc; // x^15 is stored in LSB
+
+ // Timers
+ emu_timer *m_fsm_timer;
+ emu_timer *m_head_timer;
+ emu_timer *m_bit_byte_timer;
+
+ // PLL
+ fdc_pll_t m_pll;
+
+ void floppy_ready_cb(floppy_image_device *floppy , int state);
+ void floppy_index_cb(floppy_image_device *floppy , int state);
+
+ void set_state(int new_state);
+ void init_status(unsigned unit_no);
+ void encode_error(bool writing);
+ void set_error(unsigned error_code);
+ void new_word();
+ void do_FSM();
+ bool load_head();
+ void recalibrate();
+ void one_step(bool outward);
+ void adv_sector();
+ void start_rd();
+ void stop_rdwr();
+ uint16_t rd_word();
+ void wr_word(uint16_t word);
+ void preset_crc();
+ void update_crc(bool bit);
+ void set_ibf(bool state);
+ void set_output();
+ void output_status(bool delayed = false);
+ void update_busy();
+};
+
+DECLARE_DEVICE_TYPE(HP9885 , hp9885_device)
+
+#endif /* MAME_BUS_HP9845_IO_HP9885_H */
diff --git a/src/lib/formats/hpi_dsk.cpp b/src/lib/formats/hpi_dsk.cpp
index 8d23f151862..63d5057de57 100644
--- a/src/lib/formats/hpi_dsk.cpp
+++ b/src/lib/formats/hpi_dsk.cpp
@@ -4,9 +4,10 @@
hpi_dsk.cpp
- HP9895A "HPI" disk images
+ HP9885/HP9895A "HPI" disk images
- CHS = 77/2/30
+ CHS = 67/1/30 (SSDD)
+ CHS = 77/2/30 (DSDD)
Sector size 256 bytes
Cell size 2 µs
Gap1 = 16 x 0x00
@@ -40,7 +41,7 @@
256 bytes per sector). There's also a "reduced" version holding
just 75 cylinders.
When loading, the disk image is translated to MMFM encoding so
- that it can be loaded into HP9895 emulator.
+ that it can be loaded into HP9885/HP9895 emulator.
*********************************************************************/
@@ -65,6 +66,7 @@ constexpr int ID_DATA_OFFSET = 30 * 16; // Nominal distance (in cells) between I
// Size of image file (holding 77 cylinders)
constexpr unsigned HPI_IMAGE_SIZE = HPI_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
constexpr unsigned HPI_RED_TRACKS = 75; // Reduced number of tracks
+constexpr unsigned HPI_9885_TRACKS = 67; // Tracks visible to HP9885 drives
// Size of reduced image file (holding 75 cylinders)
constexpr unsigned HPI_RED_IMAGE_SIZE = HPI_RED_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
@@ -79,27 +81,79 @@ int hpi_format::identify(io_generic *io, uint32_t form_factor)
// we try to stay back and give only 50 points, since another image
// format may also have images of the same size (there is no header and no
// magic number for HPI format...
+ unsigned dummy_heads;
+ unsigned dummy_tracks;
if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
- ((size == HPI_RED_IMAGE_SIZE) || (size == HPI_IMAGE_SIZE))) {
+ geometry_from_size(size , dummy_heads , dummy_tracks)) {
return 50;
} else {
return 0;
}
}
+bool hpi_format::geometry_from_size(uint64_t image_size , unsigned& heads , unsigned& tracks)
+{
+ if ((image_size % HPI_SECTOR_SIZE) != 0) {
+ // Not a whole number of sectors
+ return false;
+ }
+ unsigned sectors = unsigned(image_size / HPI_SECTOR_SIZE);
+ if ((sectors % HPI_SECTORS) != 0) {
+ // Not a whole number of tracks
+ return false;
+ }
+ unsigned tot_tracks = sectors / HPI_SECTORS;
+ // Possible combinations
+ //
+ // | Tot tracks | Heads | Tracks | Format |
+ // |------------+-------+--------+--------|
+ // | 67 | 1 | 67 | SSDD |
+ // | 77 | 1 | 77 | SSDD |
+ // | 150 | 2 | 75 | DSDD |
+ // | 154 | 2 | 77 | DSDD |
+ //
+ switch (tot_tracks) {
+ case HPI_9885_TRACKS:
+ heads = 1;
+ tracks = HPI_9885_TRACKS;
+ return true;
+
+ case HPI_TRACKS:
+ heads = 1;
+ tracks = HPI_TRACKS;
+ return true;
+
+ case HPI_RED_TRACKS * 2:
+ heads = 2;
+ tracks = HPI_RED_TRACKS;
+ return true;
+
+ case HPI_TRACKS * 2:
+ heads = 2;
+ tracks = HPI_TRACKS;
+ return true;
+
+ default:
+ return false;
+ }
+}
+
bool hpi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
- image->set_variant(floppy_image::DSDD); // We actually need to derive the variant from the image size depending on the form factor
+ unsigned heads;
+ unsigned cylinders;
uint64_t size = io_generic_size(io);
- unsigned cylinders;
- if (size == HPI_RED_IMAGE_SIZE) {
- cylinders = HPI_RED_TRACKS;
- } else if (size == HPI_IMAGE_SIZE) {
- cylinders = HPI_TRACKS;
- } else {
+ if (!geometry_from_size(size, heads, cylinders)) {
+ return false;
+ }
+ int max_tracks;
+ int max_heads;
+ image->get_maximal_geometry(max_tracks , max_heads);
+ if (cylinders > max_tracks || heads > max_heads) {
return false;
}
+ image->set_variant(heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
// Suck in the whole image
std::vector<uint8_t> image_data(size);
@@ -118,11 +172,11 @@ bool hpi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
unsigned list_offset = 0;
for (unsigned cyl = 0; cyl < cylinders; cyl++) {
- for (unsigned head = 0; head < HPI_HEADS; head++) {
+ for (unsigned head = 0; head < heads; head++) {
std::vector<uint32_t> track_data;
for (unsigned sector = 0; sector < HPI_SECTORS; sector++) {
unsigned real_sector = sector_list[ (sector + list_offset) % HPI_SECTORS ];
- unsigned offset_in_image = chs_to_lba(cyl, head, real_sector) * HPI_SECTOR_SIZE;
+ unsigned offset_in_image = chs_to_lba(cyl, head, real_sector, heads) * HPI_SECTOR_SIZE;
write_sector(track_data , cyl , real_sector + (head << 7) , &image_data[ offset_in_image ]);
}
fill_with_gap3(track_data);
@@ -135,8 +189,12 @@ bool hpi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
bool hpi_format::save(io_generic *io, floppy_image *image)
{
- for (unsigned cyl = 0; cyl < HPI_TRACKS; cyl++) {
- for (unsigned head = 0; head < HPI_HEADS; head++) {
+ int tracks;
+ int heads;
+ image->get_actual_geometry(tracks, heads);
+
+ for (int cyl = 0; cyl < tracks; cyl++) {
+ for (int head = 0; head < heads; head++) {
uint8_t bitstream[ 21000 ];
int bitstream_size;
generate_bitstream_from_track(cyl , head , CELL_SIZE , bitstream , bitstream_size , image , 0);
@@ -145,7 +203,7 @@ bool hpi_format::save(io_generic *io, floppy_image *image)
uint8_t sector_data[ HPI_SECTOR_SIZE ];
while (get_next_sector(bitstream , bitstream_size , pos , track_no , head_no , sector_no , sector_data)) {
if (track_no == cyl && head_no == head && sector_no < HPI_SECTORS) {
- unsigned offset_in_image = chs_to_lba(cyl, head, sector_no) * HPI_SECTOR_SIZE;
+ unsigned offset_in_image = chs_to_lba(cyl, head, sector_no, heads) * HPI_SECTOR_SIZE;
io_generic_write(io, sector_data, offset_in_image, HPI_SECTOR_SIZE);
}
}
@@ -300,9 +358,9 @@ void hpi_format::fill_with_gap3(std::vector<uint32_t> &buffer)
}
}
-unsigned hpi_format::chs_to_lba(unsigned cylinder , unsigned head , unsigned sector)
+unsigned hpi_format::chs_to_lba(unsigned cylinder , unsigned head , unsigned sector , unsigned heads)
{
- return sector + (head + cylinder * HPI_HEADS) * HPI_SECTORS;
+ return sector + (head + cylinder * heads) * HPI_SECTORS;
}
std::vector<uint8_t> hpi_format::get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos)
diff --git a/src/lib/formats/hpi_dsk.h b/src/lib/formats/hpi_dsk.h
index 8457db1ea24..287f37fd302 100644
--- a/src/lib/formats/hpi_dsk.h
+++ b/src/lib/formats/hpi_dsk.h
@@ -36,6 +36,7 @@ public:
private:
typedef std::array<uint8_t , HPI_SECTORS> sector_list_t;
+ static bool geometry_from_size(uint64_t image_size , unsigned& heads , unsigned& tracks);
static void interleaved_sectors(unsigned il_factor , sector_list_t& sector_list);
void write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit);
void write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock = 0);
@@ -43,7 +44,7 @@ private:
void write_crc(std::vector<uint32_t> &buffer , uint16_t crc);
void write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_head_no , const uint8_t *sect_data);
void fill_with_gap3(std::vector<uint32_t> &buffer);
- static unsigned chs_to_lba(unsigned cylinder , unsigned head , unsigned sector);
+ static unsigned chs_to_lba(unsigned cylinder , unsigned head , unsigned sector , unsigned heads);
std::vector<uint8_t> get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos);
bool get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data);
diff --git a/src/mame/drivers/hp9825.cpp b/src/mame/drivers/hp9825.cpp
index 063387c8426..6cad2fd45c7 100644
--- a/src/mame/drivers/hp9825.cpp
+++ b/src/mame/drivers/hp9825.cpp
@@ -17,10 +17,16 @@
// - Printer
// - Beeper
// - Internal expansion ROMs
+// - I/O expansion slots: 98032, 98034 & 98035 modules can be connected
// What's not yet in:
// - External expansion ROMs
// - Configurable RAM size
-// - I/O expansion slots: 98034 & 98035 modules from hp9845 emulation can be used here, too
+//
+// Thanks to Dyke Shaffer for publishing (on https://groups.io/g/VintHPcom)
+// the source code of 98217 mass memory ROM. The 98217.bin image was reconstructed
+// by re-assembling the source code (this is the reason why it's marked as
+// a BAD_DUMP). And thanks to Ansgar Kueckes for adapting his assembler to
+// handle HP9825 source files.
//
// 9825A & 9825T can also be emulated. At the moment I haven't all the necessary
// ROM dumps, though.
@@ -225,8 +231,9 @@ void hp9825_state::cpu_io_map(address_map &map)
void hp9825_state::cpu_mem_map(address_map &map)
{
map.unmap_value_low();
- map(0x0000 , 0x2fff).rom();
- map(0x3400 , 0x3bff).rom();
+ // map(0x0000 , 0x2fff).rom();
+ // map(0x3400 , 0x3bff).rom();
+ map(0x0000 , 0x3bff).rom();
map(0x4000 , 0x4fff).rom();
map(0x5000 , 0x7fff).ram();
}
@@ -824,6 +831,7 @@ ROM_START(hp9825b)
ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0))
ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54))
ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a))
+ ROM_LOAD("98217.bin" , 0x6000 , 0x0800 , BAD_DUMP CRC(ea1fcf63) SHA1(e535c82897210a1c67c1ca16f44f936d4c470463))
ROM_LOAD("genio_t.bin" , 0x6800 , 0x0800 , CRC(ade1d1ed) SHA1(9af74a65b29ef1885f74164238ecf8d16ac995d6))
ROM_LOAD("plot72.bin" , 0x7000 , 0x0800 , CRC(0a9cb8db) SHA1(d0d126fca108f2715e1e408cb31b09ba69385ac4))
ROM_LOAD("advpgm_t.bin", 0x8000 , 0x0800 , CRC(965b5e5a) SHA1(ff44dd15f8fa4ca03dfd970ed8b200e8a071ec13))