diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 6a2af2aa2c3..f84d84642cb 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -2707,6 +2707,7 @@ files { MAME_DIR .. "src/mame/drivers/mekd5.cpp", MAME_DIR .. "src/mame/drivers/mvme147.cpp", MAME_DIR .. "src/mame/drivers/mvme162.cpp", + MAME_DIR .. "src/mame/drivers/uchroma68.cpp", } createMESSProjects(_target, _subtarget, "multitch") diff --git a/src/mame/drivers/uchroma68.cpp b/src/mame/drivers/uchroma68.cpp new file mode 100644 index 00000000000..a9b075c183e --- /dev/null +++ b/src/mame/drivers/uchroma68.cpp @@ -0,0 +1,497 @@ +// license:BSD-3-Clause +// copyright-holders: 68bit +/****************************************************************************** + +Micro Chroma 68 +Motorola evaluation board for the MC6847 VDG and MC1372 RF modulator. + +The Micro Chroma was supplied as a kit that included the PCB and the major +parts and is reported to have been designed by the "applications group" in +Austin including Tim Ahrens, Jack Brown, and Hunter Scales. The micro Chroma +68 uses the TVBUG monitor which is a 2k monitor ROM that is included in the +MC6846 timer and is reported to have been written by John Dumas with parts +taken from MIKBUG with the help of Mike Wiles the author of MIKBUG. + +References: + +1. "Micro Chroma 68: The new 'bug' from Motorola TVBUG 1.2", Motorola + Semiconductor Products Inc. Microprocessor Operations., 1978 + +G - Go to user program. +G - Go to user program, starting at the given address 'addr'. +L - Load tape. The encoding uses the Kansas City Standard and the format is + largely JBUG compatible. Enter an offset, the difference between the + existing start address and the target start address. If the data can not be + stored in the memory then 'Memory Bad' is printed and it exits. The load + can be aborted by pressing the Escape key. +P - Punch tape. Enter the start address, the end address, a name up to 31 + characters long, and enter return to start. The format is largely JBUG + compatible, just avoid using 'B' or 'G' in the name. The punch can be + aborted by pressing the Escape key. +V - Verify Tape. Enter an offset, a difference between the existing start + address and the target start address. If there is an error then 'Memory + Bad' is printed and it exits. The verfiy can be aborted by pressing the + Escape key. +M - Memory examine and change at address 'addr'. + - Enter new data for the address and increase the address. + If the memory can not be changed the 'Memory Bad' is printed that the + memory examine is exited. + '^' - decrease the address. + - exit the memory examine function. +E - Examine a memory block. Enter the start address, the end address, and then + a space to print the next line. +Q - Quick load. Enter the start address, the end address, then the hex bytes + to load separate by a space. +F - Fill memory. Enter the start address, the end address, and the fill byte. +O - Offset calculation. Enter the start address and then the end address. The + offset is printed as a 16 bit number, the lower 8 bits being the offset. + If the offset is too large then 'TOO FAR' is printed. + +R - Register display: CC B A X PC SP. The stack pointer (SP) is stored at + 0xf382 to 0xf383 and may be modifed via the memory examine 'M' function + there. The other registers are on the stack at: + SP + 1 : CC + SP + 2 : B + SP + 3 : A + SP + 4,5 : X + SP + 6,7 : P +Z - Clear screen. +S - Set a breakpoint at address 'addr'. Up to eight breakpoints may be + defined. A breakpoint can not be set at 0x0000. +U - Unset the breakpoint at address 'addr'. +D - Remove all breakpoints. +B - Print all breakpoints. +C - Contine user program, until next breakpoint. +N - Traces one instruction. +T - Traces 'n' instructions. + +! - User defined code from 0xf396 to 0xf398, a jump instruction. +" - User defined code from 0xf399 to 0xf39a, a jump instruction. +# - User defined code from 0xf39c to 0xf39d, a jump instruction. + +A user input function can be defined at 0xf390 to 0xf392, which is room for a +jump instruction, and this is called by the monitor input loop. This function +should set the carry bit on return if there is user input. + +A user output function can be defined at 0xf393 to 0xf395 which is room for a +jump instruction. + +The board has a "Reset" button, and a "Break" button that toggled the NMI +line. TODO implement these. + +The documentation includes hardware suggestions for memory expansion. This +expands the RAM to use the address range 0x0000 to 0xcfff. + +The address range 0xe800 to 0xefff is for user expansion. The documentation +gives examples using this address range for two 1k or one 2k EPROM. + +The documentation includes position independent ROMable code for driving a +printer via the ACIA, and it also adds S19 'load', 'punch', and 'verify' tape +support for compatibility with MIKBUG, EXBUG, and MINIBUG. The documentation +suggests using this as an optional ROM at 0xe800 to 0xea73. The printer +support implements a user output function definable at 0xf393 to direct screen +output to the ACIA and with a hardware patch this can be directed to a RS232 +printer. The entry point for the printer support is 0xe800. The entry point +for the S19 support is 0xe802, so type 'G E802 ' to enter this option and +it displays a choice of operations. It prints a 'B' for each record 'punched', +and a 'S' for each record loaded or verified, and it reports the address of +any bad memory. The S19 support writes a 0x11 (DC1) to start the tape playback +and a 0x12 (DC2) to start the tape recording as might be expected for the +Texas Instruments 733 ASR twin tape cassette terminal with automatic device +control, but it does not appear to issue DC3 or DC4 to turn the tape off. + +The documentation includes patches for a range or TSC software which is +generally supplied in Kansas City S19 format, so might need the above extra +EPROM support. The omission of any mention of support for Motorola software is +notable. + +The documentation includes instructions on using the Micro Chroma 68 as a dumb +terminal, requiring some minor hardware modifications to reuse the MC6850 ACIA +for a RS-232 interface, and it includes ROMable code at 0xfc00 to 0xffff code +to implement this, a substitute for the TVBUG monitor. TODO might consider +implementing this, but it was hardly a good terminal for the time. + +Address decoding was available in the range 0xc000 to 0xcfff in 1k blocks for +expansion. + +******************************************************************************/ + +#include "emu.h" +#include "cpu/m6800/m6800.h" +#include "machine/6821pia.h" +#include "machine/6850acia.h" +#include "machine/mc6846.h" +#include "machine/clock.h" +#include "machine/timer.h" +#include "video/mc6847.h" +#include "imagedev/cassette.h" +#include "sound/wave.h" +#include "speaker.h" + +#include "machine/terminal.h" +#include "video/mc6845.h" +#include "screen.h" + +#define XTAL_UCHROMA68 3.579545_MHz_XTAL + +class uchroma68_state : public driver_device +{ +public: + uchroma68_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_mc6846(*this, "mc6846") + , m_mc6847(*this, "mc6847") + , m_pia(*this, "pia") + , m_acia(*this, "acia") + , m_acia_tx_clock(*this, "acia_tx_clock") + , m_screen(*this, "screen") + , m_video_ram(*this, "videoram") + , m_cass(*this, "cassette") + , m_semi_graphics_six_mod(*this, "semi_graphics_six_mod") + { } + + void uchroma68(machine_config &config); + + enum + { + TIMER_KBD_STROBE + }; + +private: + required_device m_maincpu; + required_device m_mc6846; + required_device m_mc6847; + required_device m_pia; + required_device m_acia; + required_device m_acia_tx_clock; + required_device m_screen; + required_shared_ptr m_video_ram; + required_device m_cass; + required_ioport m_semi_graphics_six_mod; + + void uchroma68_mem(address_map &map); + virtual void machine_start() override; + virtual void machine_reset() override; + + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + DECLARE_READ8_MEMBER(mc6847_videoram_r); + DECLARE_READ8_MEMBER(pia_pa_r); + DECLARE_READ8_MEMBER(pia_pb_r); + DECLARE_WRITE8_MEMBER(mc6846_out_w); + void kbd_put(uint8_t data); + emu_timer *m_kbd_strobe_timer; + bool m_kbd_strobe; + uint8_t m_kbd_data; + bool m_video_inv; + + TIMER_DEVICE_CALLBACK_MEMBER(kansas_r); + DECLARE_WRITE_LINE_MEMBER(kansas_w); + uint8_t m_cass_rx_clock_count, m_cass_rx_period, m_cass_txcount; + bool m_cass_in, m_cass_inbit, m_cass_txbit, m_cass_last_txbit; +}; + + +/*********************************************************** + + Address Map + +************************************************************/ + +void uchroma68_state::uchroma68_mem(address_map &map) +{ + // On board user RAM + map(0x0000, 0x1fff).ram(); + + // Off board RAM expansion, as documented. + map(0x2000, 0xcfff).ram(); + + // Display RAM + map(0xd000, 0xe7ff).ram().share(m_video_ram); + + // Optional EPROM, with the supplied S19 tape format support. + map(0xe800, 0xebff).rom(); + + // Stack RAM + map(0xf000, 0xf3ff).ram(); + + map(0xf408, 0xf409).mirror(0x03b0).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); + + map(0xf404, 0xf407).mirror(0x03b0).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); + + // Programmable counter/timer and I/O. + map(0xf440, 0xf447).mirror(0x0380).rw(m_mc6846, FUNC(mc6846_device::read), FUNC(mc6846_device::write)); + + // TVBUG 1.2 ROM + map(0xf800, 0xffff).rom(); +} + +/*********************************************************** + + Keys + +************************************************************/ + +static INPUT_PORTS_START(uchroma68) + + // The documentation notes a hardware modification to allow the use of + // the Semi-graphics 6 mode of the MC6847. + PORT_START("semi_graphics_six_mod") + PORT_CONFNAME(0x01, 0x00, "Semi-graphics 6 mode modification") + PORT_CONFSETTING(0x00, "No") + PORT_CONFSETTING(0x01, "Yes") + +INPUT_PORTS_END + + +/*********************************************************** + +************************************************************/ + +void uchroma68_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_KBD_STROBE: + m_kbd_strobe = 1; + m_kbd_data = 0; + break; + default: + throw emu_fatalerror("Unknown id in uchroma68_state::device_timer"); + } +} + +WRITE8_MEMBER(uchroma68_state::mc6846_out_w) +{ + m_mc6847->css_w(!BIT(data, 0)); + m_mc6847->intext_w(!BIT(data, 1)); + m_mc6847->ag_w(!BIT(data, 2)); + m_mc6847->gm0_w(BIT(data, 3)); + m_mc6847->gm1_w(BIT(data, 4)); + m_mc6847->gm2_w(BIT(data, 5)); + m_video_inv = BIT(data, 6); + // P7 is NC. +} + + +READ8_MEMBER(uchroma68_state::mc6847_videoram_r) +{ + offset &= 0x1fff; + if (offset > 0x17ff) return 0xff; + + uint8_t code = m_video_ram[offset]; + + if (m_semi_graphics_six_mod->read()) + m_mc6847->inv_w(m_video_inv); + else + m_mc6847->inv_w(m_video_inv | BIT(code, 6)); + + m_mc6847->as_w(BIT(code, 7)); + + return code; +} + +// TVBUG polls the parallel keyboard strobe every four video frames - it +// blinks the cursor for four frames between polling. At a 60Hz frame rate +// that gives polling at about every 67ms. A 70ms keyboard strobe period +// appears to be more than adequate. +void uchroma68_state::kbd_put(uint8_t data) +{ + m_kbd_data = data; + m_kbd_strobe_timer->adjust(attotime::from_msec(70)); + m_kbd_strobe = 0; +} + +READ8_MEMBER(uchroma68_state::pia_pa_r) +{ + uint8_t data = m_kbd_data; + return (m_kbd_strobe << 7) | data; +} + +READ8_MEMBER(uchroma68_state::pia_pb_r) +{ + // PB0 to PB4 are Up, Down, Left, Right, Home. + // PB5 is NC + // PB6 is V VDG + // PB7 is H VDG + return (m_mc6847->hs_r() << 7) | (m_mc6847->fs_r() << 6); +} + + +TIMER_DEVICE_CALLBACK_MEMBER(uchroma68_state::kansas_r) +{ + // Turn 1200/2400Hz to a bit + uint8_t cassin = (m_cass->input() > +0.04) ? 1 : 0; + uint8_t inbit = m_cass_inbit; + + m_cass_rx_period++; + + // The RX clock is recovered from the input data, and synchronized to + // the bit transitions. The software expects a 1x RX clock. + if (++m_cass_rx_clock_count > 133) + m_cass_rx_clock_count = 0; + + if (cassin != m_cass_in) + { + // A transition, so now check the period. + inbit = (m_cass_rx_period < 12) ? 1 : 0; + m_cass_in = cassin; + m_cass_rx_period = 0; + } + else if (m_cass_rx_period > 32) + { + // Idle the ACIA if there is no data. + m_cass_rx_period = 32; + inbit = 1; + } + + if (inbit != m_cass_inbit) + { + m_acia->write_rxd(inbit); + m_cass_inbit = inbit; + // Sync the RX clock with the data edges. + m_cass_rx_clock_count = 0; + } + + if (m_cass_rx_clock_count == 0) + m_acia->write_rxc(0); + else if (m_cass_rx_clock_count == 67) + m_acia->write_rxc(1); +} + +WRITE_LINE_MEMBER(uchroma68_state::kansas_w) +{ + // The Kansas City cassette format encodes a '0' bit by four cycles of + // a 1200 Hz sine wave, and a '1' bit as eight cycles of 2400 Hz, + // giving a 300 baud rate. + // + // The clock rate to the ACIA is 16x the baud rate, or 4800Hz, and is + // divided by 2 to get the 2400 Hz rate, or divided by 4 to get the + // 1200 Hz rate. + + // Sync the period phase on TX bit transitions. + if (m_cass_txbit != m_cass_last_txbit) + { + m_cass_txcount = 0; + m_cass_last_txbit = m_cass_txbit; + } + + if (m_cass_txbit) + m_cass->output(BIT(m_cass_txcount, 1) ? +1.0 : -1.0); // 2400Hz + else + m_cass->output(BIT(m_cass_txcount, 2) ? +1.0 : -1.0); // 1200Hz + + m_cass_txcount++; + m_acia->write_txc(state); +} + +void uchroma68_state::machine_start() +{ + m_kbd_strobe_timer = timer_alloc(TIMER_KBD_STROBE); + m_kbd_strobe = 1; + + save_item(NAME(m_kbd_strobe)); + save_item(NAME(m_kbd_data)); + save_item(NAME(m_video_inv)); + save_item(NAME(m_cass_rx_clock_count)); + save_item(NAME(m_cass_rx_period)); + save_item(NAME(m_cass_txcount)); + save_item(NAME(m_cass_in)); + save_item(NAME(m_cass_inbit)); + save_item(NAME(m_cass_txbit)); + save_item(NAME(m_cass_last_txbit)); +} + +void uchroma68_state::machine_reset() +{ + m_mc6846->reset(); + m_pia->reset(); + m_kbd_strobe_timer->reset(); + m_kbd_strobe = 1; + m_kbd_data = 0; + + // These MC6846 I/O outputs are pulled high. The high impedance state + // of these lines is not yet emulated by the mc6846 driver, and TVBUG + // does not drive them. On reset these are high impedance anyway, and + // pulled high, so their lines going to the VDG are initialized + // here. The first three lines are inverted. + m_mc6847->css_w(0); + m_mc6847->intext_w(0); + m_mc6847->ag_w(0); + m_mc6847->gm0_w(1); + m_mc6847->gm1_w(1); + m_mc6847->gm2_w(1); + m_video_inv = 1; + + m_cass_rx_clock_count = 0; + m_cass_rx_period = 0; + m_cass_txcount = 0; + m_cass_in = 0; + m_cass_inbit = 0; + m_cass_txbit = 0; + m_cass_last_txbit = 0; +} + +/*********************************************************** + + Machine + +************************************************************/ + +void uchroma68_state::uchroma68(machine_config &config) +{ + M6808(config, m_maincpu, XTAL_UCHROMA68); // 894.8 kHz clock + m_maincpu->set_addrmap(AS_PROGRAM, &uchroma68_state::uchroma68_mem); + + MC6846(config, m_mc6846, XTAL_UCHROMA68 / 4); // Same as the cpu clock + m_mc6846->out_port().set(FUNC(uchroma68_state::mc6846_out_w)); + + SPEAKER(config, "mono").front_center(); + + CASSETTE(config, m_cass); + m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED); + m_cass->add_route(ALL_OUTPUTS, "mono", 0.05); + + SCREEN(config, "screen", SCREEN_TYPE_RASTER); + + mc6847_ntsc_device &vdg(MC6847_NTSC(config, "mc6847", XTAL_UCHROMA68)); + vdg.set_screen("screen"); + vdg.input_callback().set(FUNC(uchroma68_state::mc6847_videoram_r)); + + PIA6821(config, m_pia, 0); + m_pia->readpa_handler().set(FUNC(uchroma68_state::pia_pa_r)); + m_pia->readpb_handler().set(FUNC(uchroma68_state::pia_pb_r)); + + ACIA6850(config, m_acia, 0); + m_acia->txd_handler().set([this] (bool state) { m_cass_txbit = state; }); + + CLOCK(config, m_acia_tx_clock, 4800); + m_acia_tx_clock->signal_handler().set(FUNC(uchroma68_state::kansas_w)); + + TIMER(config, "kansas_r").configure_periodic(FUNC(uchroma68_state::kansas_r), attotime::from_hz(40000)); + + generic_keyboard_device &keyboard(GENERIC_KEYBOARD(config, "keyboard", 0)); + keyboard.set_keyboard_callback(FUNC(uchroma68_state::kbd_put)); +} + +/*********************************************************** + + ROMS + +************************************************************/ + +ROM_START(uchroma68) + ROM_REGION(0x10000,"maincpu",0) + ROM_LOAD("tvbug.rom", 0xf800, 0x0800, CRC(47f721e4) SHA1(31ea8d596f1f99ee26c4bea694245448bfdc3ee6)) + ROM_LOAD("uch68s19.rom", 0xe800, 0x0400, CRC(8932484f) SHA1(231098ab10185d400032415cf9b7273d736d3b87)) +ROM_END + +/*************************************************************************** + + Game driver(s) + +***************************************************************************/ + +// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS +COMP( 1980, uchroma68, 0, 0, uchroma68, uchroma68, uchroma68_state, empty_init, "Motorola", "Micro Chroma 68" , MACHINE_NO_SOUND_HW ) diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 1bd562d29f4..53d5886efa0 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -38965,6 +38965,9 @@ blazlaz // (c) 1989 Hudson Soft keith // (c) 1989 Hudson Soft paclandp // (c) 1989 Namco +@source:uchroma68.cpp +uchroma68 // 1980 Motorola evaluation board for the MC6847 + @source:uknc.cpp uknc // diff --git a/src/mame/mess.flt b/src/mame/mess.flt index 90a53f57d13..ab84be3277b 100644 --- a/src/mame/mess.flt +++ b/src/mame/mess.flt @@ -878,6 +878,7 @@ tvboy.cpp tvc.cpp tvgame.cpp tx0.cpp +uchroma68.cpp uknc.cpp unichamp.cpp unior.cpp