diff options
author | 2020-01-04 22:11:48 +0100 | |
---|---|---|
committer | 2020-01-04 16:11:48 -0500 | |
commit | 2ec25ca6d5cf0dd68d5556f8a58af0dc5b1e2260 (patch) | |
tree | 8256049c139f4ad9bacbaed240276c4b05249a5d | |
parent | c59bf24ff119dc271f2754e926b69624f5e9b004 (diff) |
Emulation of isbc202 floppy disk controller (#6119)
* imds2: isbc202 floppy disk controller added
* imds2: attempt to fix problems with CI checks
-rw-r--r-- | scripts/src/bus.lua | 14 | ||||
-rw-r--r-- | scripts/src/formats.lua | 12 | ||||
-rw-r--r-- | scripts/src/machine.lua | 24 | ||||
-rw-r--r-- | scripts/target/mame/mess.lua | 4 | ||||
-rw-r--r-- | src/devices/bus/multibus/isbc202.cpp | 1195 | ||||
-rw-r--r-- | src/devices/bus/multibus/isbc202.h | 160 | ||||
-rw-r--r-- | src/devices/bus/multibus/multibus.cpp | 59 | ||||
-rw-r--r-- | src/devices/bus/multibus/multibus.h | 54 | ||||
-rw-r--r-- | src/devices/machine/i3001.cpp | 137 | ||||
-rw-r--r-- | src/devices/machine/i3001.h | 106 | ||||
-rw-r--r-- | src/devices/machine/i3002.cpp | 428 | ||||
-rw-r--r-- | src/devices/machine/i3002.h | 130 | ||||
-rw-r--r-- | src/lib/formats/img_dsk.cpp | 214 | ||||
-rw-r--r-- | src/lib/formats/img_dsk.h | 54 | ||||
-rw-r--r-- | src/mame/drivers/imds2.cpp | 8 | ||||
-rw-r--r-- | src/tools/floptool.cpp | 2 |
16 files changed, 2601 insertions, 0 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index d917a846642..17261a3addb 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -4085,3 +4085,17 @@ if (BUSES["TMC600"]~=null) then MAME_DIR .. "src/devices/bus/tmc600/euro.h", } end + +--------------------------------------------------- +-- +--@src/devices/bus/multibus/multibus.h,BUSES["MULTIBUS"] = true +--------------------------------------------------- + +if (BUSES["MULTIBUS"]~=null) then + files { + MAME_DIR .. "src/devices/bus/multibus/multibus.cpp", + MAME_DIR .. "src/devices/bus/multibus/multibus.h", + MAME_DIR .. "src/devices/bus/multibus/isbc202.cpp", + MAME_DIR .. "src/devices/bus/multibus/isbc202.h", + } +end diff --git a/scripts/src/formats.lua b/scripts/src/formats.lua index 6bb724e3e40..e09d34c2e33 100644 --- a/scripts/src/formats.lua +++ b/scripts/src/formats.lua @@ -1003,6 +1003,18 @@ end -------------------------------------------------- -- +--@src/lib/formats/img_dsk.h,FORMATS["IMG_DSK"] = true +-------------------------------------------------- + +if (FORMATS["IMG_DSK"]~=null or _OPTIONS["with-tools"]) then + files { + MAME_DIR.. "src/lib/formats/img_dsk.cpp", + MAME_DIR.. "src/lib/formats/img_dsk.h", + } +end + +-------------------------------------------------- +-- --@src/lib/formats/iq151_dsk.h,FORMATS["IQ151_DSK"] = true -------------------------------------------------- diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 7d1eaef0e7b..f04cb88e031 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -4196,3 +4196,27 @@ if (MACHINES["VRENDER0"]~=null) then MAME_DIR .. "src/devices/machine/vrender0.h", } end + +--------------------------------------------------- +-- +--@src/devices/machine/i3001.h,MACHINES["I3001"] = true +--------------------------------------------------- + +if (MACHINES["I3001"]~=null) then + files { + MAME_DIR .. "src/devices/machine/i3001.cpp", + MAME_DIR .. "src/devices/machine/i3001.h", + } +end + +--------------------------------------------------- +-- +--@src/devices/machine/i3002.h,MACHINES["I3002"] = true +--------------------------------------------------- + +if (MACHINES["I3002"]~=null) then + files { + MAME_DIR .. "src/devices/machine/i3002.cpp", + MAME_DIR .. "src/devices/machine/i3002.h", + } +end diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 8bc8627ae2b..1c89fe13140 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -486,6 +486,8 @@ MACHINES["HP_TACO"] = true MACHINES["1MA6"] = true MACHINES["1MB5"] = true MACHINES["I2CMEM"] = true +MACHINES["I3001"] = true +MACHINES["I3002"] = true MACHINES["I7220"] = true MACHINES["I80130"] = true MACHINES["I8087"] = true @@ -825,6 +827,7 @@ BUSES["MIDI"] = true BUSES["MEGADRIVE"] = true BUSES["MSX_SLOT"] = true BUSES["MTX"] = true +BUSES["MULTIBUS"] = true BUSES["NASBUS"] = true BUSES["NEOGEO"] = true BUSES["NEOGEO_CTRL"] = true @@ -975,6 +978,7 @@ FORMATS["HECT_TAP"] = true FORMATS["HTI_TAP"] = true FORMATS["HPI_DSK"] = true FORMATS["HP_IPC_DSK"] = true +FORMATS["IMG_DSK"] = true FORMATS["IQ151_DSK"] = true FORMATS["ITT3030_DSK"] = true FORMATS["JFD_DSK"] = true diff --git a/src/devices/bus/multibus/isbc202.cpp b/src/devices/bus/multibus/isbc202.cpp new file mode 100644 index 00000000000..c8fe127c277 --- /dev/null +++ b/src/devices/bus/multibus/isbc202.cpp @@ -0,0 +1,1195 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + isbc202.cpp + + Intel iSBC-202 SSDD 8" floppy disk controller + + This controller interfaces a standard 8/16-bit Multibus system + with up to 4 8" SSDD floppy drives. It was typically used to + expand a MDS-II system with double-density drives. + The ISIS-II OS identifies the drives as :F0: :F1: :F2: :F3: + The iSBC-202 controller is based on two boards: a "channel" + board with the actual controller and an "interface" board. + The latter handles the low-level aspects of the disks, especially + the encoding and decoding of MMFM modulated bits. This board + interfaces directly with standard Shugart SA-800-1 drives. + The drives are housed, two at time, in a (big) external box + that also holds their power supply. + The channel board is implemented with a 3000-series bit slice + processor. Microcode is stored on 4 512x8 bipolar PROMs. + The channel board is bus mastering, i.e. it can issue memory + read/write cycles to the main processor RAM. + Format of data on disk is entirely Intel proprietary. + Intel also designed a similar controller (iSBC-201) for FM + IBM-standard disks. AFAIK, this board shares the channel board + with iSBC-202 (but not the microcode) and has a different + interface board. + This table summarizes the main characteristics of the disks. + + | Bit cell size | 2 µs | + | Modulation | MMFM | + | Bit order | MS first | + | Sides | 1 | + | Tracks | 77 | + | Sectors/track | 52 | + | Sector size | 128 bytes| + | Formatted size | 500.5 kB | + | Rotation speed | 360 RPM | + + Special thanks to Eric Smith for dumping the microcode PROMs. + Without his work this driver wouldn't exist at all. + + Reference manuals. + - Intellec series II MDS double-density diskette subsystem + (schematic drawings) - Intel 1980 - 9800425-02 Rev. B + - Intellec double-density diskette operating system hardware + reference manual - Intel 1977 - 9800422A + - SBC 202 double-density diskette controller hardware reference + manual - Intel 1977 - 9800420A + + What follows is a list of things that I left out. They could be + implemented at a later time just for completeness sake as ISIS-II + doesn't rely on them at all. + - The STOP signal (it seems to be used in iSBC-201 only) + - Interrupt to CPU + - Head load/unload commands (MAME doesn't emulate head loading) + +*********************************************************************/ + +#include "emu.h" +#include "isbc202.h" +#include "formats/img_dsk.h" + +// Debugging +#include "logmacro.h" +#define LOG_BUS_MASK (LOG_GENERAL << 1) +#define LOG_BUS(...) LOGMASKED(LOG_BUS_MASK, __VA_ARGS__) +#define LOG_RD_MASK (LOG_BUS_MASK << 1) +#define LOG_RD(...) LOGMASKED(LOG_RD_MASK, __VA_ARGS__) +#define LOG_WR_MASK (LOG_RD_MASK << 1) +#define LOG_WR(...) LOGMASKED(LOG_WR_MASK, __VA_ARGS__) +#define LOG_DR_MASK (LOG_WR_MASK << 1) +#define LOG_DR(...) LOGMASKED(LOG_DR_MASK, __VA_ARGS__) +#undef VERBOSE +//#define VERBOSE (LOG_GENERAL|LOG_BUS_MASK|LOG_RD_MASK|LOG_WR_MASK|LOG_DR_MASK) +#define VERBOSE LOG_GENERAL + +// 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); + } +} + +// Constants +constexpr unsigned TIMEOUT_MS = 10; // "timeout" timer: 10 ms +constexpr unsigned HALF_BIT_CELL_US = 1;// Half bit cell duration in µs +constexpr unsigned BIT_FREQUENCY = 500000; // Frequency of bit cells in Hz +constexpr uint16_t CRC_POLY = 0x1021; // CRC-CCITT + +// Timers +enum { + TIMEOUT_TMR_ID, + BYTE_TMR_ID, + F_TMR_ID +}; + +// device type definition +DEFINE_DEVICE_TYPE(ISBC202, isbc202_device, "iSBC202", "iSBC-202 floppy controller") + +// Microcode disassembler +class isbc202_disassembler : public util::disasm_interface +{ +public: + isbc202_disassembler(); + virtual ~isbc202_disassembler() = default; + + virtual u32 opcode_alignment() const override; + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; +}; + +isbc202_disassembler::isbc202_disassembler() +{ +} + +u32 isbc202_disassembler::opcode_alignment() const +{ + return 1; +} + +offs_t isbc202_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + uint32_t microcode = opcodes.r32(pc); + + // Decode address control instruction + uint8_t ac = (uint8_t)(microcode >> 25); + + if ((ac & 0b1100000) == 0b0000000) { + // JCC + util::stream_format(stream , "JCC $%03x" , (((uint16_t)ac & 0b11111) << 4) | (pc & 0xf)); + } else if ((ac & 0b1110000) == 0b0100000) { + // JZR + util::stream_format(stream , "JZR $00%x" , ac & 0b1111); + } else if ((ac & 0b1110000) == 0b0110000) { + // JCR + util::stream_format(stream , "JCR $%03x" , (pc & 0b111110000) | (ac & 0b1111)); + } else if ((ac & 0b1111000) == 0b1110000) { + // JCE + util::stream_format(stream , "JCE $%03x" , (pc & 0b110001111) | ((ac & 0b111) << 4)); + } else if ((ac & 0b1110000) == 0b1000000) { + // JFL + util::stream_format(stream , "JFL $%03x" , (pc & 0b100001000) | ((ac & 0b1111) << 4) | 0b10); + } else if ((ac & 0b1111000) == 0b1010000) { + // JCF + util::stream_format(stream , "JCF $%03x" , (pc & 0b110001000) | ((ac & 0b111) << 4) | 0b10); + } else if ((ac & 0b1111000) == 0b1011000) { + // JZF + util::stream_format(stream , "JZF $%03x" , (pc & 0b110001000) | ((ac & 0b111) << 4) | 0b10); + } else if ((ac & 0b1111000) == 0b1100000) { + // JPR + util::stream_format(stream , "JPR $%02xx" , ((pc >> 4) & 0b11000) | (ac & 0b111)); + } else if ((ac & 0b1111000) == 0b1101000) { + // JLL + util::stream_format(stream , "JLL $%03x" , (pc & 0b110000000) | ((ac & 0b111) << 4) | 0b100); + } else if ((ac & 0b1111100) == 0b1111100) { + // JRL + util::stream_format(stream , "JRL $%03x" , (pc & 0b110000000) | ((ac & 0b11) << 4) | 0b001001100); + } else { + // JPX + util::stream_format(stream , "JPX $%02xx" , ((pc >> 4) & 0b11100) | (ac & 0b11)); + } + + // Decode input multiplexer + uint8_t in = (uint8_t)((microcode >> 13) & 7); + util::stream_format(stream , " I=%u" , in); + + // Decode function code + uint8_t fc = (uint8_t)((microcode >> 18) & 0x7f); + uint8_t fg; + uint8_t rg; + unsigned reg; + i3002_device::decode_fc(fc , fg , rg , reg); + util::stream_format(stream , " CPE=%u%u/%2s" , fg , rg + 1 , i3002_device::reg_name(reg)); + + // Decode flag control + stream << (BIT(microcode , 17) ? " FF1" : " FF0"); + stream << (BIT(microcode , 16) ? " HCZ" : " SCZ"); + + // Decode K + uint8_t slk = (uint8_t)((microcode >> 8) & 3); + uint8_t mask = (uint8_t)microcode; + uint8_t kbus; + if (!BIT(slk , 0)) { + kbus = mask; + } else if (slk == 1) { + kbus = 0xff; + } else { + kbus = 0; + } + util::stream_format(stream , " K=$%02x" , kbus); + + // Decode OUT + util::stream_format(stream , " S=%u M=$%02x" , slk , mask); + if (BIT(slk , 0) && !BIT(mask , 7)) { + uint8_t out = (uint8_t)((microcode >> 10) & 7); + util::stream_format(stream , " O=%u" , out); + } + + return 1 | SUPPORTED; +} + +// isbc202_device +isbc202_device::isbc202_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : cpu_device(mconfig , ISBC202 , tag , owner , multibus_slot_device::BUS_CLOCK / 4) + , device_multibus_interface(mconfig , *this) + , m_mcu(*this , "mcu") + , m_cpes(*this , "cpe%u" , 0) + , m_drives(*this , "floppy%u" , 0) + , m_program_config("microprogram" , ENDIANNESS_BIG , 32 , 9 , -2) + , m_cache(nullptr) + , m_mem_space(nullptr) +{ +} + +isbc202_device::~isbc202_device() +{ +} + +void isbc202_device::install_io_rw(address_space& space) +{ + space.install_readwrite_handler(0x78 , 0x7f , read8_delegate(*this , FUNC(isbc202_device::io_r)) , write8_delegate(*this , FUNC(isbc202_device::io_w))); +} + +void isbc202_device::install_mem_rw(address_space& space) +{ + m_mem_space = &space; +} + +READ8_MEMBER(isbc202_device::io_r) +{ + uint8_t res = 0; + + switch (offset) { + case 0: + // Read drive status & INT (auto XACK) + // Bit What + // 7 0 + // 6 Drive 3 ready + // 5 Drive 2 ready + // 4 1 + // 3 1 + // 2 Int pending + // 1 Drive 1 ready + // 0 Drive 0 ready + { + uint8_t ready = m_ready_in & m_ready_ff; + if (BIT(ready , 3)) { + BIT_SET(res , 6); + } + if (BIT(ready , 2)) { + BIT_SET(res , 5); + } + BIT_SET(res , 4); + BIT_SET(res , 3); + if (m_irq) { + BIT_SET(res , 2); + } + if (BIT(ready , 1)) { + BIT_SET(res , 1); + } + if (BIT(ready , 0)) { + BIT_SET(res , 0); + } + } + break; + + case 1: + // Read result type (auto XACK) + m_irq = false; + res = m_data_low_out; + break; + + case 3: + // Read result byte (no auto XACK) + if (m_cpu == nullptr) { + m_cpu = dynamic_cast<cpu_device*>(&space.device()); + set_start(3 , true); + } else { + m_cpu = nullptr; + res = m_data_low_out; + } + break; + + default: + LOG("RD from unknown reg!\n"); + break; + } + + LOG_BUS("IO R @%u=%02x\n" , offset , res); + return res; +} + +WRITE8_MEMBER(isbc202_device::io_w) +{ + LOG_BUS("IO W @%u=%02x\n" , offset , data); + + switch (offset) { + case 0: + case 1: + // Write LSB address + case 2: + // Write MSB address & start op + case 4: + case 5: + case 6: + if (m_cpu != nullptr) { + LOG("CPU != NULL!\n"); + } + m_cpu = dynamic_cast<cpu_device*>(&space.device()); + m_cpu_data = data; + set_start(offset , false); + break; + + case 7: + // Reset + pulse_input_line(INPUT_LINE_RESET , attotime::zero); + break; + + default: + LOG("WR to unknown reg!\n"); + break; + } +} + +WRITE_LINE_MEMBER(isbc202_device::co_w) +{ + m_inputs[ IN_SEL_CO ] = state; + m_mcu->fi_w(state); + m_cpes[ 3 ]->li_w(state); +} + +READ8_MEMBER(isbc202_device::px_r) +{ + if (BIT(m_px_s1s0 , 0)) { + return m_cmd & 7; + } else if (BIT(m_px_s1s0 , 1)) { + return (m_op_us & 7) | 8; + } else { + return 0; + } +} + +void isbc202_device::device_start() +{ + state_add(STATE_GENPC , "GENPC" , m_microcode_addr).noshow(); + state_add(STATE_GENPCBASE , "CURPC" , m_microcode_addr).noshow(); + state_add(STATE_GENFLAGS , "FLAGS" , m_flags).noshow().callimport().callexport().formatstr("%9s"); + + for (int i = 0; i < i3002_device::REG_COUNT; ++i) { + state_add(i , i3002_device::reg_name(i) , m_regs[ i ]).callimport().callexport(); + } + + save_item(NAME(m_flags)); + save_item(NAME(m_regs)); + save_item(NAME(m_microcode_addr)); + save_item(NAME(m_code_word)); + save_item(NAME(m_ac)); + save_item(NAME(m_fc)); + save_item(NAME(m_fc32)); + save_item(NAME(m_fc10)); + save_item(NAME(m_in_sel)); + save_item(NAME(m_out_sel)); + save_item(NAME(m_slk)); + save_item(NAME(m_mask)); + save_item(NAME(m_kbus)); + save_item(NAME(m_inputs)); + save_item(NAME(m_op_us)); + save_item(NAME(m_px_s1s0)); + save_item(NAME(m_cmd)); + save_item(NAME(m_cpu_rd)); + save_item(NAME(m_ready_in)); + save_item(NAME(m_ready_ff)); + save_item(NAME(m_gate_lower)); + save_item(NAME(m_irq)); + save_item(NAME(m_data_low_out)); + save_item(NAME(m_data_low_in)); + save_item(NAME(m_cpu_data)); + save_item(NAME(m_addr_low_out)); + save_item(NAME(m_mem_wrt)); + save_item(NAME(m_wrt_inh)); + save_item(NAME(m_direction)); + save_item(NAME(m_ibus_cached)); + save_item(NAME(m_ibus)); + save_item(NAME(m_crc)); + save_item(NAME(m_crc_enabled)); + save_item(NAME(m_crc_out)); + save_item(NAME(m_reading)); + save_item(NAME(m_writing)); + save_item(NAME(m_data_sr)); + save_item(NAME(m_last_data_bit)); + save_item(NAME(m_clock_sr)); + save_item(NAME(m_last_f_time)); + save_item(NAME(m_clock_gate)); + save_item(NAME(m_amwrt)); + save_item(NAME(m_dlyd_amwrt)); + + m_cache = space(AS_PROGRAM).cache<2 , -2 , ENDIANNESS_BIG>(); + set_icountptr(m_icount); + space(AS_PROGRAM).install_rom(0 , 0x1ff , memregion("microcode")->base()); + + for (auto& d : m_drives) { + d->get_device()->setup_ready_cb(floppy_image_device::ready_cb(&isbc202_device::floppy_ready_cb , this)); + d->get_device()->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&isbc202_device::floppy_index_cb , this)); + } + + m_timeout_timer = timer_alloc(TIMEOUT_TMR_ID); + m_byte_timer = timer_alloc(BYTE_TMR_ID); + m_f_timer = timer_alloc(F_TMR_ID); +} + +void isbc202_device::device_reset() +{ + // Set start address + m_mcu->addr_w(0); + // Select drive #0 + m_op_us = 0; + m_current_drive = m_drives[ 0 ]->get_device(); + + // XFERREQ is always 1 because R/W in CPU memory is instantaneous + m_inputs[ IN_SEL_XFERQ ] = true; + + m_inputs[ IN_SEL_TIMEOUT ] = true; + m_inputs[ IN_SEL_F ] = false; + + m_cpu = nullptr; + + m_irq = false; + + m_reading = false; + m_writing = false; + + m_timeout_timer->reset(); + m_byte_timer->reset(); + m_f_timer->reset(); +} + +void isbc202_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) { + case TIMEOUT_TMR_ID: + m_inputs[ IN_SEL_TIMEOUT ] = true; + break; + + case BYTE_TMR_ID: + m_inputs[ IN_SEL_F ] = true; + m_f_timer->adjust(attotime::from_usec(HALF_BIT_CELL_US * 2)); + m_dlyd_amwrt = m_amwrt; + if (m_reading) { + m_last_f_time = machine().time(); + rd_bits(8); + m_byte_timer->adjust(m_pll.ctime - machine().time()); + // Updating of AZ flag actually happens when F goes low + m_inputs[ IN_SEL_AZ ] = m_crc == 0; + } + break; + + case F_TMR_ID: + m_inputs[ IN_SEL_F ] = false; + if (m_writing) { + write_byte(); + m_data_sr = dbus_r(); + m_byte_timer->adjust(attotime::from_usec(HALF_BIT_CELL_US * 14)); + } + break; + + default: + break; + } +} + +ROM_START(isbc202) + ROM_REGION(0x800 , "microcode" , ROMREGION_32BIT | ROMREGION_BE) + ROM_LOAD32_BYTE("sbc202-a10-0230.bin" , 0x000 , 0x200 , CRC(e8fa3893) SHA1(88fab74b0466e8aac36eee46cd7536ed1b32a2c9)) + ROM_LOAD32_BYTE("sbc202-a11-0261.bin" , 0x001 , 0x200 , CRC(3ad01769) SHA1(4c22b8fc3ea599dd49684ff4dcafc29ec3425c4c)) + ROM_LOAD32_BYTE("sbc202-a12-0233.bin" , 0x002 , 0x200 , CRC(61496232) SHA1(b0473217944b2f6e966d97e97cf5ad8d883a09e4)) + ROM_LOAD32_BYTE("sbc202-a13-0232.bin" , 0x003 , 0x200 , CRC(c369ab86) SHA1(fc3b7f9c3e71ea1442827c51247a9944c6d40b37)) +ROM_END + +const tiny_rom_entry *isbc202_device::device_rom_region() const +{ + return ROM_NAME(isbc202); +} + +static void isbc202_floppies(device_slot_interface &device) +{ + device.option_add("8ssdd" , FLOPPY_8_SSDD); +} + +static const floppy_format_type isbc202_floppy_formats[] = { + FLOPPY_MFI_FORMAT, + FLOPPY_IMG_FORMAT, + nullptr +}; + +void isbc202_device::device_add_mconfig(machine_config &config) +{ + I3001(config , m_mcu , 0); + + // Allocation of the bit-slices: + // m_cpes[ 0 ] Bits 0..1 + // m_cpes[ 1 ] Bits 2..3 + // m_cpes[ 2 ] Bits 4..5 + // m_cpes[ 3 ] Bits 6..7 + for (auto& finder : m_cpes) { + I3002(config , finder , 0); + } + + // Connect CO/CI signals + m_mcu->fo_w().set(m_cpes[ 0 ] , FUNC(i3002_device::ci_w)); + m_cpes[ 0 ]->co_w().set(m_cpes[ 1 ] , FUNC(i3002_device::ci_w)); + m_cpes[ 1 ]->co_w().set(m_cpes[ 2 ] , FUNC(i3002_device::ci_w)); + m_cpes[ 2 ]->co_w().set(m_cpes[ 3 ] , FUNC(i3002_device::ci_w)); + m_cpes[ 3 ]->co_w().set(FUNC(isbc202_device::co_w)); + + // Connect RO/LI signals + m_cpes[ 0 ]->ro_w().set(FUNC(isbc202_device::co_w)); + m_cpes[ 1 ]->ro_w().set(m_cpes[ 0 ] , FUNC(i3002_device::li_w)); + m_cpes[ 2 ]->ro_w().set(m_cpes[ 1 ] , FUNC(i3002_device::li_w)); + m_cpes[ 3 ]->ro_w().set(m_cpes[ 2 ] , FUNC(i3002_device::li_w)); + + // Connect M-bus + m_cpes[ 0 ]->mbus_r().set([this]() { return mbus_r(); }); + m_cpes[ 1 ]->mbus_r().set([this]() { return mbus_r() >> 2; }); + m_cpes[ 2 ]->mbus_r().set([this]() { return mbus_r() >> 4; }); + m_cpes[ 3 ]->mbus_r().set([this]() { return mbus_r() >> 6; }); + + // Connect I-bus + m_cpes[ 0 ]->ibus_r().set([this]() { return ibus_r(); }); + m_cpes[ 1 ]->ibus_r().set([this]() { return ibus_r() >> 2; }); + m_cpes[ 2 ]->ibus_r().set([this]() { return ibus_r() >> 4; }); + m_cpes[ 3 ]->ibus_r().set([this]() { return ibus_r() >> 6; }); + + // Connect SX input + m_mcu->sx_r().set([this]() { return m_microcode_addr & 0xf; }); + // Connect PX input + m_mcu->px_r().set(FUNC(isbc202_device::px_r)); + + // Drives + for (auto& finder : m_drives) { + FLOPPY_CONNECTOR(config , finder , isbc202_floppies , "8ssdd" , isbc202_floppy_formats).set_fixed(true); + } +} + +void isbc202_device::execute_run() +{ + do { + m_microcode_addr = m_mcu->addr_r(); + debugger_instruction_hook(m_microcode_addr); + m_code_word = m_cache->read_dword(m_microcode_addr); + + // Unpack microcode into fields + // Bits Field + //================ + // 31..25 Address Control + // 24..18 Function Code + // 17 Flag Control bits 3 & 2 + // 16 Flag Control bits 1 & 0 + // 15..13 Input multiplexer selection + // 12..10 Output control + // 9..8 SLK field + // 7..0 Mask field + m_ac = (uint8_t)(m_code_word >> 25); + m_fc = (uint8_t)((m_code_word >> 18) & 0x7f); + m_fc32 = BIT(m_code_word , 17); + m_fc10 = BIT(m_code_word , 16); + m_in_sel = (uint8_t)((m_code_word >> 13) & 7); + m_out_sel = (uint8_t)((m_code_word >> 10) & 7); + m_slk = (uint8_t)((m_code_word >> 8) & 3); + m_mask = (uint8_t)m_code_word; + + m_mcu->fc_w((m_fc32 ? 0b1100 : 0b0000) | (m_fc10 ? 0b0011 : 0b0000)); + + // Set outputs + if (BIT(m_slk , 0) && !BIT(m_mask , 7)) { + set_output(); + } + + // Compute K-bus + if (!BIT(m_slk , 0)) { + m_kbus = m_mask; + } else if (m_slk == 1) { + m_kbus = 0xff; + } else { + m_kbus = 0; + } + + m_ibus_cached = false; + + // Update CPEs + m_cpes[ 0 ]->fc_kbus_w(m_fc , m_kbus); + m_cpes[ 1 ]->fc_kbus_w(m_fc , m_kbus >> 2); + m_cpes[ 2 ]->fc_kbus_w(m_fc , m_kbus >> 4); + m_cpes[ 3 ]->fc_kbus_w(m_fc , m_kbus >> 6); + if (m_cpes[ 0 ]->update_ro()) { + // Data propagate to right (right-shift op) + m_cpes[ 3 ]->clk_w(1); + m_cpes[ 2 ]->clk_w(1); + m_cpes[ 1 ]->clk_w(1); + m_cpes[ 0 ]->clk_w(1); + } else { + // Data propagate to left (every op but right-shift) + m_cpes[ 0 ]->clk_w(1); + m_cpes[ 1 ]->clk_w(1); + m_cpes[ 2 ]->clk_w(1); + m_cpes[ 3 ]->clk_w(1); + } + + // Update MCU + if (m_in_sel == IN_SEL_AC0) { + m_mcu->ac_w(m_ac); + } else { + m_mcu->ac_w((m_ac & 0b1111110) | m_inputs[ m_in_sel ]); + } + m_mcu->clk_w(1); + + m_icount--; + } while (m_icount > 0); +} + +device_memory_interface::space_config_vector isbc202_device::memory_space_config() const +{ + return space_config_vector{ + std::make_pair(AS_PROGRAM , &m_program_config) + }; +} + +std::unique_ptr<util::disasm_interface> isbc202_device::create_disassembler() +{ + return std::make_unique<isbc202_disassembler>(); +} + +void isbc202_device::state_import(const device_state_entry &entry) +{ + switch (entry.index()) { + case i3002_device::REG_R0: + case i3002_device::REG_R1: + case i3002_device::REG_R2: + case i3002_device::REG_R3: + case i3002_device::REG_R4: + case i3002_device::REG_R5: + case i3002_device::REG_R6: + case i3002_device::REG_R7: + case i3002_device::REG_R8: + case i3002_device::REG_R9: + case i3002_device::REG_T: + case i3002_device::REG_AC: + case i3002_device::REG_MAR: + m_cpes[ 0 ]->get_reg(entry.index()) = m_regs[ entry.index() ] & i3002_device::WORD_MASK; + m_cpes[ 1 ]->get_reg(entry.index()) = (m_regs[ entry.index() ] >> 2) & i3002_device::WORD_MASK; + m_cpes[ 2 ]->get_reg(entry.index()) = (m_regs[ entry.index() ] >> 4) & i3002_device::WORD_MASK; + m_cpes[ 3 ]->get_reg(entry.index()) = (m_regs[ entry.index() ] >> 6) & i3002_device::WORD_MASK; + break; + + default: + break; + } +} + +void isbc202_device::state_export(const device_state_entry &entry) +{ + switch (entry.index()) { + case STATE_GENFLAGS: + m_flags = 0; + if (m_inputs[ IN_SEL_CO ]) { + BIT_SET(m_flags , 0); + } + if (m_mcu->fo_r()) { + BIT_SET(m_flags , 1); + } + if (m_mcu->carry_r()) { + BIT_SET(m_flags , 2); + } + if (m_mcu->zero_r()) { + BIT_SET(m_flags , 3); + } + break; + + case i3002_device::REG_R0: + case i3002_device::REG_R1: + case i3002_device::REG_R2: + case i3002_device::REG_R3: + case i3002_device::REG_R4: + case i3002_device::REG_R5: + case i3002_device::REG_R6: + case i3002_device::REG_R7: + case i3002_device::REG_R8: + case i3002_device::REG_R9: + case i3002_device::REG_T: + case i3002_device::REG_AC: + case i3002_device::REG_MAR: + m_regs[ entry.index() ] = m_cpes[ 3 ]->get_reg(entry.index()); + m_regs[ entry.index() ] <<= 2; + m_regs[ entry.index() ] |= m_cpes[ 2 ]->get_reg(entry.index()); + m_regs[ entry.index() ] <<= 2; + m_regs[ entry.index() ] |= m_cpes[ 1 ]->get_reg(entry.index()); + m_regs[ entry.index() ] <<= 2; + m_regs[ entry.index() ] |= m_cpes[ 0 ]->get_reg(entry.index()); + break; + + default: + break; + } +} + +void isbc202_device::state_string_export(const device_state_entry &entry, std::string &str) const +{ + switch (entry.index()) { + case STATE_GENFLAGS: + str = string_format("%c %c %s %s" , + BIT(m_flags , 3) ? 'Z' : '-' , + BIT(m_flags , 2) ? 'C' : '-' , + BIT(m_flags , 1) ? "FO" : "--" , + BIT(m_flags , 0) ? "FI" : "--"); + break; + + default: + break; + } +} + +void isbc202_device::set_output() +{ + switch (m_out_sel) { + case 0: + // Bit What + // 6..3 - + // 2 Head load + // 1 Start timeout + // 0 Step pulse + if (BIT(m_mask , 1)) { + m_inputs[ IN_SEL_TIMEOUT ] = false; + m_timeout_timer->adjust(attotime::from_msec(TIMEOUT_MS)); + } + if (BIT(m_mask , 0) && (!m_direction || m_current_drive->trk00_r())) { + LOG_DR("Step %s\n" , m_direction ? "OUT" : "IN"); + m_current_drive->dir_w(m_direction); + m_current_drive->stp_w(0); + m_current_drive->stp_w(1); + } + break; + + case 1: + // Bit What + // 6 Reset data overrun + // 5 Set XACK + // 4 Set write inhibit + // 3 Clear write inhibit + // 2 - + // 1 Reset RDY latches (0) + // 0 - + if (BIT(m_mask , 5)) { + if (m_cpu != nullptr) { + // Release CPU from wait state + LOG_BUS("CPU out of wait state\n"); + m_cpu->trigger(1); + if (!m_cpu_rd) { + m_cpu = nullptr; + } + // Ensure the MCU executes a few instruction before the CPU + machine().scheduler().boost_interleave(attotime::from_usec(1) , attotime::from_usec(5)); + } else { + LOG("No CPU to wake up?\n"); + } + m_inputs[ IN_SEL_START ] = false; + } + if (BIT(m_mask , 4)) { + m_wrt_inh = true; + } + if (BIT(m_mask , 3)) { + m_wrt_inh = false; + } + if (!BIT(m_mask , 1)) { + m_ready_ff = 0xf; + } + break; + + case 2: + // Bit What + // 6..2 - + // 1..0 s1:s0 for PX input selection + m_px_s1s0 = m_mask & 3; + break; + + case 3: + // Bit What + // 6 Z2 + // 5 SR OUT (0 = DATA, 1 = CRC) + // 4 Write gate (0) + // 3 - + // 2 Z1 + // 1..0 - + m_crc_out = BIT(m_mask , 5); + set_rd_wr(m_reading, !BIT(m_mask , 4)); + break; + + case 4: + // Bit What + // 6 Stepping direction (1 = out, 0 = in) + // 5 CRC enable (0) + // 4..0 - + m_direction = BIT(m_mask , 6); + m_crc_enabled = !BIT(m_mask , 5); + break; + + case 5: + // Bit What + // 6..5 - + // 4 Mem write (0) + // 3 GATE LOWER (0) + // 2 - + // 1 INOP RESET (0) + // 0 AMWRT + m_mem_wrt = !BIT(m_mask , 4); + m_gate_lower = BIT(m_mask , 3); + m_amwrt = BIT(m_mask , 0); + break; + + case 6: + // Bit What + // 6 Latch data bus into A24/A25 + // 5 Set INT FF + // 4 Set Track > 43 + // 3 Latch D into A[7..0] + // 2 Latch D into D[7..0] + // 1 Latch D into D[F..8] + // 0 Set XFER REQ + if (BIT(m_mask , 6)) { + m_data_low_in = m_cpu_data; + } + if (BIT(m_mask , 5)) { + m_irq = true; + } + if (BIT(m_mask , 3)) { + m_addr_low_out = dbus_r(); + } + if (BIT(m_mask , 2)) { + m_data_low_out = dbus_r(); + } + if (BIT(m_mask , 0)) { + if (m_mem_wrt) { + if (!m_wrt_inh) { + // CPU memory write + uint16_t addr = m_addr_low_out | ((uint16_t)abus_r() << 8); + if (m_mem_space) { + LOG_BUS("MEM W %04x=%02x\n" , addr , m_data_low_out); + m_mem_space->write_byte(addr , m_data_low_out); + } else { + LOG("CPU AS not set!\n"); + } + } + } else { + // CPU memory read + uint16_t addr = m_addr_low_out | ((uint16_t)abus_r() << 8); + if (m_mem_space) { + m_data_low_in = m_mem_space->read_byte(addr); + LOG_BUS("MEM R %04x=%02x\n" , addr , m_data_low_in); + } else { + LOG("CPU AS not set!\n"); + } + } + } + break; + + case 7: + // Bit What + // 6 Latch OP & US + // 5 Reset INDEX + // 4 Set Track <= 43 + // 3 Clear START/STOP + // 2 Unload head + // 1 Set RESET READ + // 0 Clear RESET READ + if (BIT(m_mask , 6)) { + m_op_us = dbus_r() & 0x3f; + m_current_drive = m_drives[ selected_drive() ]->get_device(); + } + if (BIT(m_mask , 5)) { + m_inputs[ IN_SEL_INDEX ] = false; + } + if (BIT(m_mask , 3)) { + m_cmd = 0; + LOG_BUS("CLR ST\n"); + // TODO: more + } + if (BIT(m_mask , 1)) { + set_rd_wr(false, m_writing); + } + if (BIT(m_mask , 0)) { + set_rd_wr(true, m_writing); + } + break; + } +} + +unsigned isbc202_device::selected_drive() const +{ + return (m_op_us >> 4) & 3; +} + +unsigned isbc202_device::drive_idx(floppy_image_device *drive) +{ + for (unsigned i = 0; i < 4; ++i) { + if (drive == m_drives[ i ]->get_device()) { + return i; + } + } + LOG("Unknown drive!\n"); + return 0; +} + +void isbc202_device::floppy_ready_cb(floppy_image_device *floppy , int state) +{ + unsigned idx = drive_idx(floppy); + + uint8_t old_state = m_ready_in; + + if (!state) { + BIT_SET(m_ready_in , idx); + } else { + BIT_CLR(m_ready_in , idx); + } + + uint8_t gone_not_ready = ~m_ready_in & old_state; + + BIT_CLR(gone_not_ready, selected_drive()); + + m_ready_ff &= ~gone_not_ready; +} + +void isbc202_device::floppy_index_cb(floppy_image_device *floppy , int state) +{ + if (state && floppy == m_current_drive) { + LOG_DR("Index @%.6f\n" , machine().time().as_double()); + m_inputs[ IN_SEL_INDEX ] = true; + } +} + +uint8_t isbc202_device::dbus_r() const +{ + return m_cpes[ 0 ]->dbus_r() | + (m_cpes[ 1 ]->dbus_r() << 2) | + (m_cpes[ 2 ]->dbus_r() << 4) | + (m_cpes[ 3 ]->dbus_r() << 6); +} + +uint8_t isbc202_device::mbus_r() const +{ + return m_gate_lower ? 0 : m_data_low_in; +} + +uint8_t isbc202_device::ibus_r() +{ + if (!m_ibus_cached) { + m_ibus_cached = true; + m_ibus = 0xff; + if (BIT(m_slk , 0) && BIT(m_mask , 7)) { + if (BIT(m_mask , 5)) { + // Bit What + // 7 Drive 3/1 ready + // 6 Drive 2/0 ready + // 5 Track 0 + // 4 STOP + // 3..0 1 + m_ibus = 0x0f; + uint8_t ready = m_ready_in & m_ready_ff; + if (m_gate_lower) { + ready >>= 2; + } + if (BIT(ready , 1)) { + BIT_SET(m_ibus, 7); + } + if (BIT(ready , 0)) { + BIT_SET(m_ibus, 6); + } + if (!m_current_drive->trk00_r()) { + BIT_SET(m_ibus, 5); + } + // TODO: STOP bit + } else if (BIT(m_mask , 6)) { + // Bit What + // 7 Selected drive not ready + // 6 Write fault + // 5 Write protection + // 4 Data overrun + // 3..0 1 + m_ibus = 0x0f; + if (!BIT(m_ready_in , selected_drive())) { + BIT_SET(m_ibus, 7); + } + if (m_current_drive->wpt_r()) { + BIT_SET(m_ibus, 5); + } + } else if (BIT(m_mask , 3)) { + // Read clock SR + m_ibus = aligned_rd_data(m_clock_sr); + } else if (BIT(m_mask , 4)) { + // Read data SR + m_ibus = aligned_rd_data(m_data_sr); + } + } + } + + return m_ibus; +} + +uint8_t isbc202_device::abus_r() const +{ + return m_cpes[ 0 ]->abus_r() | + (m_cpes[ 1 ]->abus_r() << 2) | + (m_cpes[ 2 ]->abus_r() << 4) | + (m_cpes[ 3 ]->abus_r() << 6); +} + +void isbc202_device::set_start(uint8_t off , bool read) +{ + m_cmd = off; + m_inputs[ IN_SEL_START ] = true; + // Put CPU in wait state + m_cpu->spin_until_trigger(1); + m_cpu_rd = read; + LOG_BUS("CPU in wait state (rd=%d)\n" , read); + if (read) { + // If CPU is suspended when reading, rewind PC so that the + // "IN" instruction is repeated when CPU is released + m_cpu->set_pc(m_cpu->pc() - 2); + } +} + +void isbc202_device::set_rd_wr(bool new_rd , bool new_wr) +{ + if (!m_reading && new_rd) { + // Start reading + LOG_RD("Start RD @%.6f\n" , machine().time().as_double()); + m_pll.set_clock(attotime::from_usec(HALF_BIT_CELL_US)); + m_pll.read_reset(machine().time()); + + // Search for next SYNC (16x 1 and a 0) + m_byte_timer->reset(); + m_f_timer->reset(); + unsigned cnt_trans = 0; + attotime rot_period = attotime::from_hz(6); + while ((m_pll.ctime - machine().time()) < rot_period) { + attotime edge = m_current_drive->get_next_transition(m_pll.ctime); + if (edge.is_never()) { + break; + } + attotime tm; + bool bit = m_pll.feed_read_data(tm , edge , attotime::never); + if (cnt_trans < 32) { + if (!(BIT(cnt_trans , 0) ^ bit)) { + cnt_trans++; + } else { + cnt_trans = 0; + } + } else if (cnt_trans == 32) { + if (!bit) { + cnt_trans++; + } else { + cnt_trans = 0; + } + } else { + if (!bit) { + LOG_RD("SYNC found @%.6f\n" , m_pll.ctime.as_double()); + // GOT SYNC! + if (m_crc_enabled) { + // CRC shouldn't be enabled here or register won't get cleared + LOG("Huh? CRC enabled during SYNC scan?\n"); + } + m_crc = 0; + // Load the "0" bit into data/clock SR + m_data_sr = 0; + m_clock_sr = 0; + // Read 7 more bits to make a full byte + rd_bits(7); + // Timer to go off at end of 8th bit of AM byte (when F signal goes high) + m_byte_timer->adjust(m_pll.ctime - machine().time()); + break; + } else { + cnt_trans = 32; + } + } + } + } else if (m_reading && !new_rd) { + // Stop reading + LOG_RD("Stop RD\n"); + m_byte_timer->reset(); + m_f_timer->reset(); + m_inputs[ IN_SEL_F ] = false; + } + m_reading = new_rd; + + if (!m_writing && new_wr) { + // Start writing + LOG_WR("Start WR\n"); + m_pll.set_clock(attotime::from_usec(HALF_BIT_CELL_US)); + m_pll.start_writing(machine().time()); + m_pll.ctime = machine().time(); + m_last_data_bit = false; + m_byte_timer->adjust(attotime::from_usec(HALF_BIT_CELL_US * 14)); + } else if (m_writing && !new_wr) { + // Stop writing + LOG_WR("Stop WR\n"); + m_pll.stop_writing(m_current_drive , machine().time()); + m_byte_timer->reset(); + m_f_timer->reset(); + m_inputs[ IN_SEL_F ] = false; + } + m_writing = new_wr; +} + +uint8_t isbc202_device::aligned_rd_data(uint16_t sr) +{ + attotime tmp{ machine().time() - m_last_f_time }; + + // Compute how many bit cells have gone by since the last time F went high + unsigned bits = tmp.as_ticks(BIT_FREQUENCY); + if (bits) { + LOG_RD("Aligning by %u bits\n" , bits); + sr <<= bits; + } + return (uint8_t)(sr >> 8); +} + +void isbc202_device::rd_bits(unsigned n) +{ + while (n--) { + attotime edge = m_current_drive->get_next_transition(m_pll.ctime); + if (edge.is_never()) { + break; + } + attotime tm; + bool clock_bit = m_pll.feed_read_data(tm , edge , attotime::never); + edge = m_current_drive->get_next_transition(m_pll.ctime); + if (edge.is_never()) { + break; + } + bool data_bit = m_pll.feed_read_data(tm , edge , attotime::never); + + m_clock_sr = (m_clock_sr << 1) | clock_bit; + bool crc_bit = BIT(m_data_sr , 15); + m_data_sr = (m_data_sr << 1) | data_bit; + update_crc(crc_bit); + } + LOG_RD("CLK %04x DT %04x CRC %04x\n" , m_clock_sr , m_data_sr , m_crc); +} + +void isbc202_device::write_byte() +{ + LOG_WR("WR DT %02x CRC %04x CE %d CO %d AW %d @%.6f\n" , m_data_sr & 0xff , m_crc , m_crc_enabled , m_crc_out , m_dlyd_amwrt , machine().time().as_double()); + + for (unsigned i = 0; i < 8; i++) { + bool sr_bit = BIT(m_data_sr , 7); + bool crc_bit = update_crc(sr_bit); + bool data_bit = m_crc_out ? crc_bit : sr_bit; + bool clock_bit = m_clock_gate && !data_bit && !m_last_data_bit; + if (i <= 3 && m_dlyd_amwrt) { + m_clock_gate = true; + } else { + m_clock_gate = !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); + m_data_sr <<= 1; + m_last_data_bit = data_bit; + } + m_pll.commit(m_current_drive , machine().time()); + m_pll.ctime = machine().time(); +} + +bool isbc202_device::update_crc(bool bit) +{ + bool out = BIT(m_crc , 15); + + if (m_crc_enabled && (out ^ bit)) { + m_crc = (m_crc << 1) ^ CRC_POLY; + } else { + m_crc <<= 1; + } + + return out; +} diff --git a/src/devices/bus/multibus/isbc202.h b/src/devices/bus/multibus/isbc202.h new file mode 100644 index 00000000000..b72e860b9fc --- /dev/null +++ b/src/devices/bus/multibus/isbc202.h @@ -0,0 +1,160 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + isbc202.h + + Intel iSBC-202 SSDD 8" floppy disk controller + +*********************************************************************/ + +#ifndef MAME_BUS_MULTIBUS_ISBC202_H +#define MAME_BUS_MULTIBUS_ISBC202_H + +#pragma once + +#include "multibus.h" +#include "machine/i3001.h" +#include "machine/i3002.h" +#include "machine/fdc_pll.h" +#include "imagedev/floppy.h" + +class isbc202_device : public cpu_device, + public device_multibus_interface +{ +public: + // Construction/destruction + isbc202_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + virtual ~isbc202_device(); + + // device_multibus_interface overrides + virtual void install_io_rw(address_space& space) override; + virtual void install_mem_rw(address_space& space) override; + + // Access to I/O space by CPU + DECLARE_READ8_MEMBER(io_r); + DECLARE_WRITE8_MEMBER(io_w); + + DECLARE_WRITE_LINE_MEMBER(co_w); + + DECLARE_READ8_MEMBER(px_r); + +protected: + // device_t overrides + 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; + virtual const tiny_rom_entry *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; + // device_execute_interface overrides + virtual uint32_t execute_min_cycles() const noexcept override { return 1; } + virtual void execute_run() override; + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + // device_disasm_interface overrides + virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; + // device_state_interface overrides + virtual void state_import(const device_state_entry &entry) override; + virtual void state_export(const device_state_entry &entry) override; + virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; + +private: + required_device<i3001_device> m_mcu; + required_device_array<i3002_device , 4> m_cpes; + required_device_array<floppy_connector , 4> m_drives; + + address_space_config m_program_config; + memory_access_cache<2 , -2 , ENDIANNESS_BIG> *m_cache; + + address_space *m_mem_space; + + int m_icount; + uint8_t m_flags; + uint8_t m_regs[ i3002_device::REG_COUNT ]; + uint16_t m_microcode_addr; + uint32_t m_code_word; + uint8_t m_ac; + uint8_t m_fc; + bool m_fc32; + bool m_fc10; + enum { // m_in_sel Input on AC[0] + IN_SEL_AC0, // 0 AC[0] + IN_SEL_CO, // 1 CO + IN_SEL_START, // 2 START + IN_SEL_F, // 3 F + IN_SEL_AZ, // 4 AZ + IN_SEL_INDEX, // 5 INDEX + IN_SEL_XFERQ, // 6 XFERREQ + IN_SEL_TIMEOUT,//7 TIMEOUT + IN_SEL_COUNT + }; + uint8_t m_in_sel; + uint8_t m_out_sel; + uint8_t m_slk; + uint8_t m_mask; + uint8_t m_kbus; + bool m_inputs[ IN_SEL_COUNT ]; + // IC identifiers have this form: x-Ayy + // where x is either C (for channel board) or I (for interface board) + // and yy is the numeric IC ID. + uint8_t m_op_us; // C-A44 + floppy_image_device *m_current_drive; + uint8_t m_px_s1s0; // C-A16-11 & C-A16-13 + uint8_t m_cmd; // C-A8 + cpu_device *m_cpu; // When != nullptr: CPU is suspended in wait state + bool m_cpu_rd; + uint8_t m_ready_in; + uint8_t m_ready_ff; // I-A44 & I-A43 + bool m_gate_lower; // I-A58-9 + bool m_irq; // C-A37-9 + uint8_t m_data_low_out; // C-A43 + uint8_t m_data_low_in; // C-A25 + uint8_t m_cpu_data; + uint8_t m_addr_low_out; // C-A41 + bool m_mem_wrt; // I-A58-4 + bool m_wrt_inh; // I-A46-13 + bool m_direction; // I-A58-11 + bool m_ibus_cached; + uint8_t m_ibus; + uint16_t m_crc; // I-A62 + bool m_crc_enabled; // I-A58-13 + bool m_crc_out; // C-A16-2 + bool m_reading; // I-A46-9 + bool m_writing; // C-A16-4 + uint16_t m_data_sr; // C-A36 & C-A29 (MSB), next byte in LSB + bool m_last_data_bit; // I-A48-6 + uint16_t m_clock_sr; // C-A34 & C-A27 (MSB), next byte in LSB + attotime m_last_f_time; + bool m_clock_gate; // I-A10-8 + bool m_amwrt; // I-A58-6 + bool m_dlyd_amwrt; + + // PLL + fdc_pll_t m_pll; + + // Timers + emu_timer *m_timeout_timer; + emu_timer *m_byte_timer; + emu_timer *m_f_timer; + + void set_output(); + unsigned selected_drive() const; + unsigned drive_idx(floppy_image_device *drive); + void floppy_ready_cb(floppy_image_device *floppy , int state); + void floppy_index_cb(floppy_image_device *floppy , int state); + uint8_t dbus_r() const; + uint8_t mbus_r() const; + uint8_t abus_r() const; + uint8_t ibus_r(); + void set_start(uint8_t off , bool read); + void set_rd_wr(bool new_rd , bool new_wr); + uint8_t aligned_rd_data(uint16_t sr); + void rd_bits(unsigned n); + void write_byte(); + bool update_crc(bool bit); +}; + +// device type declaration +DECLARE_DEVICE_TYPE(ISBC202, isbc202_device) + +#endif /* MAME_BUS_MULTIBUS_ISBC202_H */ diff --git a/src/devices/bus/multibus/multibus.cpp b/src/devices/bus/multibus/multibus.cpp new file mode 100644 index 00000000000..d962e01070b --- /dev/null +++ b/src/devices/bus/multibus/multibus.cpp @@ -0,0 +1,59 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + multibus.cpp + + Intel Multibus + +*********************************************************************/ + +#include "emu.h" +#include "multibus.h" +#include "isbc202.h" + +// device type definition +DEFINE_DEVICE_TYPE(MULTIBUS_SLOT, multibus_slot_device, "Multibus slot", "Intel Multibus slot") + +multibus_slot_device::multibus_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig , MULTIBUS_SLOT , tag , owner , clock) + , device_single_card_slot_interface<device_multibus_interface>(mconfig , *this) +{ + option_reset(); + option_add("isbc202" , ISBC202); + set_default_option(nullptr); + set_fixed(false); +} + +multibus_slot_device::~multibus_slot_device() +{ +} + +void multibus_slot_device::install_io_rw(address_space& space) +{ + device_multibus_interface *card = get_card_device(); + if (card) { + card->install_io_rw(space); + } +} + +void multibus_slot_device::install_mem_rw(address_space& space) +{ + device_multibus_interface *card = get_card_device(); + if (card) { + card->install_mem_rw(space); + } +} + +void multibus_slot_device::device_start() +{ +} + +device_multibus_interface::device_multibus_interface(const machine_config &mconfig , device_t &device) + : device_interface(device , "multibus") +{ +} + +device_multibus_interface::~device_multibus_interface() +{ +} diff --git a/src/devices/bus/multibus/multibus.h b/src/devices/bus/multibus/multibus.h new file mode 100644 index 00000000000..f45a486a516 --- /dev/null +++ b/src/devices/bus/multibus/multibus.h @@ -0,0 +1,54 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + multibus.h + + Intel Multibus + +*********************************************************************/ + +#ifndef MAME_BUS_MULTIBUS_MULTIBUS_H +#define MAME_BUS_MULTIBUS_MULTIBUS_H + +#pragma once + +class device_multibus_interface; + +class multibus_slot_device : public device_t, + public device_single_card_slot_interface<device_multibus_interface> +{ +public: + multibus_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + virtual ~multibus_slot_device(); + + static constexpr unsigned BUS_CLOCK = 9830400; + + // Install r/w functions in I/O space + void install_io_rw(address_space& space); + + // Set memory space + void install_mem_rw(address_space& space); + +protected: + virtual void device_start() override; +}; + +class device_multibus_interface : public device_interface +{ +public: + // Install r/w functions in I/O space + virtual void install_io_rw(address_space& space) = 0; + + // Set CPU memory space + virtual void install_mem_rw(address_space& space) = 0; + +protected: + device_multibus_interface(const machine_config &mconfig , device_t &device); + virtual ~device_multibus_interface(); +}; + +// device type declaration +DECLARE_DEVICE_TYPE(MULTIBUS_SLOT, multibus_slot_device) + +#endif /* MAME_BUS_MULTIBUS_MULTIBUS_H */ diff --git a/src/devices/machine/i3001.cpp b/src/devices/machine/i3001.cpp new file mode 100644 index 00000000000..decca559d56 --- /dev/null +++ b/src/devices/machine/i3001.cpp @@ -0,0 +1,137 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************** + + i3001.cpp + + Intel 3001 Microprogram Control Unit + +**********************************************************************/ + +#include "emu.h" +#include "i3001.h" + +// Device type definition +DEFINE_DEVICE_TYPE(I3001, i3001_device, "i3001", "Intel i3001 MCU") + +i3001_device::i3001_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock) + : device_t(mconfig , I3001 , tag , owner , clock) + , m_fo_handler(*this) + , m_px_handler(*this) + , m_sx_handler(*this) +{ +} + +void i3001_device::fc_w(uint8_t fc) +{ + m_fc = fc; + + switch (fc & 0b1100) { + case 0: + // FF0 + m_fo = false; + break; + + case 0b0100: + // FFC + m_fo = m_carry; + break; + + case 0b1000: + // FFZ + m_fo = m_zero; + break; + + case 0b1100: + // FF1 + m_fo = true; + break; + } + m_fo_handler(m_fo); +} + +WRITE_LINE_MEMBER(i3001_device::clk_w) +{ + update(); +} + +void i3001_device::device_start() +{ + m_fo_handler.resolve_safe(); + m_px_handler.resolve_safe(0); + m_sx_handler.resolve_safe(0); + + save_item(NAME(m_addr)); + save_item(NAME(m_pr)); + save_item(NAME(m_ac)); + save_item(NAME(m_fc)); + save_item(NAME(m_fi)); + save_item(NAME(m_carry)); + save_item(NAME(m_zero)); + save_item(NAME(m_flag)); +} + +void i3001_device::update() +{ + m_flag = m_fi; + + // Decode FC10 + switch (m_fc & 0b11) { + case 0: + // SCZ + m_carry = m_zero = m_flag; + break; + + case 0b01: + // STZ + m_zero = m_flag; + break; + + case 0b10: + // STC + m_carry = m_flag; + break; + + default: + // HCZ + break; + } + + // Decode AC + if ((m_ac & 0b1100000) == 0b0000000) { + // JCC + m_addr = pack_row_col(m_ac & 0b11111 , get_col(m_addr)); + } else if ((m_ac & 0b1110000) == 0b0100000) { + // JZR + m_addr = pack_row_col(0 , m_ac & 0b1111); + } else if ((m_ac & 0b1110000) == 0b0110000) { + // JCR + m_addr = pack_row_col(get_row(m_addr) , m_ac & 0b1111); + } else if ((m_ac & 0b1111000) == 0b1110000) { + // JCE + m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , get_col(m_addr)); + } else if ((m_ac & 0b1110000) == 0b1000000) { + // JFL + m_addr = pack_row_col((get_row(m_addr) & 0b10000) | (m_ac & 0b01111) , (get_col(m_addr) & 0b1000) | 0b0010 | m_flag); + } else if ((m_ac & 0b1111000) == 0b1010000) { + // JCF + m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , (get_col(m_addr) & 0b1000) | 0b0010 | m_carry); + } else if ((m_ac & 0b1111000) == 0b1011000) { + // JZF + m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , (get_col(m_addr) & 0b1000) | 0b0010 | m_zero); + } else if ((m_ac & 0b1111000) == 0b1100000) { + // JPR + m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , m_pr); + } else if ((m_ac & 0b1111000) == 0b1101000) { + // JLL + m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , 0b0100 | (m_pr >> 2)); + } else if ((m_ac & 0b1111100) == 0b1111100) { + // JRL + m_addr = pack_row_col((get_row(m_addr) & 0b11000) | 0b00100 | (m_ac & 0b00011) , 0b1100 | (m_pr & 0b0011)); + } else { + // JPX + uint8_t px = m_px_handler() & 0b1111; + m_pr = m_sx_handler() & 0b1111; + m_addr = pack_row_col((get_row(m_addr) & 0b11100) | (m_ac & 0b00011) , px); + } +} diff --git a/src/devices/machine/i3001.h b/src/devices/machine/i3001.h new file mode 100644 index 00000000000..11231456f5b --- /dev/null +++ b/src/devices/machine/i3001.h @@ -0,0 +1,106 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************** + + i3001.h + + Intel 3001 Microprogram Control Unit + +********************************************************************** + _____ _____ + PX4/ 1|* \_/ |40 VCC + PX7/ 2| |39 AC0 + PX6/ 3| |38 AC1 + PX5/ 4| |37 AC5 + SX3/ 5| |36 LD + SX2/ 6| |35 ERA + PR2 7| |34 MA8 + SX1/ 8| |33 MA7 + PR1 9| DIP40 |32 MA6 + SX0/ 10| i3001 |31 MA5 + PR0 11| |30 MA4 + FC3 12| |29 MA0 + FC2 13| |28 MA3 + FO/ 14| |27 MA2 + FC0 15| |26 MA1 + FC1 16| |25 EN + FI/ 17| |24 AC6 + ISE 18| |23 AC4 + CLK 19| |22 AC3 + GND 20|_____________|21 AC2 + +**********************************************************************/ + +#ifndef MAME_MACHINE_I3001_H +#define MAME_MACHINE_I3001_H + +#pragma once + +class i3001_device : public device_t +{ +public: + i3001_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock = 0); + + // Mask of valid bits in address + static constexpr uint16_t ADDR_MASK = ((1U << 9) - 1); + + // Read current microprogram address (9 bits) + uint16_t addr_r() const { return m_addr; } + + // Set Address Control code (7 bits) + void ac_w(uint16_t ac) { m_ac = ac; } + + // Set Flag Control code (4 bits) + void fc_w(uint8_t fc); + + // Write Flag Input + DECLARE_WRITE_LINE_MEMBER(fi_w) { m_fi = state; } + + // Read Flag Output + DECLARE_READ_LINE_MEMBER(fo_r) { return m_fo; } + + // Read carry/zero flags + DECLARE_READ_LINE_MEMBER(carry_r) { return m_carry; } + DECLARE_READ_LINE_MEMBER(zero_r) { return m_zero; } + + // Output callbacks + auto fo_w() { return m_fo_handler.bind(); } + + // Input callbacks + auto px_r() { return m_px_handler.bind(); } + auto sx_r() { return m_sx_handler.bind(); } + + // Load address (in real hw address is loaded through PX/SX buses) + void addr_w(uint16_t addr) { m_addr = addr & ADDR_MASK; } + + // Clock pulse + DECLARE_WRITE_LINE_MEMBER(clk_w); + +protected: + virtual void device_start() override; + +private: + devcb_write_line m_fo_handler; + devcb_read8 m_px_handler; + devcb_read8 m_sx_handler; + + uint16_t m_addr; + uint8_t m_pr; + uint8_t m_ac; + uint8_t m_fc; + bool m_fi; + bool m_fo; + bool m_carry; + bool m_zero; + bool m_flag; + + void update(); + static uint8_t get_row(uint16_t addr) { return (addr >> 4); } + static uint8_t get_col(uint16_t addr) { return addr & 0b1111; } + static uint16_t pack_row_col(uint8_t row , uint8_t col) { return ((uint16_t)row << 4) | col; } +}; + +// device type definition +DECLARE_DEVICE_TYPE(I3001, i3001_device) + +#endif /* MAME_MACHINE_I3001_H */ diff --git a/src/devices/machine/i3002.cpp b/src/devices/machine/i3002.cpp new file mode 100644 index 00000000000..0abfff3eb45 --- /dev/null +++ b/src/devices/machine/i3002.cpp @@ -0,0 +1,428 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************** + + i3002.cpp + + Intel 3002 Central Processing Element + +**********************************************************************/ + +#include "emu.h" +#include "i3002.h" + +// Device type definition +DEFINE_DEVICE_TYPE(I3002, i3002_device, "i3002", "Intel i3002 CPE") + +// 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); + } +} + +// Table to decode bits 0..3 of FC into RG and register index +struct fc_fields { + uint8_t rg; // Register group [0..2] ("I" , "II" , "III" in Intel docs) + unsigned reg; // Register index +}; + +constexpr std::array<struct fc_fields , 16> fc_field_tab = + {{ + { 0 , i3002_device::REG_R0 }, // 0000 + { 0 , i3002_device::REG_R1 }, // 0001 + { 0 , i3002_device::REG_R2 }, // 0010 + { 0 , i3002_device::REG_R3 }, // 0011 + { 0 , i3002_device::REG_R4 }, // 0100 + { 0 , i3002_device::REG_R5 }, // 0101 + { 0 , i3002_device::REG_R6 }, // 0110 + { 0 , i3002_device::REG_R7 }, // 0111 + { 0 , i3002_device::REG_R8 }, // 1000 + { 0 , i3002_device::REG_R9 }, // 1001 + { 1 , i3002_device::REG_T }, // 1010 + { 1 , i3002_device::REG_AC }, // 1011 + { 0 , i3002_device::REG_T }, // 1100 + { 0 , i3002_device::REG_AC }, // 1101 + { 2 , i3002_device::REG_T }, // 1110 + { 2 , i3002_device::REG_AC } // 1111 + }}; + +// Register names +static const std::array<const char* , i3002_device::REG_COUNT> reg_names = + {{ + "R0", + "R1", + "R2", + "R3", + "R4", + "R5", + "R6", + "R7", + "R8", + "R9", + "T", + "AC", + "MAR" + }}; + +i3002_device::i3002_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock) + : device_t(mconfig , I3002 , tag , owner , clock) + , m_co_handler(*this) + , m_ro_handler(*this) + , m_ibus_handler(*this) + , m_mbus_handler(*this) +{ +} + +const char *i3002_device::reg_name(unsigned reg_idx) +{ + return reg_names[ reg_idx ]; +} + +void i3002_device::decode_fc(uint8_t fc , uint8_t& fg , uint8_t& rg , unsigned& reg) +{ + // Function group + fg = (fc >> 4) & 7; + + uint8_t lsn = fc & 0xf; + + // Register & register group + rg = fc_field_tab[ lsn ].rg; + reg = fc_field_tab[ lsn ].reg; +} + +void i3002_device::fc_kbus_w(uint8_t fc , uint8_t k) +{ + m_fc = fc & 0x7f; + m_kbus = k & WORD_MASK; +} + +WRITE_LINE_MEMBER(i3002_device::clk_w) +{ + if (state) { + update(); + } +} + +bool i3002_device::update_ro() +{ + // Update RO output if doing a right-shift only + uint8_t fg; + uint8_t rg; + unsigned reg; + + decode_fc(m_fc , fg , rg , reg); + + if (fg == 0 && rg == 2) { + uint8_t ik = m_ibus_handler() & m_kbus; + uint8_t at = m_reg[ reg ]; + set_ro(BIT(at , 0) && !BIT(ik , 0)); + return true; + } else { + return false; + } +} + +void i3002_device::device_start() +{ + m_co_handler.resolve_safe(); + m_ro_handler.resolve_safe(); + m_ibus_handler.resolve_safe(0); + m_mbus_handler.resolve_safe(0); + + save_item(NAME(m_reg)); + save_item(NAME(m_fc)); + save_item(NAME(m_kbus)); + save_item(NAME(m_ci)); + save_item(NAME(m_li)); + save_item(NAME(m_co)); + save_item(NAME(m_ro)); +} + +void i3002_device::update() +{ + uint8_t fg; + uint8_t rg; + unsigned reg; + + decode_fc(m_fc , fg , rg , reg); + + switch (fg) { + case 0: + switch (rg) { + case 0: + // FC 0 RG I + { + uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m_reg[ reg ] + m_ci; + m_reg[ REG_AC ] = m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + + case 1: + // FC 0 RG II + { + uint8_t m = m_mbus_handler() & WORD_MASK; + uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + + case 2: + // FC 0 RG III + { + // Who designed this mess??? + uint8_t ik = m_ibus_handler() & WORD_MASK & m_kbus; + uint8_t at = m_reg[ reg ]; + m_reg[ reg ] = 0; + set_ro(BIT(at , 0) && !BIT(ik , 0)); + if (m_li || (BIT(ik , 1) && BIT(at , 1))) { + BIT_SET(m_reg[ reg ] , 1); + } + if ((BIT(at , 0) && BIT(ik , 0)) || (BIT(at , 1) || BIT(ik , 1))) { + BIT_SET(m_reg[ reg ] , 0); + } + } + break; + } + break; + + case 1: + switch (rg) { + case 0: + // FC 1 RG I + { + m_reg[ REG_MAR ] = m_kbus | m_reg[ reg ]; + uint8_t tmp = m_reg[ reg ] + m_kbus + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + + case 1: + // FC 1 RG II + { + uint8_t m = m_mbus_handler() & WORD_MASK; + m_reg[ REG_MAR ] = m_kbus | m; + uint8_t tmp = m + m_kbus + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + + case 2: + // FC 1 RG III + { + uint8_t tmp = ((m_reg[ reg ] ^ WORD_MASK) | m_kbus) + (m_reg[ reg ] & m_kbus) + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + } + break; + + case 2: + switch (rg) { + case 0: + case 1: + // FC 2 RG I + // FC 2 RG II + { + uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + WORD_MASK + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + + case 2: + // FC 2 RG III + { + uint8_t i = m_ibus_handler() & WORD_MASK; + uint8_t tmp = (i & m_kbus) + WORD_MASK + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + } + break; + + case 3: + switch (rg) { + case 0: + // FC 3 RG I + { + uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m_reg[ reg ] + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + + case 1: + // FC 3 RG II (== FC 0 RG II) + { + uint8_t m = m_mbus_handler() & WORD_MASK; + uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + + case 2: + // FC 3 RG III + { + uint8_t i = m_ibus_handler() & WORD_MASK; + uint8_t tmp = (i & m_kbus) + m_reg[ reg ] + m_ci; + m_reg[ reg ] = tmp & WORD_MASK; + set_co(BIT(tmp , SLICE_SIZE)); + } + break; + } + break; + + case 4: + switch (rg) { + case 0: + // FC 4 RG I + { + uint8_t tmp = m_reg[ reg ] & m_reg[ REG_AC ] & m_kbus; + m_reg[ reg ] = tmp; + set_co(m_ci || tmp != 0); + } + break; + + case 1: + // FC 4 RG II + { + uint8_t m = m_mbus_handler() & WORD_MASK; + uint8_t tmp = m & m_reg[ REG_AC ] & m_kbus; + m_reg[ reg ] = tmp; + set_co(m_ci || tmp != 0); + } + break; + + case 2: + // FC 4 RG III + { + uint8_t i = m_ibus_handler() & WORD_MASK; + uint8_t tmp = i & m_reg[ reg ] & m_kbus; + m_reg[ reg ] = tmp; + set_co(m_ci || tmp != 0); + } + break; + } + break; + + case 5: + switch (rg) { + case 0: + case 2: + // FC 5 RG I + // FC 5 RG III + { + uint8_t tmp = m_reg[ reg ] & m_kbus; + m_reg[ reg ] = tmp; + set_co(m_ci || tmp != 0); + } + break; + + case 1: + // FC 5 RG II + { + uint8_t m = m_mbus_handler() & WORD_MASK; + uint8_t tmp = m & m_kbus; + m_reg[ reg ] = tmp; + set_co(m_ci || tmp != 0); + } + break; + } + break; + + case 6: + switch (rg) { + case 0: + // FC 6 RG I + { + uint8_t tmp = m_reg[ REG_AC ] & m_kbus; + m_reg[ reg ] |= tmp; + set_co(m_ci || tmp != 0); + } + break; + + case 1: + // FC 6 RG II + { + uint8_t m = m_mbus_handler() & WORD_MASK; + uint8_t tmp = m_reg[ REG_AC ] & m_kbus; + m_reg[ reg ] = m | tmp; + set_co(m_ci || tmp != 0); + } + break; + + case 2: + // FC 6 RG III + { + uint8_t ik = m_ibus_handler() & WORD_MASK & m_kbus; + m_reg[ reg ] |= ik; + set_co(m_ci || ik != 0); + } + break; + } + break; + + case 7: + switch (rg) { + case 0: + // FC 7 RG I + { + uint8_t tmp = m_reg[ REG_AC ] & m_kbus; + set_co(m_ci || (m_reg[ reg ] & tmp) != 0); + m_reg[ reg ] = m_reg[ reg ] ^ tmp ^ WORD_MASK; + } + break; + + case 1: + // FC 7 RG II + { + uint8_t m = m_mbus_handler() & WORD_MASK; + uint8_t tmp = m_reg[ REG_AC ] & m_kbus; + set_co(m_ci || (m & tmp) != 0); + m_reg[ reg ] = m ^ tmp ^ WORD_MASK; + } + break; + + case 2: + // FC 7 RG III + { + uint8_t ik = m_ibus_handler() & WORD_MASK & m_kbus; + set_co(m_ci || (m_reg[ reg ] & ik) != 0); + m_reg[ reg ] = m_reg[ reg ] ^ ik ^ WORD_MASK; + } + break; + } + break; + } +} + +void i3002_device::set_co(bool co) +{ + m_co = co; + m_co_handler(m_co); +} + +void i3002_device::set_ro(bool ro) +{ + m_ro = ro; + m_ro_handler(m_ro); +} diff --git a/src/devices/machine/i3002.h b/src/devices/machine/i3002.h new file mode 100644 index 00000000000..70932afe7a2 --- /dev/null +++ b/src/devices/machine/i3002.h @@ -0,0 +1,130 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************** + + i3002.h + + Intel 3002 Central Processing Element + +********************************************************************** + _____ _____ + I0/ 1|* \_/ |28 VCC + I1/ 2| |27 F2 + K0/ 3| |26 F1 + K1/ 4| |25 F0 + X 5| |24 F3 + Y 6| |23 ED/ + CO/ 7| |22 M0/ + RO/ 8| |21 M1/ + LI/ 9| DIP28 |20 D1/ + CI/ 10| i3002 |19 D0/ + EA/ 11| |18 CLK + A1/ 12| |17 F4 + A0/ 13| |16 F5 + GND 14|_____________|15 F6 + +**********************************************************************/ + +#ifndef MAME_MACHINE_I3002_H +#define MAME_MACHINE_I3002_H + +#pragma once + +class i3002_device : public device_t +{ +public: + i3002_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock = 0); + + // Width of bit-slice + static constexpr unsigned SLICE_SIZE = 2; + + // Mask of significant bits + static constexpr uint8_t WORD_MASK = ((1U << SLICE_SIZE) - 1); + + // Registers + enum : unsigned { + REG_R0, + REG_R1, + REG_R2, + REG_R3, + REG_R4, + REG_R5, + REG_R6, + REG_R7, + REG_R8, + REG_R9, + REG_T, + REG_AC, + REG_MAR, + REG_COUNT + }; + + static const char *reg_name(unsigned reg_idx); + + // Decode function code into function group, register group and register index + static void decode_fc(uint8_t fc , uint8_t& fg , uint8_t& rg , unsigned& reg); + + // Set function code (7 bits) and K-bus (2 bits) + void fc_kbus_w(uint8_t fc , uint8_t k); + + // Write Carry Input + DECLARE_WRITE_LINE_MEMBER(ci_w) { m_ci = state != 0; } + + // Write Left Input + DECLARE_WRITE_LINE_MEMBER(li_w) { m_li = state != 0; } + + // Read Carry Output + DECLARE_READ_LINE_MEMBER(co_r) const { return m_co; } + + // Read Right Output + DECLARE_READ_LINE_MEMBER(ro_r) const { return m_ro; } + + // Output callbacks + auto co_w() { return m_co_handler.bind(); } + auto ro_w() { return m_ro_handler.bind(); } + + // Input bus callbacks + auto ibus_r() { return m_ibus_handler.bind(); } + auto mbus_r() { return m_mbus_handler.bind(); } + + // Read output buses + uint8_t abus_r() const { return m_reg[ REG_MAR ]; } + uint8_t dbus_r() const { return m_reg[ REG_AC ]; } + + // Clock pulse + DECLARE_WRITE_LINE_MEMBER(clk_w); + + // Compute RO if FC is the code of right-shift op (and return true) + // Return false in all other cases + bool update_ro(); + + // Access to registers (debugging only: registers are not directly accessible in the real hw) + const uint8_t& get_reg(unsigned idx) const { return m_reg[ idx ]; } + uint8_t& get_reg(unsigned idx) { return m_reg[ idx ]; } + +protected: + virtual void device_start() override; + +private: + devcb_write_line m_co_handler; + devcb_write_line m_ro_handler; + devcb_read8 m_ibus_handler; + devcb_read8 m_mbus_handler; + + uint8_t m_reg[ REG_COUNT ]; + uint8_t m_fc; + uint8_t m_kbus; + bool m_ci; + bool m_li; + bool m_co; + bool m_ro; + + void update(); + void set_co(bool co); + void set_ro(bool ro); +}; + +// device type definition +DECLARE_DEVICE_TYPE(I3002, i3002_device) + +#endif /* MAME_MACHINE_I3002_H */ diff --git a/src/lib/formats/img_dsk.cpp b/src/lib/formats/img_dsk.cpp new file mode 100644 index 00000000000..4145d74a665 --- /dev/null +++ b/src/lib/formats/img_dsk.cpp @@ -0,0 +1,214 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + img_dsk.cpp + + "IMG" disk format + + This format is just a raw image of every sector on SSDD 8" floppy + disks as used in Intel MDS-II systems. + Files with this format have no header/trailer and are exactly + 512512 bytes in size (52 sectors, 1 head, 77 tracks, + 128 bytes per sector). + +*********************************************************************/ + +#include "emu.h" +#include "img_dsk.h" + +// Debugging +#define VERBOSE 0 +#define LOG(...) do { if (VERBOSE) printf(__VA_ARGS__); } while (false) + +constexpr unsigned CELL_SIZE = 1200; // Bit cell size (1 µs) +constexpr uint8_t INDEX_AM = 0x0c; // Index address mark +constexpr uint8_t ID_AM = 0x0e; // ID address mark +constexpr uint8_t DATA_AM = 0x0b; // Data address mark +constexpr uint8_t AM_CLOCK = 0x70; // Clock pattern of AM +constexpr unsigned PREIDX_GAP = 45; // Size of pre-index gap +//constexpr unsigned GAP1_SIZE = 28; // Size of gap 1 +//constexpr unsigned GAP2_SIZE = 28; // Size of gap 2 +//constexpr int ID_DATA_OFFSET = 35 * 16; // Nominal distance (in cells) between ID & DATA AM +// Size of image file +constexpr unsigned IMG_IMAGE_SIZE = IMG_TRACKS * IMG_HEADS * IMG_SECTORS * IMG_SECTOR_SIZE; +constexpr uint16_t CRC_POLY = 0x1021; // CRC-CCITT + +img_format::img_format() +{ +} + +int img_format::identify(io_generic *io, uint32_t form_factor) +{ + uint64_t size = io_generic_size(io); + + if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) && + size == IMG_IMAGE_SIZE) { + return 50; + } else { + return 0; + } +} + +bool img_format::load(io_generic *io, uint32_t form_factor, floppy_image *image) +{ + uint64_t size = io_generic_size(io); + if (size != IMG_IMAGE_SIZE) { + return false; + } + image->set_variant(floppy_image::SSDD); + + // Suck in the whole image + std::vector<uint8_t> image_data(size); + io_generic_read(io, (void *)image_data.data(), 0, size); + + for (unsigned cyl = 0; cyl < IMG_TRACKS; cyl++) { + std::vector<uint32_t> track_data; + + write_gap(track_data, 0 , PREIDX_GAP); + write_mmfm_byte(track_data , INDEX_AM , AM_CLOCK); + + for (unsigned sector = 0; sector < IMG_SECTORS; sector++) { + unsigned offset_in_image = (sector + cyl * IMG_SECTORS) * IMG_SECTOR_SIZE; + write_sector(track_data , cyl , sector + 1 , &image_data[ offset_in_image ]); + } + fill_with_gap4(track_data); + generate_track_from_levels(cyl , 0 , track_data , 0 , image); + } + return true; +} + +bool img_format::save(io_generic *io, floppy_image *image) +{ + // TODO: + return false; +} + +const char *img_format::name() const +{ + return "img"; +} + +const char *img_format::description() const +{ + return "MDS-II floppy disk image"; +} + +const char *img_format::extensions() const +{ + return "img"; +} + +bool img_format::supports_save() const +{ + // TODO: + return false; +} + +void img_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit) +{ + bool had_transition = buffer.size() < 2 ? false : bit_r(buffer, buffer.size() - 1) || bit_r(buffer , buffer.size() - 2); + clock_bit = !data_bit && (clock_bit || !had_transition); + bit_w(buffer , clock_bit , CELL_SIZE); + bit_w(buffer , data_bit , CELL_SIZE); + + if (BIT(m_crc , 15) ^ data_bit) { + m_crc = (m_crc << 1) ^ CRC_POLY; + } else { + m_crc <<= 1; + } +} + +void img_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock) +{ + for (int i = 7; i >= 0; i--) { + write_mmfm_bit(buffer , BIT(data , i) , BIT(clock , i)); + } +} + +void img_format::write_sync(std::vector<uint32_t> &buffer) +{ + write_gap(buffer , 18 , 10); +} + +void img_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc) +{ + // Note that CRC is stored with MSB (x^15) first + for (unsigned i = 0; i < 16; i++) { + write_mmfm_bit(buffer , BIT(crc , 15 - i) , 0); + } +} + +void img_format::write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff) +{ + for (unsigned i = 0; i < size_00; ++i) { + write_mmfm_byte(buffer, 0); + } + for (unsigned i = 0; i < size_ff; ++i) { + write_mmfm_byte(buffer, 0xff); + } +} + +void img_format::write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data) +{ + // **** On-disk format of a sector **** + // + // | Offset | Size | Value | Content | + // |--------+------+-------+----------------------| + // | 0 | 18 | 00 | Gap 1/3 | + // | 18 | 10 | ff | Gap 1/3 | + // | 28 | 1 | 0e | ID AM (clock = 70) | + // | 29 | 1 | xx | Track no. | + // | 30 | 1 | 00 | N/U | + // | 31 | 1 | xx | Sector no. | + // | 32 | 1 | 00 | N/U | + // | 33 | 2 | xx | ID CRC | + // | 35 | 18 | 00 | Gap 2 | + // | 53 | 10 | ff | Gap 2 | + // | 63 | 1 | 0b | Data AM (clock = 70) | + // | 64 | 128 | xx | Sector data | + // | 192 | 2 | xx | Data CRC | + // | 194 | | | | + + // Gap1 + write_sync(buffer); + // ID AM + m_crc = 0; + write_mmfm_byte(buffer , ID_AM , AM_CLOCK); + // Track # + write_mmfm_byte(buffer , track_no); + write_mmfm_byte(buffer , 0); + // Sector # + write_mmfm_byte(buffer , sect_no); + write_mmfm_byte(buffer , 0); + // ID CRC + write_crc(buffer , m_crc); + // Gap 2 + write_sync(buffer); + // Data AM + m_crc = 0; + write_mmfm_byte(buffer , DATA_AM , AM_CLOCK); + for (unsigned i = 0; i < IMG_SECTOR_SIZE; i++) { + // Data + write_mmfm_byte(buffer , sect_data[ i ]); + } + // Data CRC + write_crc(buffer , m_crc); +} + +void img_format::fill_with_gap4(std::vector<uint32_t> &buffer) +{ + // Cell count in a track (1 µs cells in a 1/6 s track) + unsigned cell_count = (500000 * 120) / 360; + unsigned cells_in_buffer = buffer.size(); + // Size of gap 4 + unsigned gap_4 = (cell_count - cells_in_buffer) / 16; + // Gap 4 + write_gap(buffer , gap_4 , 0); + // Last cell to round everything up to 2E+8 + if (buffer.size() * CELL_SIZE < 200000000) { + bit_w(buffer , false , 200000000 - buffer.size() * CELL_SIZE); + } +} + +const floppy_format_type FLOPPY_IMG_FORMAT = &floppy_image_format_creator<img_format>; diff --git a/src/lib/formats/img_dsk.h b/src/lib/formats/img_dsk.h new file mode 100644 index 00000000000..86f08b6919b --- /dev/null +++ b/src/lib/formats/img_dsk.h @@ -0,0 +1,54 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + img_dsk.h + + "IMG" disk format for SSDD Intel MDS-II 8" disks + +*********************************************************************/ +#ifndef MAME_FORMATS_IMG_DSK_H +#define MAME_FORMATS_IMG_DSK_H + +#pragma once + +#include "flopimg.h" + +#include <vector> + +// Geometry constants +constexpr unsigned IMG_TRACKS = 77; +constexpr unsigned IMG_HEADS = 1; +constexpr unsigned IMG_SECTORS = 52; +constexpr unsigned IMG_SECTOR_SIZE = 128; + +class img_format : public floppy_image_format_t +{ +public: + img_format(); + + virtual int identify(io_generic *io, uint32_t form_factor) override; + virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override; + virtual bool save(io_generic *io, floppy_image *image) override; + virtual const char *name() const override; + virtual const char *description() const override; + virtual const char *extensions() const override; + virtual bool supports_save() const override; + +private: + uint16_t m_crc; + + 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); + void write_sync(std::vector<uint32_t> &buffer); + void write_crc(std::vector<uint32_t> &buffer , uint16_t crc); + void write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff); + void write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data); + void fill_with_gap4(std::vector<uint32_t> &buffer); + 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); +}; + +extern const floppy_format_type FLOPPY_IMG_FORMAT; + +#endif // MAME_FORMATS_IMG_DSK_H diff --git a/src/mame/drivers/imds2.cpp b/src/mame/drivers/imds2.cpp index be7837d6272..1d059f11269 100644 --- a/src/mame/drivers/imds2.cpp +++ b/src/mame/drivers/imds2.cpp @@ -87,6 +87,7 @@ #include "machine/pit8253.h" #include "machine/pic8259.h" #include "bus/rs232/rs232.h" +#include "bus/multibus/multibus.h" // CPU oscillator of IPC board: 8 MHz #define IPC_XTAL_Y2 8_MHz_XTAL @@ -124,6 +125,7 @@ private: required_device<ls259_device> m_ipcctrl; required_device_array<rs232_port_device, 2> m_serial; required_device<imds2ioc_device> m_ioc; + required_device<multibus_slot_device> m_slot; std::vector<uint8_t> m_ipc_ram; @@ -161,6 +163,7 @@ imds2_state::imds2_state(const machine_config &mconfig, device_type type, const m_ipcctrl(*this, "ipcctrl"), m_serial(*this, "serial%u", 0U), m_ioc(*this, "ioc"), + m_slot(*this, "slot"), m_ipc_rom(*this, "ipcrom") { } @@ -218,6 +221,9 @@ void imds2_state::driver_start() { // Allocate 64k for IPC RAM m_ipc_ram.resize(0x10000); + + m_slot->install_io_rw(m_ipccpu->space(AS_IO)); + m_slot->install_mem_rw(m_ipccpu->space(AS_PROGRAM)); } bool imds2_state::in_ipc_rom(offs_t offset) const @@ -300,6 +306,8 @@ void imds2_state::imds2(machine_config &config) IMDS2IOC(config, m_ioc); m_ioc->master_intr_cb().set(m_ipclocpic, FUNC(pic8259_device::ir6_w)); m_ioc->parallel_int_cb().set(m_ipclocpic, FUNC(pic8259_device::ir5_w)); + + MULTIBUS_SLOT(config , m_slot , 0); } ROM_START(imds2) diff --git a/src/tools/floptool.cpp b/src/tools/floptool.cpp index 1953e7b0538..591fd5b91ad 100644 --- a/src/tools/floptool.cpp +++ b/src/tools/floptool.cpp @@ -50,6 +50,7 @@ #include "formats/applix_dsk.h" #include "formats/hpi_dsk.h" +#include "formats/img_dsk.h" #include "formats/dvk_mx_dsk.h" #include "formats/aim_dsk.h" @@ -98,6 +99,7 @@ static floppy_format_type floppy_formats[] = { FLOPPY_APPLIX_FORMAT, FLOPPY_HPI_FORMAT, + FLOPPY_IMG_FORMAT, FLOPPY_DVK_MX_FORMAT, FLOPPY_AIM_FORMAT, |