summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Peter Sobot <psobot@gmail.com>2022-04-21 08:23:46 -0400
committer GitHub <noreply@github.com>2022-04-21 22:23:46 +1000
commitf7eec60e64d2b9deff71411299c68cb0a8405316 (patch)
tree6d86bf337c229a50ed9195515c1e615591e3209b
parent0467f7c836ad162d86182f8a642ed944e3d93479 (diff)
Get Kurzweil K2000 skeleton driver to boot. (#9545)
-rw-r--r--src/mame/drivers/krz2000.cpp290
1 files changed, 280 insertions, 10 deletions
diff --git a/src/mame/drivers/krz2000.cpp b/src/mame/drivers/krz2000.cpp
index c17d110c678..5e92a1ec283 100644
--- a/src/mame/drivers/krz2000.cpp
+++ b/src/mame/drivers/krz2000.cpp
@@ -5,28 +5,38 @@
krz2000.cpp - Kurzweil K2000 series
Skeleton driver by R. Belmont
+ Additional improvements by Peter Sobot
Hardware in brief:
- TMP68301 CPU @ 16 MHz
+ TMP68301 CPU @ 16 MHz (with debug port on RXD2/TXD2)
uPD72064 FDC
- 85C30 SCSI
- M37450 on I/O board to handle panel/display/keyboard scanning
+ 85C30 SCSI (53C80 on schematic?)
+ M37450 @ 10 MHz on I/O board to handle panel/display/keyboard scanning, LED control, LCD contrast, and the audio switching matrix
+ (16K of ROM stored on U69, 384 bytes of RAM, 8 A/D inputs, 2 D/A outputs, 6 ports of I/O, and a 31.25Kbaud serial link to the maincpu)
HD6303 on I/O board to manage reverb DSP program loading
- VLSI ASIC "Calvin" - Unknown purpose (speculation: handles maincpu and PCM ROM/RAM addressing and DRAM refresh?)
+ VLSI ASIC "Calvin" - Handles maincpu and PCM ROM/RAM addressing, as well as resampling (and possibly DRAM refresh)
(on later K2000 and all later K-series units this is replaced by a more advanced VLSI ASIC "Janis")
- 2x VLSI ASIC "Hobbes" - Unknown purpose (speculation: 'reverb' DSP, interpolation, accumulation, and shifting and dithering down a wider accumulator to a 16 bit output sample?)
+ (service manual says "sample fetching tasks")
+ 2x VLSI ASIC "Hobbes" - specialized DSP processors for sound synthesis (oscillators, filters, etc)
+ (service manual says "wave form generation and DSP functions")
+ (each Hobbes chip handles a subset of available voices, and seems independently programmable)
+ Digitech DSP256 Multi Effects VLSI Chip (reverb, chorus, delay, etc)
+ (EPROM seems avaialble on eBay, but the chip itself is quite custom)
***************************************************************************/
#include "emu.h"
#include "cpu/m68000/m68000.h"
+#include "cpu/m6502/m3745x.h"
#include "machine/tmp68301.h"
#include "machine/bankdev.h"
#include "machine/upd765.h"
#include "machine/ncr5380.h"
#include "bus/nscsi/cd.h"
#include "bus/nscsi/hd.h"
+#include "bus/midi/midi.h"
#include "video/t6963c.h"
+#include "imagedev/floppy.h"
#include "screen.h"
#include "emupal.h"
#include "softlist.h"
@@ -38,8 +48,11 @@ public:
k2000_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
+ // , m_scanner(*this, "scanner")
, m_1m_bank(*this, "bank1m")
, m_mainram(*this, "mainram")
+ // , m_midi(*this, "midi")
+ // , m_fdc(*this, "fdc")
{ }
void k2000(machine_config &config);
@@ -52,24 +65,105 @@ private:
void bank_map_1m(address_map &map);
required_device<cpu_device> m_maincpu;
+ // required_device<cpu_device> m_scanner;
required_device<address_map_bank_device> m_1m_bank;
required_shared_ptr<uint16_t> m_mainram;
+ // optional_device<midi_port_device> m_midi;
+ // required_device<upd765a_device> m_fdc;
- void ctrl_w(uint16_t data);
+ void swap_bit_ctrl_w(uint16_t data);
+ uint16_t battery_voltage_and_keyscanner_version(offs_t offset);
+
+ uint16_t calvin_read(offs_t offset);
+ void calvin_write(offs_t offset, uint16_t data);
+
+ uint16_t hobbes0_read(offs_t offset);
+ void hobbes0_write(offs_t offset, uint16_t data);
+
+ uint16_t hobbes1_read(offs_t offset);
+ void hobbes1_write(offs_t offset, uint16_t data);
void k2000_palette(palette_device &palette) const;
+
+ TIMER_CALLBACK_MEMBER(boot_hack_timer);
+ emu_timer *m_boot_hack_timer;
};
-void k2000_state::ctrl_w(uint16_t data)
+/**
+ * From the K2000/K2000R service manual:
+ *
+ * The 68301 processor's 16 megabits (sic) of address space are divided into 16 equal memory blocks,
+ * numbered 0-15. The first time the K2000 is powered up, Block 0 processes data for the boot
+ * EPROM and setup EPROM, while Block 1 processes data for the PSRAM. Many of the K2000's basic
+ * functions utilize the PSRAM, which accesses the higher address space of Block 1 in the CPU.
+ * This slows down the access time somewhat, since the address space of the CPU is accessed in
+ * sequence, beginning with Block 0.
+ *
+ * To optimize the processing speed, the K2000 performs a one-time software operation when
+ * powered up for the first time. The memory management unit (MMU) in the gate array logic chip
+ * (GAL) located at U35 on the engine board executes a "swap bit" function which makes Block 0
+ * of the CPU responsible for the PSRAM, and Block 1 responsible for the Boot EPROM and Setup
+ * EPROM. Consequently the PSRAM functions are processed as early as possible.
+ *
+ * Note this "16 equal memory blocks" idea is really just the top hex digit of the address -
+ * i.e.: 0x?FFFFF
+ */
+void k2000_state::swap_bit_ctrl_w(uint16_t data)
{
data &= 0xff;
- logerror("%02x to ctrl_w\n", data);
- // bit 4: 0=program ROM at 0, work RAM at 100000, 1=work RAM at 0, program ROM at 100000.
+ logerror("%02x written to swap_bit_ctrl_w\n", data);
+ // bit 4:
+ // 0 = program ROM at 0x000000, work RAM at 0x100000
+ // 1 = work RAM at 0x000000, program ROM at 0x100000
m_1m_bank->set_bank((data >> 4) & 1);
}
+uint16_t k2000_state::battery_voltage_and_keyscanner_version(offs_t offset)
+{
+ logerror("reading battery voltage and keyscanner version at %02x\n", offset);
+ uint8_t battery_voltage = 0x36;
+ uint8_t keyscanner_version = 0x12;
+ return (((uint16_t) battery_voltage) << 8) | ((uint16_t) keyscanner_version);
+}
+
+uint16_t k2000_state::calvin_read(offs_t offset)
+{
+ logerror("Read from Calvin at %06x\n", offset);
+ return 0;
+}
+
+void k2000_state::calvin_write(offs_t offset, uint16_t data)
+{
+ logerror("Write to Calvin at %06x = %04x\n", offset, data);
+}
+
+uint16_t k2000_state::hobbes0_read(offs_t offset)
+{
+ logerror("Read from Hobbes[0] at %06x\n", offset);
+ return 0;
+}
+
+void k2000_state::hobbes0_write(offs_t offset, uint16_t data)
+{
+ logerror("Write to Hobbes[0] at %06x = %04x\n", offset, data);
+}
+
+
+uint16_t k2000_state::hobbes1_read(offs_t offset)
+{
+ logerror("Read from Hobbes[1] at %06x\n", offset);
+ return 0;
+}
+
+void k2000_state::hobbes1_write(offs_t offset, uint16_t data)
+{
+ logerror("Write to Hobbes[1] at %06x = %04x\n", offset, data);
+}
+
void k2000_state::machine_start()
{
+ m_boot_hack_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(k2000_state::boot_hack_timer), this));
+ m_boot_hack_timer->adjust(attotime::from_seconds(2), 0, attotime::never);
}
void k2000_state::machine_reset()
@@ -77,23 +171,161 @@ void k2000_state::machine_reset()
m_1m_bank->set_bank(0);
}
+TIMER_CALLBACK_MEMBER(k2000_state::boot_hack_timer)
+{
+ logerror("Jumping to interrupt vector to continue boot sequence");
+ m_maincpu->set_pc(m_1m_bank->read32(0x80));
+}
+
void k2000_state::k2000_map(address_map &map)
{
// word writes to 000000 region - unknown
// word writes to 000090 region - unknown
// word writes to 000180 region - unknown
// word writes to 000240 region - unknown
+
+ // The engine ROMs are mapped onto 0x0 when ENGROMCSb is low, which is IO3 of GODOT (pin 18)
+ // The setup ROMs are mapped onto 0x100000 when SUROMCSb is low, which is the IO2 output (pin 17) of GODOT
+ // This implies that IO3 of GODOT is low iff CA{20,23} are also low, and IO2 of GODOT is low iff CA{20,23} = 0x01
+
+ // Block 0, Block 1: ROM and RAM (depending on bank switch)
map(0x000000, 0x1fffff).m(m_1m_bank, FUNC(address_map_bank_device::amap16));
+
+ // Lots of writes to 0x372B68 -> 0x372C8C
+
+ // Block 4: PRAM?
+ // "The K2000 comes standard with 116KB, and with the PRAM upgrade, totals 760KB."
+ // Another source says:
+ // For the K2000 Series, the total memory allocated to all of these objects is
+ // 120K expandable to 760K with the PRAM option. For the K2500 Series, it is 256k,
+ // expandable to 1280k with the PRAM option. For the K2600 Series, it is 486k,
+ // expandable to 1506k with the PRAM option.
+ // These are pretty odd numbers - but it sounds like ~8KB of the RAM space is used
+ // for OS functions, meaning the built-in RAM might only be 128KB in total.
+ // The K2000 schematic shows 2x 1M "NVRAM" chips, one for the low byte, and one for
+ // the high byte, which means we should (?) actually have 2 * 1024 * 1024 / 8 = 262,144 bytes?
+ //
+ // The boot routines seem to write to 0x400000 and compare the value saved there, so
+ // this is probably the optional PRAM expansion.
+ map(0x400000, 0x400000 + ((2 * 1024 * 1024) / 8) - 1).ram();
+
+ // Init routine sets 0x4BAB44 -> 0x4BFFFA to 0x0000
+ // Reads from 0x4BFFFC and 0x4BFFFE seem to happen on startup.
+ // HOBBES0CSb = NAND(CS1', CA19) = high unless CS1 is high and CA19 is high
+ // HOBBES1CSb = NAND(CS1', !CA19) = high unless CS1 is high and CA19 is low
+ // 0xFFFC00 is the Address Decoder area that controls which part of memory causes CS1 to be "enabled" (low)
+ // At runtime,
+ // 0xFFFC00 = 0x50 =
+ // 0xFFFC01 = 0x3F
+ // 0xFFFC02 = 0x00
+ // 0xFFFC03 = 0x30
+ // For CS1:
+ // 0xFFFC04 = 0x60 = 0b0110 0000 = Start address of the memory area for CS1 = A21 + A22
+ // 0xFFFC05 = 0x3F = 0b0011 1111 = Size of memory area = M19 + M18 + M17 + M16 + M15-M9 + M8 = 1MB
+ // 0xFFFC06 = 0x00
+ // 0xFFFC07 = 0x30 = 0b0011 0000 = Area enabled, use external DTACK
+ // By this logic, Hobbes chips are mapped to 0x100000, 0x200000, 0x400000, 0x600000
+ // And their address mask is 0x000FFF00, which means the effective ranges are:
+ // 0x100???00
+
+ // Hobbes chip selection seems to look at CA19 and CS1
+
+ // Internal sample ROM is switched on SBA20,SBA21, which corresponds to CA21,CA22 (one bit off)
+ // meaning the internal address space for accessing the sample ROM is 0x200000 through 0x6fffff (?)
+ // Blocks 2, 3, 4, 5, 6
+ // map(0x200000, 0x2fffff).rom().region("pcm", 0); // Block 2: Sample ROM
+ // map(0x300000, 0x3fffff).rom().region("pcm", 0x100000); // Block 3: Sample ROM
+ // map(0x400000, 0x4fffff).rom().region("pcm", 0x200000); // Block 4: Sample ROM
+ // Writes to 0x4FFD82, 0x4FFD88, 0x4FFE24
+
+ // Hypothesis: Calvin (or Janis) is mapped at 0x500000
+ // Writes to 0x500402, 0x500418, 0x50041A early in the boot process
+ // Initialization then writes zeros in a descending ladder:
+ // 0x500400, 0x500402
+ // 0x5003FC, 0x5003FE, ...
+ // down to 0x500004 (then 0x500006)
+
+ // Calvin memory?
+ map(0x500000, 0x5003FF).rw(FUNC(k2000_state::calvin_read), FUNC(k2000_state::calvin_write));
+ // Calvin control registers?
+ map(0x500400, 0x5004FF).rw(FUNC(k2000_state::calvin_read), FUNC(k2000_state::calvin_write));
+ // Reads and writes to 0x580000
+
+ // Hobbes (it seems) is connected in the 0x600000 range
+ // The second Hobbes chip (?) seems connected at 0x680000
+ // Initialization seems to:
+ // - write to 0x600300, followed by 0x600xxx where xxx is in 0x400 through 0x5FF
+ // - write to 0x6?0002, 0x6?0006, 0x6?0004, through 0x6?000F4
+ // - write to 0x600004, 0x600014, 0x600024... through 0x6002F4
+ // (with a repeating pattern of [0x02, 0x00, 0x26, 0x24, 0x4A, 0x48])
+ // - write to 0x680004, 0x680014, 0x680024... through 0x6802F4
+ // (with a repeating pattern of [0x80, 0x02, 0xA4, 0x26, 0xC6, 0x4A])
+ // Each Hobbes chip seems to have two control registers, mapped at 0x600300/0x680300 and 0x600400/0x680400
+
+ // Hobbes memory?
+ map(0x600000, 0x6002FF).rw(FUNC(k2000_state::hobbes0_read), FUNC(k2000_state::hobbes0_write));
+ // Hobbes control registers?
+ map(0x600300, 0x6005FF).rw(FUNC(k2000_state::hobbes0_read), FUNC(k2000_state::hobbes0_write));
+
+ // Hobbes memory?
+ map(0x680000, 0x6802FF).rw(FUNC(k2000_state::hobbes1_read), FUNC(k2000_state::hobbes1_write));
+ // Hobbes control registers?
+ map(0x680300, 0x6805FF).rw(FUNC(k2000_state::hobbes1_read), FUNC(k2000_state::hobbes1_write));
+
+ // SROM - Setup ROM?
+ uint32 srom_base = 0x280000;
+ map(srom_base, srom_base + 0x40000 - 1).rom().region("maincpu", 0x100000);
+
+ // Calvin chip has a 20-bit address bus with A0 tied low and A12-15 tied high, giving it access to 0x?0??[0-E]
+ // Later models' Janis chips use 24-bit address buses (or at least they're all connected in the schematic)
+ // Hobbes has a 10-bit address bus with A0 tied low, giving access to 0x0 through 0x3FF
+
+ // Block 7: LCD control and bank switching
map(0x700000, 0x700003).rw("lcd", FUNC(lm24014h_device::read), FUNC(lm24014h_device::write)).umask16(0x00ff);
- map(0x7e0000, 0x7e0001).w(FUNC(k2000_state::ctrl_w));
+
+ // Reads from 0x740000 (floppy?)
+ // Writes to 0x760000
+ // Writes to 0x7C0000
+
+ // Single byte reads seem to happen at 0x740000, which seems to be FDCCTLCSb?
+ // map(0x740000, 0x74000F).
+ map(0x7e0000, 0x7e0001).w(FUNC(k2000_state::swap_bit_ctrl_w));
+
+ // Some sort of hardware mapping - initial firmware boot expects a 4 at $78000B
+ map(0x780000, 0x78FFFF).ram();
+
+ // When (CPUASb + GODOT I6) and (GODOT I/O0 + IOCSb) are low,
+ // Address bits 17, 18, 19 get demultiplexed onto:
+ // 0, 0, 0 => LCDCSb
+ // 0, 0, 1 => FDCCSb
+ // 0, 1, 0 => FDCCTLCSb
+ // 0, 1, 1 => FDCOUTCSb
+ // 1, 0, 0 => SCSICSb
+ // 1, 0, 1 => SCSIDMACSb
+ // 1, 1, 0 => CPUEXPCSb
+ // 1, 1, 1 => MCTLREGCSb (OR'ed with WRITELOb to provide input into the Memory Management Control Register)
+
+ // map(0x800000, 0x8fffff); // Block 8: ???
+ // map(0x900000, 0x9fffff); // Block 9: ???
+ // map(0xa00000, 0xffffff); // Blocks 10, 11, 12, 13, 14: Orchestral ROM
+ // map(0x1000000, 0x18fffff); // Blocks 15, 16, 17, 18, 19: Contemporary ROM
}
void k2000_state::bank_map_1m(address_map &map)
{
map(0x000000, 0x0fffff).rom().region("maincpu", 0);
map(0x100000, 0x1fffff).ram().share("mainram");
+
+ // The "stride" of this bank map is 0x200000, so this is the second "view" of the same data
map(0x200000, 0x2fffff).ram().share("mainram");
map(0x300000, 0x3fffff).rom().region("maincpu", 0);
+
+ // Unsure if the battery voltage and keyscanner data is memory-mapped at these ranges (unlikely)
+ // or if this data is written to some time during the boot process. If it's the latter,
+ // this will have to change when loading different versions of the ROM (or someone has to figure
+ // out where the battery voltage is actually read from).
+ map(0x100000 + 0x1052, 0x100000 + 0x1053).r(FUNC(k2000_state::battery_voltage_and_keyscanner_version));
+ map(0x200000 + 0x1052, 0x200000 + 0x1053).r(FUNC(k2000_state::battery_voltage_and_keyscanner_version));
}
void k2000_state::k2000(machine_config &config)
@@ -101,14 +333,47 @@ void k2000_state::k2000(machine_config &config)
TMP68301(config, m_maincpu, XTAL(12'000'000));
m_maincpu->set_addrmap(AS_PROGRAM, &k2000_state::k2000_map);
+ // MIDI_PORT(config, m_midi, midiin_slot, "midiin");
+ // TODO: Uncomment when the TMP68301 has serial/MIDI support, to wire up MIDI I/O and scanner.
+ // MIDI_PORT(config, m_midi, midiout_slot, "midiout");
+
+ // M37450(config, m_scanner, XTAL(10'000'000));
+ // The "Scanner" sub-CPU is connected to main CPU via:
+ // - MIDI_FM_ENG, going to P34/RxD, which comes from SERTxD, which is INTMIDITxD == TxD1
+ // - 37450_TXD, coming from P35/TxD, which is NAND'd together with 6303_TXD
+ // and inverted to provide SERRxD to the engine (maincpu), which goes to INTMIDIRxD
+ // eventually ending up at RxD1
+ // Essentially, the 68k's second built-in serial port runs at 31250Hz, which is MIDI baud rate,
+ // which is how it talks to the scanner to tell it to operate the LEDs on the front of the case.
+ // The external MIDI port(s) are connected directly to the 68K's first serial port, RxD0 and TxD0.
+ // (RxD2 and TxD2 are hooked up to a "debug port" which the manual says requires additional
+ // hardware - this is untested.)
+ // The scanner's ROM comes from an NTE2764 (U69, 28-pin DIP) EPROM of which 16Kb are used.
+ // (Note that on the K2500 and above, the scanner is an M37451M8-FP the ROM is 16KB, but stored on an M27256.)
+ // The scanner ROM from the K2500 may be compatible with the K2000, and can be pulled from "U2"
+ // on the K2500 Audio Scanner Board (Rev D)
+
ADDRESS_MAP_BANK(config, "bank1m").set_map(&k2000_state::bank_map_1m).set_options(ENDIANNESS_BIG, 16, 24, 0x200000);
+ // uPD765AC; clocked through SED9420C
+ // UPD765A(config, m_fdc, 16_MHz_XTAL / 4, true, true);
+ // m_fdc->intrq_wr_callback().set_inputline(m_maincpu, I6);
+ // FLOPPY_CONNECTOR(config, "fdc:0", dss1_floppies, "35dd", floppy_image_device::default_floppy_formats).enable_sound(true);
+ // FLOPPY_CONNECTOR(config, "fdc:1", dss1_floppies, nullptr, floppy_image_device::default_floppy_formats).enable_sound(true);
+
+ PALETTE(config, "palette", FUNC(k2000_state::k2000_palette), 2);
+
LM24014H(config, "lcd");
SPEAKER(config, "lspeaker").front_left();
SPEAKER(config, "rspeaker").front_right();
}
+void k2000_state::k2000_palette(palette_device &palette) const
+{
+ palette.set_pen_color(0, rgb_t(131, 136, 139));
+ palette.set_pen_color(1, rgb_t( 92, 83, 88));
+}
static INPUT_PORTS_START( k2000 )
@@ -199,7 +464,12 @@ ROM_START( k2000 )
ROM_REGION(0x3000, "pals", 0)
ROM_LOAD( "pseudo_v4d.u11.gal16v8b.jed", 0x000000, 0x000bd0, CRC(43561132) SHA1(a0c567c81022bc7fb83023d89556ccd5aa1ab36d) )
ROM_LOAD( "sndram_v1.u50.gal16v8b.jed", 0x001000, 0x000bd0, CRC(cabc9335) SHA1(968fa5baa43c7589c901f09b12085437834aeb37) )
+ // GODOT seems to take in the top 4 maincpu address lines (A20-A24) and multiplex/decode onto 11 (?) I/O lines
+ // Effectively takes care of the "memory block" logic (MMU/bank switching) that the service manual describes
ROM_LOAD( "godot_v5.u10.gal20v8a.jed", 0x002000, 0x00066f, CRC(c6517456) SHA1(b82530d46afdca5f6460e77ac11710cad55a6b89) )
+ // There's an additional GAL20V8 for the MMU - exact logic unknown, but the MCTL[0-3] lines are used, which seem
+ // like the "address space select" lines - so at least one or more of those address space areas map directly to SBA[22-25], which
+ // seem to be the Sample Bank address lines.
ROM_END
CONS( 1990, k2000, 0, 0, k2000, k2000, k2000_state, empty_init, "Kurzweil Music Systems", "K2000", MACHINE_NOT_WORKING|MACHINE_NO_SOUND )