// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*
Convergent NGen series
10-11-14 - Skeleton driver
Interrupts based on patents:
level 1 - SIO
level 3 - timer (from PIT, presumably channel 0? Patent says "channel 3")
level 4 - "interrupt detector" - keyboard, printer, RTC
level 7 - floppy/hard disk
DMA channels:
channel 0 - communications (RS-232)
channel 1 - X-Bus expansion modules (except disk and graphics)
channel 2 - graphics?
channel 3 - hard disk
On the CP-001/B26 channels 4 on are handled by the 80186.
channel 4 - floppy disk
To get to "menu mode", press Space quickly after reset (might need good timing)
The bootstrap ROM version number is displayed, along with "B,D,L,M,P,T:"
You can press one of these keys for the following tests:
B: Bootstrap
Loads the system image file (from disk or master workstation)
D: Dump
RAM contents are dumped to a local disk drive or master workstation
L: Load
Loads the system image file, then enters the Panel Debugger. Exiting the Panel
Debugger will continue execution of the system image
M: Memory Test
Continuously performs the Memory Test until the system is reset.
P: Panel Debugger
Enters the Panel Debugger
T: Type of Operating System
Gives an "OS:" prompt, at which you can enter the number of the system image to
load at the master workstation.
Panel Debugger:
- Open/Modify RAM
Enter an address (seg:off) followed by a forward-slash, the contents of this word will
appear, you can enter a value to set it to, or just press Next (default: Enter) to leave
it as is. It will then go on to the next word. Pressing Return (scan code unknown
currently) will return to the debugger prompt.
- Open/Modify Register
Enter the register only, and the contents will appear, you can leave it or alter it (you
must enter all digits (eg: 0A03 if you're modifying DX) then press Return.
- I/O to or from a port
Input: Address (segment is ignored, and not required) followed by I, a byte is read from
the port defined by the offset, and the byte is displayed.
Output: Address followed by O, you are now prompted with an '='. Enter the byte to send
to the port, and press Return.
- Set Haltpoint:
Enter an address (seg:off) followed by H. Sets a haltpoint at the specified address. Does
not work for ROM addresses. Only one allowed at a time. Haltpoint info is stored at
0000:01F0. Uses a software interrupt (INT 7C), rather than INT 3.
To start or continue from the current address, enter P.
To start from a specific address, enter the address (seg:off) followed by a G.
*/
#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/i386/i386.h"
#include "cpu/i86/i186.h"
#include "imagedev/harddriv.h"
#include "machine/am9517a.h"
#include "machine/clock.h"
#include "machine/i8251.h"
#include "machine/ngen_kb.h"
#include "machine/pic8259.h"
#include "machine/pit8253.h"
#include "machine/wd2010.h"
#include "machine/wd_fdc.h"
#include "machine/z80dart.h"
#include "video/mc6845.h"
#include "screen.h"
class ngen_state : public driver_device
{
public:
ngen_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_maincpu(*this,"maincpu"),
m_i386cpu(*this,"i386cpu"),
m_crtc(*this,"crtc"),
m_viduart(*this,"videouart"),
m_iouart(*this,"iouart"),
m_dmac(*this,"dmac"),
m_pic(*this,"pic"),
m_pit(*this,"pit"),
m_hdc(*this,"hdc"),
m_fdc(*this,"fdc"),
m_fdc_timer(*this,"fdc_timer"),
m_hdc_timer(*this,"hdc_timer"),
m_disk_rom(*this,"disk"),
m_fd0(*this,"fdc:0"),
m_hd_buffer(*this,"hd_buffer_ram")
{
}
void ngen(machine_config &config);
protected:
DECLARE_READ8_MEMBER(hd_buffer_r);
DECLARE_WRITE8_MEMBER(hd_buffer_w);
DECLARE_WRITE_LINE_MEMBER(pit_out0_w);
DECLARE_WRITE_LINE_MEMBER(pit_out1_w);
DECLARE_WRITE_LINE_MEMBER(pit_out2_w);
DECLARE_WRITE_LINE_MEMBER(dma_hrq_changed);
DECLARE_WRITE_LINE_MEMBER(dma_eop_changed);
DECLARE_WRITE_LINE_MEMBER(dack0_w);
DECLARE_WRITE_LINE_MEMBER(dack1_w);
DECLARE_WRITE_LINE_MEMBER(dack2_w);
DECLARE_WRITE_LINE_MEMBER(dack3_w);
DECLARE_READ8_MEMBER(dma_read_word);
DECLARE_WRITE8_MEMBER(dma_write_word);
// TODO: sort out what devices use which channels
DECLARE_READ8_MEMBER( dma_0_dack_r ) { uint16_t ret = 0xffff; m_dma_high_byte = ret & 0xff00; return ret; }
DECLARE_READ8_MEMBER( dma_1_dack_r ) { uint16_t ret = 0xffff; m_dma_high_byte = ret & 0xff00; return ret; }
DECLARE_READ8_MEMBER( dma_2_dack_r ) { uint16_t ret = 0xffff; m_dma_high_byte = ret & 0xff00; return ret; }
DECLARE_READ8_MEMBER( dma_3_dack_r );
DECLARE_WRITE8_MEMBER( dma_0_dack_w ) { popmessage("IOW0: data %02x",data); }
DECLARE_WRITE8_MEMBER( dma_1_dack_w ) { }
DECLARE_WRITE8_MEMBER( dma_2_dack_w ) { }
DECLARE_WRITE8_MEMBER( dma_3_dack_w ) { popmessage("IOW3: data %02x",data); }
MC6845_UPDATE_ROW(crtc_update_row);
DECLARE_WRITE_LINE_MEMBER(timer_clk_out);
DECLARE_WRITE_LINE_MEMBER(fdc_irq_w);
void ngen386_io(address_map &map);
void ngen386_mem(address_map &map);
void ngen386i_mem(address_map &map);
optional_device<i80186_cpu_device> m_maincpu;
optional_device<i386_device> m_i386cpu;
required_device<mc6845_device> m_crtc;
required_device<i8251_device> m_viduart;
required_device<upd7201_device> m_iouart;
required_device<am9517a_device> m_dmac;
required_device<pic8259_device> m_pic;
required_device<pit8254_device> m_pit;
optional_device<wd2010_device> m_hdc;
optional_device<wd2797_device> m_fdc;
optional_device<pit8253_device> m_fdc_timer;
optional_device<pit8253_device> m_hdc_timer;
private:
DECLARE_WRITE16_MEMBER(cpu_peripheral_cb);
DECLARE_WRITE16_MEMBER(peripheral_w);
DECLARE_READ16_MEMBER(peripheral_r);
DECLARE_WRITE16_MEMBER(xbus_w);
DECLARE_READ16_MEMBER(xbus_r);
DECLARE_WRITE_LINE_MEMBER(cpu_timer_w);
DECLARE_WRITE16_MEMBER(hfd_w);
DECLARE_READ16_MEMBER(hfd_r);
DECLARE_WRITE_LINE_MEMBER(fdc_drq_w);
DECLARE_WRITE8_MEMBER(fdc_control_w);
DECLARE_READ8_MEMBER(irq_cb);
DECLARE_WRITE8_MEMBER(hdc_control_w);
DECLARE_WRITE8_MEMBER(disk_addr_ext);
DECLARE_READ16_MEMBER(b38_keyboard_r);
DECLARE_WRITE16_MEMBER(b38_keyboard_w);
DECLARE_READ16_MEMBER(b38_crtc_r);
DECLARE_WRITE16_MEMBER(b38_crtc_w);
void ngen_io(address_map &map);
void ngen_mem(address_map &map);
virtual void machine_reset() override;
virtual void machine_start() override;
optional_memory_region m_disk_rom;
memory_array m_vram;
memory_array m_fontram;
optional_device<floppy_connector> m_fd0;
optional_shared_ptr<uint8_t> m_hd_buffer;
void set_dma_channel(int channel, int state);
uint8_t m_xbus_current; // currently selected X-Bus module
uint16_t m_peripheral;
uint16_t m_upper;
uint16_t m_middle;
uint16_t m_port00;
uint16_t m_periph141;
uint8_t m_dma_offset[4];
int8_t m_dma_channel;
uint16_t m_dma_high_byte;
uint16_t m_control;
uint16_t m_disk_rom_ptr;
uint8_t m_hdc_control;
uint8_t m_disk_page;
};
class ngen386_state : public ngen_state
{
public:
ngen386_state(const machine_config &mconfig, device_type type, const char *tag)
: ngen_state(mconfig, type, tag)
{}
void ngen386(machine_config &config);
void _386i(machine_config &config);
private:
};
WRITE_LINE_MEMBER(ngen_state::pit_out0_w)
{
m_pic->ir3_w(state); // Timer interrupt
popmessage("PIT Timer 0 state %i\n",state);
}
WRITE_LINE_MEMBER(ngen_state::pit_out1_w)
{
popmessage("PIT Timer 1 state %i\n",state);
m_iouart->rxcb_w(state);
m_iouart->txcb_w(state); // channels in the correct order?
}
WRITE_LINE_MEMBER(ngen_state::pit_out2_w)
{
m_iouart->rxca_w(state);
m_iouart->txca_w(state);
popmessage("PIT Timer 2 state %i\n",state);
}
WRITE_LINE_MEMBER(ngen_state::cpu_timer_w)
{
if(state != 0)
popmessage("80186 Timer 0 state %i\n",state);
m_pic->ir5_w(state);
}
WRITE_LINE_MEMBER(ngen_state::timer_clk_out)
{
m_viduart->write_rxc(state); // Keyboard UART Rx/Tx clocks
m_viduart->write_txc(state);
// 80186 timer pins also? EXT bit is enabled for BTOS PIT test.
if(m_maincpu)
{
m_maincpu->tmrin0_w(state);
//m_maincpu->tmrin1_w(state);
}
}
WRITE16_MEMBER(ngen_state::cpu_peripheral_cb)
{
uint32_t addr;
switch(offset)
{
case 0: // upper memory
m_upper = data;
break;
case 2: // peripheral
m_peripheral = data;
addr = (m_peripheral & 0xffc0) << 4;
if(m_middle & 0x0040)
{
m_maincpu->space(AS_PROGRAM).install_readwrite_handler(addr, addr + 0x3ff, read16_delegate(FUNC(ngen_state::peripheral_r), this), write16_delegate(FUNC(ngen_state::peripheral_w), this));
logerror("Mapped peripherals to memory 0x%08x\n",addr);
}
else
{
addr &= 0xffff;
m_maincpu->space(AS_IO).install_readwrite_handler(addr, addr + 0x3ff, read16_delegate(FUNC(ngen_state::peripheral_r), this), write16_delegate(FUNC(ngen_state::peripheral_w), this));
logerror("Mapped peripherals to I/O 0x%04x\n",addr);
}
break;
case 4:
m_middle = data;
break;
}
}
// 80186 peripheral space
// Largely guesswork at this stage
WRITE16_MEMBER(ngen_state::peripheral_w)
{
switch(offset)
{
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
if(ACCESSING_BITS_0_7)
m_dmac->write(space,offset,data & 0xff);
break;
case 0x80: // DMA page offset?
case 0x81:
case 0x82:
case 0x83:
if(ACCESSING_BITS_0_7)
m_dma_offset[offset-0x80] = data & 0xff;
break;
case 0xc0: // X-Bus modules reset
m_xbus_current = 0;
break;
case 0x10c:
if(ACCESSING_BITS_0_7)
m_pic->write(0,data & 0xff);
break;
case 0x10d:
if(ACCESSING_BITS_0_7)
m_pic->write(1,data & 0xff);
break;
case 0x110:
case 0x111:
case 0x112:
case 0x113:
if(ACCESSING_BITS_0_7)
m_pit->write(offset-0x110,data & 0xff);
break;
case 0x141:
// bit 1 enables speaker?
COMBINE_DATA(&m_periph141);
break;
case 0x144:
if(ACCESSING_BITS_0_7)
m_crtc->address_w(space,0,data & 0xff);
break;
case 0x145:
if(ACCESSING_BITS_0_7)
m_crtc->register_w(space,0,data & 0xff);
break;
case 0x146:
case 0x147:
if(ACCESSING_BITS_0_7)
m_viduart->write(offset & 1, data & 0xff);
break;
case 0x1a0: // serial?
logerror("Serial(?) 0x1a0 write offset %04x data %04x mask %04x\n",offset,data,mem_mask);
break;
default:
logerror("Unknown 80186 peripheral write offset %04x data %04x mask %04x\n",offset,data,mem_mask);
}
}
READ16_MEMBER(ngen_state::peripheral_r)
{
uint16_t ret = 0xffff;
switch(offset)
{
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
if(ACCESSING_BITS_0_7)
ret = m_dmac->read(space,offset);
logerror("DMA read offset %04x mask %04x returning %04x\n",offset,mem_mask,ret);
break;
case 0x80: // DMA page offset?
case 0x81:
case 0x82:
case 0x83:
if(ACCESSING_BITS_0_7)
ret = m_dma_offset[offset-0x80] & 0xff;
break;
case 0x10c:
if(ACCESSING_BITS_0_7)
ret = m_pic->read(0);
break;
case 0x10d:
if(ACCESSING_BITS_0_7)
ret = m_pic->read(1);
break;
case 0x110:
case 0x111:
case 0x112:
case 0x113:
if(ACCESSING_BITS_0_7)
ret = m_pit->read(offset-0x110);
break;
case 0x141:
ret = m_periph141;
break;
case 0x144:
if(ACCESSING_BITS_0_7)
ret = m_crtc->status_r(space,0);
break;
case 0x145:
if(ACCESSING_BITS_0_7)
ret = m_crtc->register_r(space,0);
break;
case 0x146:
case 0x147: // keyboard UART
// status expects bit 0 to be set (UART transmit ready)
if(ACCESSING_BITS_0_7)
ret = m_viduart->read(offset & 1);
break;
case 0x1a0: // I/O control register?
ret = m_control; // end of DMA transfer? (maybe a per-channel EOP?) Bit 6 is set during a transfer?
break;
// default:
// logerror("Unknown 80186 peripheral read offset %04x mask %04x returning %04x\n",offset,mem_mask,ret);
}
return ret;
}
// X-bus module select
// The bootstrap ROM creates a table at 0:FC9h, with a count, followed by the module IDs of each
// expansion module. The base I/O address for the currently selected module is set by writing to
// this register (bits 0-7 are ignored)
// TODO: make expansion modules slot devices
WRITE16_MEMBER(ngen_state::xbus_w)
{
uint16_t addr = (data & 0x00ff) << 8;
cpu_device* cpu;
if(m_maincpu)
cpu = m_maincpu;
else
cpu = m_i386cpu;
address_space& io = cpu->space(AS_IO);
switch(m_xbus_current)
{
case 0x00: // Floppy/Hard disk module
io.install_readwrite_handler(addr,addr+0xff,read16_delegate(FUNC(ngen_state::hfd_r),this),write16_delegate(FUNC(ngen_state::hfd_w),this),0xffffffff);
break;
default:
cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); // reached end of the modules
break;
}
if(addr != 0)
logerror("SYS: X-Bus module %i address set %04x\n",m_xbus_current+1,addr);
m_xbus_current++;
}
// returns X-bus module ID and info in the low byte (can indicate if the device is bootable, has a boot ROM (needs to be written to RAM via DMA), or if it supports a non-80186 CPU)
// bit 6, I think, indicates a bootable device
// Known module IDs:
// 0x1070 - Floppy/Hard disk module
// 0x3141 - QIC Tape module
READ16_MEMBER(ngen_state::xbus_r)
{
uint16_t ret = 0xffff;
switch(m_xbus_current)
{
case 0x00:
ret = 0x1070; // Floppy/Hard disk module
break;
default:
if(m_maincpu)
m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); // reached the end of the modules
else
m_i386cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
ret = 0x0080;
break;
}
return ret;
}
// Floppy/Hard disk module
WRITE16_MEMBER(ngen_state::hfd_w)
{
switch(offset)
{
case 0x00:
case 0x01:
case 0x02:
if(ACCESSING_BITS_0_7)
m_fdc->write(offset,data & 0xff);
break;
case 0x03:
if(ACCESSING_BITS_0_7)
{
m_fdc->write(offset,data & 0xff);
m_fdc_timer->write_clk0(1);
m_fdc_timer->write_clk0(0); // Data register access clocks the FDC's PIT channel 0
}
break;
case 0x04:
if(ACCESSING_BITS_0_7)
fdc_control_w(space,0,data & 0xff);
break;
case 0x05:
if(ACCESSING_BITS_0_7)
hdc_control_w(space,0,data & 0xff);
break;
case 0x07:
if(ACCESSING_BITS_0_7)
disk_addr_ext(space,0,data & 0xff);
break;
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
if(ACCESSING_BITS_0_7)
m_fdc_timer->write(offset-0x08,data & 0xff);
break;
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
if(ACCESSING_BITS_0_7)
m_hdc->write(space,offset-0x10,data & 0xff);
logerror("WD1010 register %i write %02x mask %04x\n",offset-0x10,data & 0xff,mem_mask);
break;
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
if(ACCESSING_BITS_0_7)
m_hdc_timer->write(offset-0x18,data & 0xff);
break;
}
}
READ16_MEMBER(ngen_state::hfd_r)
{
uint16_t ret = 0xffff;
switch(offset)
{
case 0x00:
case 0x01:
case 0x02:
if(ACCESSING_BITS_0_7)
ret = m_fdc->read(offset);
break;
case 0x03:
if(ACCESSING_BITS_0_7)
{
ret = m_fdc->read(offset);
m_fdc_timer->write_clk0(1);
m_fdc_timer->write_clk0(0); // Data register access clocks the FDC's PIT channel 0
}
break;
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
if(ACCESSING_BITS_0_7)
ret = m_fdc_timer->read(offset-0x08);
break;
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
if(ACCESSING_BITS_0_7)
ret = m_hdc->read(space,offset-0x10);
logerror("WD1010 register %i read, mask %04x\n",offset-0x10,mem_mask);
break;
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
if(ACCESSING_BITS_0_7)
ret = m_hdc_timer->read(offset-0x18);
break;
}
return ret;
}
WRITE_LINE_MEMBER(ngen_state::fdc_irq_w)
{
m_pic->ir7_w(state);
}
WRITE_LINE_MEMBER(ngen_state::fdc_drq_w)
{
m_dmac->dreq3_w(state);
}
// Floppy disk control register
// Bit 0 - enable drive and LED
// Bit 2 - floppy motor
// Bit 5 - side select
// Bit 6 - 1 = 2Mhz for seek, 0 = 1MHz for read/write
// Bit 7 - FDC reset
WRITE8_MEMBER(ngen_state::fdc_control_w)
{
m_fdc->set_floppy(m_fd0->get_device());
m_fd0->get_device()->mon_w(~data & 0x04);
m_fd0->get_device()->ss_w(data & 0x20);
if(~data & 0x80)
m_fdc->soft_reset();
}
// Hard disk control register
// bit 0 - Drive select 0 - selects module hard disk
// bit 1 - Drive select 1 - selects expansion module hard disk (if available)
// bit 2 - enable DMA transfer of module ROM contents to X-Bus master memory
// bits 3-5 - select head / expansion module head
// bit 6 - write enable, must be set to write to a hard disk
// bit 7 - HDC reset
WRITE8_MEMBER(ngen_state::hdc_control_w)
{
m_hdc_control = data;
if(m_hdc_control & 0x04)
{
m_disk_rom_ptr = 0;
popmessage("HDD: DMA ROM transfer start\n");
m_dmac->dreq3_w(1);
//m_dmac->dreq3_w(0);
}
}
// page of system RAM to access
// bit 7 = disables read/write signals to the WD1010
WRITE8_MEMBER(ngen_state::disk_addr_ext)
{
m_disk_page = data & 0x7f;
}
READ8_MEMBER(ngen_state::hd_buffer_r)
{
return m_hd_buffer[offset];
}
WRITE8_MEMBER(ngen_state::hd_buffer_w)
{
m_hd_buffer[offset] = data;
}
WRITE_LINE_MEMBER( ngen_state::dma_hrq_changed )
{
if(m_maincpu)
m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
else
m_i386cpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
}
WRITE_LINE_MEMBER( ngen_state::dma_eop_changed )
{
if(m_dma_channel == 0)
{
if(state)
m_control |= 0x02;
else
m_control &= ~0x02;
}
if(m_dma_channel == 3)
{
if(state)
{
if(m_hdc_control & 0x04) // ROM transfer
m_hdc_control &= ~0x04; // switch it off when done
}
}
}
void ngen_state::set_dma_channel(int channel, int state)
{
if(!state)
m_dma_channel = channel;
else if(m_dma_channel == channel)
m_dma_channel = -1;
}
WRITE_LINE_MEMBER( ngen_state::dack0_w ) { set_dma_channel(0, state); }
WRITE_LINE_MEMBER( ngen_state::dack1_w ) { set_dma_channel(1, state); }
WRITE_LINE_MEMBER( ngen_state::dack2_w ) { set_dma_channel(2, state); }
WRITE_LINE_MEMBER( ngen_state::dack3_w ) { set_dma_channel(3, state); }
READ8_MEMBER(ngen_state::dma_3_dack_r)
{
uint16_t ret = 0xffff;
if((m_hdc_control & 0x04) && m_disk_rom)
{
ret = m_disk_rom->base()[m_disk_rom_ptr++] << 8;
printf("DMA3 DACK: returning %02x\n",ret);
if(m_disk_rom_ptr < 0x1000)
{
m_dmac->dreq3_w(1);
//m_dmac->dreq3_w(0);
}
}
m_dma_high_byte = ret & 0xff00;
return ret;
}
READ8_MEMBER(ngen_state::dma_read_word)
{
cpu_device* cpu;
uint16_t result;
if(m_maincpu)
cpu = m_maincpu;
else
cpu = m_i386cpu;
address_space& prog_space = cpu->space(AS_PROGRAM); // get the right address space
if(m_dma_channel == -1)
return 0xff;
offs_t page_offset = ((offs_t) m_dma_offset[m_dma_channel]) << 16;
result = prog_space.read_word((page_offset & 0xfe0000) | (offset << 1));
m_dma_high_byte = result & 0xFF00;
popmessage("DMA byte address %06x read %04x\n", (page_offset & 0xfe0000) | (offset << 1),result);
return result & 0xff;
}
WRITE8_MEMBER(ngen_state::dma_write_word)
{
cpu_device* cpu;
if(m_maincpu)
cpu = m_maincpu;
else
cpu = m_i386cpu;
address_space& prog_space = cpu->space(AS_PROGRAM); // get the right address space
if(m_dma_channel == -1)
return;
offs_t page_offset = ((offs_t) m_dma_offset[m_dma_channel]) << 16;
prog_space.write_word((page_offset & 0xfe0000) | (offset << 1), data);
popmessage("DMA byte address %06x write %04x\n", (page_offset & 0xfe0000) | (offset << 1), m_dma_high_byte | data);
}
MC6845_UPDATE_ROW( ngen_state::crtc_update_row )
{
uint16_t addr = ma;
for(int x=0;x<bitmap.width();x+=9)
{
uint8_t ch = m_vram.read16(addr++) & 0xff;
for(int z=0;z<9;z++)
{
if(BIT(m_fontram.read16(ch*16+ra),8-z))
bitmap.pix32(y,x+z) = rgb_t(0,0xff,0);
else
bitmap.pix32(y,x+z) = rgb_t(0,0,0);
}
}
}
READ8_MEMBER( ngen_state::irq_cb )
{
return m_pic->acknowledge();
}
READ16_MEMBER( ngen_state::b38_keyboard_r )
{
uint8_t ret = 0;
switch(offset)
{
case 0:
case 1: // keyboard UART
// status expects bit 0 to be set (UART transmit ready)
if(ACCESSING_BITS_0_7)
ret = m_viduart->read(offset & 1);
break;
}
return ret;
}
WRITE16_MEMBER( ngen_state::b38_keyboard_w )
{
switch(offset)
{
case 0:
case 1:
if(ACCESSING_BITS_0_7)
m_viduart->write(offset & 1, data & 0xff);
break;
}
}
READ16_MEMBER( ngen_state::b38_crtc_r )
{
uint8_t ret = 0;
switch(offset)
{
case 0:
if(ACCESSING_BITS_0_7)
ret = m_crtc->register_r(space,0);
break;
case 1:
if(ACCESSING_BITS_0_7)
ret = m_viduart->data_r();
break;
}
return ret;
}
WRITE16_MEMBER( ngen_state::b38_crtc_w )
{
switch(offset)
{
case 0:
if(ACCESSING_BITS_0_7)
m_crtc->address_w(space,0,data & 0xff);
break;
case 1:
if(ACCESSING_BITS_0_7)
m_crtc->register_w(space,0,data & 0xff);
break;
}
}
void ngen_state::machine_start()
{
memory_share* vidshare = memshare("vram");
memory_share* fontshare = memshare("fontram");
m_hd_buffer.allocate(1024*8); // 8kB buffer RAM for HD controller
if(vidshare == nullptr || fontshare == nullptr)
fatalerror("VRAM not found\n");
m_vram.set(*vidshare,2);
m_fontram.set(*fontshare,2);
}
void ngen_state::machine_reset()
{
m_port00 = 0;
m_control = 0;
m_xbus_current = 0;
m_viduart->write_dsr(0);
m_viduart->write_cts(0);
m_fd0->get_device()->set_rpm(300);
}
// boot ROMs from modules are not mapped anywhere, instead, they have to send the code from the boot ROM via DMA
void ngen_state::ngen_mem(address_map &map)
{
map(0x00000, 0xf7fff).ram();
map(0xf8000, 0xf9fff).ram().share("vram");
map(0xfa000, 0xfbfff).ram().share("fontram");
map(0xfc000, 0xfcfff).ram();
map(0xfe000, 0xfffff).rom().region("bios", 0);
}
void ngen_state::ngen_io(address_map &map)
{
map(0x0000, 0x0001).rw(FUNC(ngen_state::xbus_r), FUNC(ngen_state::xbus_w));
// Floppy/Hard disk module
// AM_RANGE(0x0100, 0x0107) AM_DEVREADWRITE8("fdc",wd2797_t,read,write,0x00ff) // a guess for now
// AM_RANGE(0x0108, 0x0109) AM_WRITE8(fdc_control_w,0x00ff)
// AM_RANGE(0x010a, 0x010b) AM_WRITE8(hdc_control_w,0x00ff)
// AM_RANGE(0x010e, 0x010f) AM_WRITE8(disk_addr_ext,0x00ff) // X-Bus extended address register
// AM_RANGE(0x0110, 0x0117) AM_DEVREADWRITE8("fdc_timer",pit8253_device,read,write,0x00ff)
// 0x0120-0x012f - WD1010 Winchester disk controller (unemulated)
// AM_RANGE(0x0130, 0x0137) AM_DEVREADWRITE8("hdc_timer",pit8253_device,read,write,0x00ff)
}
void ngen_state::ngen386_mem(address_map &map)
{
map(0x00000000, 0x000f7fff).ram();
map(0x000f8000, 0x000f9fff).ram().share("vram");
map(0x000fa000, 0x000fbfff).ram().share("fontram");
map(0x000fc000, 0x000fcfff).ram();
map(0x000fe000, 0x000fffff).rom().region("bios", 0);
map(0x00100000, 0x00ffffff).ram(); // some extra RAM
map(0xffffe000, 0xffffffff).rom().region("bios", 0);
}
void ngen_state::ngen386i_mem(address_map &map)
{
map(0x00000000, 0x000f7fff).ram();
map(0x000f8000, 0x000f9fff).ram().share("vram");
map(0x000fa000, 0x000fbfff).ram().share("fontram");
map(0x000fc000, 0x000fffff).rom().region("bios", 0);
map(0x00100000, 0x00ffffff).ram(); // some extra RAM
map(0xffffc000, 0xffffffff).rom().region("bios", 0);
}
void ngen_state::ngen386_io(address_map &map)
{
map(0x0000, 0x0001).rw(FUNC(ngen_state::xbus_r), FUNC(ngen_state::xbus_w));
// AM_RANGE(0xf800, 0xfeff) AM_READWRITE16(peripheral_r, peripheral_w,0xffffffff)
map(0xfd08, 0xfd0b).rw(FUNC(ngen_state::b38_crtc_r), FUNC(ngen_state::b38_crtc_w));
map(0xfd0c, 0xfd0f).rw(FUNC(ngen_state::b38_keyboard_r), FUNC(ngen_state::b38_keyboard_w));
}
static INPUT_PORTS_START( ngen )
INPUT_PORTS_END
static void keyboard(device_slot_interface &device)
{
device.option_add("ngen", NGEN_KEYBOARD);
}
static void ngen_floppies(device_slot_interface &device)
{
device.option_add("525qd", FLOPPY_525_QD);
}
MACHINE_CONFIG_START(ngen_state::ngen)
// basic machine hardware
MCFG_DEVICE_ADD("maincpu", I80186, 16_MHz_XTAL / 2)
MCFG_DEVICE_PROGRAM_MAP(ngen_mem)
MCFG_DEVICE_IO_MAP(ngen_io)
MCFG_80186_CHIP_SELECT_CB(WRITE16(*this, ngen_state, cpu_peripheral_cb))
MCFG_80186_TMROUT0_HANDLER(WRITELINE(*this, ngen_state, cpu_timer_w))
MCFG_80186_IRQ_SLAVE_ACK(READ8(*this, ngen_state, irq_cb))
PIC8259(config, m_pic, 0);
m_pic->out_int_callback().set(m_maincpu, FUNC(i80186_cpu_device::int0_w));
PIT8254(config, m_pit, 0);
m_pit->set_clk<0>(78120/4); // 19.53kHz, /4 of the CPU timer output?
m_pit->out_handler<0>().set(FUNC(ngen_state::pit_out0_w)); // RS232 channel B baud rate
m_pit->set_clk<1>(14.7456_MHz_XTAL / 12); // correct? - based on patent
m_pit->out_handler<1>().set(FUNC(ngen_state::pit_out1_w)); // RS232 channel A baud rate
m_pit->set_clk<2>(14.7456_MHz_XTAL / 12);
m_pit->out_handler<2>().set(FUNC(ngen_state::pit_out2_w));
AM9517A(config, m_dmac, 14.7456_MHz_XTAL / 3); // NEC D8237A, divisor unknown
m_dmac->out_hreq_callback().set(FUNC(ngen_state::dma_hrq_changed));
m_dmac->out_eop_callback().set(FUNC(ngen_state::dma_eop_changed));
m_dmac->in_memr_callback().set(FUNC(ngen_state::dma_read_word)); // DMA is always 16-bit
m_dmac->out_memw_callback().set(FUNC(ngen_state::dma_write_word));
m_dmac->out_dack_callback<0>().set(FUNC(ngen_state::dack0_w));
m_dmac->out_dack_callback<1>().set(FUNC(ngen_state::dack1_w));
m_dmac->out_dack_callback<2>().set(FUNC(ngen_state::dack2_w));
m_dmac->out_dack_callback<3>().set(FUNC(ngen_state::dack3_w));
m_dmac->in_ior_callback<0>().set(FUNC(ngen_state::dma_0_dack_r));
m_dmac->in_ior_callback<1>().set(FUNC(ngen_state::dma_1_dack_r));
m_dmac->in_ior_callback<2>().set(FUNC(ngen_state::dma_2_dack_r));
m_dmac->in_ior_callback<3>().set(FUNC(ngen_state::dma_3_dack_r));
m_dmac->out_iow_callback<0>().set(FUNC(ngen_state::dma_0_dack_w));
m_dmac->out_iow_callback<1>().set(FUNC(ngen_state::dma_1_dack_w));
m_dmac->out_iow_callback<2>().set(FUNC(ngen_state::dma_2_dack_w));
m_dmac->out_iow_callback<3>().set(FUNC(ngen_state::dma_3_dack_w));
// I/O board
UPD7201(config, m_iouart, 0); // clocked by PIT channel 2?
m_iouart->out_txda_callback().set("rs232_a", FUNC(rs232_port_device::write_txd));
m_iouart->out_txdb_callback().set("rs232_b", FUNC(rs232_port_device::write_txd));
m_iouart->out_dtra_callback().set("rs232_a", FUNC(rs232_port_device::write_dtr));
m_iouart->out_dtrb_callback().set("rs232_b", FUNC(rs232_port_device::write_dtr));
m_iouart->out_rtsa_callback().set("rs232_a", FUNC(rs232_port_device::write_rts));
m_iouart->out_rtsb_callback().set("rs232_b", FUNC(rs232_port_device::write_rts));
rs232_port_device &rs232a(RS232_PORT(config, "rs232_a", default_rs232_devices, nullptr));
rs232a.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxa_w));
rs232a.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsa_w));
rs232a.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcda_w));
rs232a.ri_handler().set(m_iouart, FUNC(upd7201_device::ria_w));
rs232_port_device &rs232b(RS232_PORT(config, "rs232_b", default_rs232_devices, nullptr));
rs232b.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxb_w));
rs232b.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsb_w));
rs232b.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcdb_w));
rs232b.ri_handler().set(m_iouart, FUNC(upd7201_device::rib_w));
// TODO: SCN2652 MPCC (not implemented), used for RS-422 cluster communications?
// video board
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_SIZE(720,348)
MCFG_SCREEN_VISIBLE_AREA(0,719,0,347)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_UPDATE_DEVICE("crtc",mc6845_device, screen_update)
MC6845(config, m_crtc, 19980000 / 9); // divisor unknown -- /9 gives 60Hz output, so likely correct
m_crtc->set_screen("screen");
m_crtc->set_show_border_area(false);
m_crtc->set_char_width(9);
m_crtc->set_update_row_callback(FUNC(ngen_state::crtc_update_row), this);
// keyboard UART (patent says i8251 is used for keyboard communications, it is located on the video board)
I8251(config, m_viduart, 0); // main clock unknown, Rx/Tx clocks are 19.53kHz
// m_viduart->txempty_handler().set(m_pic, FUNC(pic8259_device::ir4_w));
m_viduart->txd_handler().set("keyboard", FUNC(rs232_port_device::write_txd));
rs232_port_device &kbd(RS232_PORT(config, "keyboard", keyboard, "ngen"));
kbd.rxd_handler().set(m_viduart, FUNC(i8251_device::write_rxd));
MCFG_DEVICE_ADD("refresh_clock", CLOCK, 19200*16) // should be 19530Hz
MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE(*this, ngen_state,timer_clk_out))
// floppy disk / hard disk module (WD2797 FDC, WD1010 HDC, plus an 8253 timer for each)
WD2797(config, m_fdc, 20_MHz_XTAL / 20);
m_fdc->intrq_wr_callback().set(FUNC(ngen_state::fdc_irq_w));
m_fdc->drq_wr_callback().set(m_maincpu, FUNC(i80186_cpu_device::drq1_w));
m_fdc->set_force_ready(true);
PIT8253(config, m_fdc_timer, 0);
m_fdc_timer->set_clk<0>(0);
m_fdc_timer->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir5_w)); // clocked on FDC data register access
m_fdc_timer->set_clk<1>(20_MHz_XTAL / 20);
// m_fdc_timer->out_handler<1>().set(m_pic, FUNC(pic8259_device::ir5_w)); // 1MHz
m_fdc_timer->set_clk<2>(20_MHz_XTAL / 20);
// m_fdc_timer->out_handler<2>().set(m_pic, FUNC(pic8259_device::ir5_w));
// TODO: WD1010 HDC (not implemented), use WD2010 for now
WD2010(config, m_hdc, 20_MHz_XTAL / 4);
m_hdc->out_intrq_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
m_hdc->in_bcs_callback().set(FUNC(ngen_state::hd_buffer_r));
m_hdc->out_bcs_callback().set(FUNC(ngen_state::hd_buffer_w));
m_hdc->in_drdy_callback().set_constant(1);
m_hdc->in_index_callback().set_constant(1);
m_hdc->in_wf_callback().set_constant(1);
m_hdc->in_tk000_callback().set_constant(1);
m_hdc->in_sc_callback().set_constant(1);
PIT8253(config, m_hdc_timer, 0);
m_hdc_timer->set_clk<2>(20_MHz_XTAL / 10); // 2MHz
MCFG_FLOPPY_DRIVE_ADD("fdc:0", ngen_floppies, "525qd", floppy_image_device::default_floppy_formats)
MCFG_HARDDISK_ADD("hard0")
MACHINE_CONFIG_END
MACHINE_CONFIG_START(ngen386_state::ngen386)
MCFG_DEVICE_ADD("i386cpu", I386, 50_MHz_XTAL / 2)
MCFG_DEVICE_PROGRAM_MAP(ngen386_mem)
MCFG_DEVICE_IO_MAP(ngen386_io)
MCFG_DEVICE_IRQ_ACKNOWLEDGE_DEVICE("pic", pic8259_device, inta_cb)
PIC8259(config, m_pic, 0);
m_pic->out_int_callback().set_inputline(m_i386cpu, 0);
PIT8254(config, m_pit, 0);
m_pit->set_clk<0>(78120/4); // 19.53kHz, /4 of the CPU timer output?
m_pit->out_handler<0>().set(FUNC(ngen386_state::pit_out0_w)); // RS232 channel B baud rate
m_pit->set_clk<1>(14.7456_MHz_XTAL / 12); // correct? - based on patent
m_pit->out_handler<1>().set(FUNC(ngen386_state::pit_out1_w)); // RS232 channel A baud rate
m_pit->set_clk<2>(14.7456_MHz_XTAL / 12);
m_pit->out_handler<2>().set(FUNC(ngen386_state::pit_out2_w));
AM9517A(config, m_dmac, 14.7456_MHz_XTAL / 3); // NEC D8237A, divisor unknown
m_dmac->out_hreq_callback().set(FUNC(ngen386_state::dma_hrq_changed));
m_dmac->out_eop_callback().set(FUNC(ngen386_state::dma_eop_changed));
m_dmac->in_memr_callback().set(FUNC(ngen386_state::dma_read_word)); // DMA is always 16-bit
m_dmac->out_memw_callback().set(FUNC(ngen386_state::dma_write_word));
m_dmac->out_dack_callback<0>().set(FUNC(ngen386_state::dack0_w));
m_dmac->out_dack_callback<1>().set(FUNC(ngen386_state::dack1_w));
m_dmac->out_dack_callback<2>().set(FUNC(ngen386_state::dack2_w));
m_dmac->out_dack_callback<3>().set(FUNC(ngen386_state::dack3_w));
m_dmac->in_ior_callback<0>().set(FUNC(ngen386_state::dma_0_dack_r));
m_dmac->in_ior_callback<1>().set(FUNC(ngen386_state::dma_1_dack_r));
m_dmac->in_ior_callback<2>().set(FUNC(ngen386_state::dma_2_dack_r));
m_dmac->in_ior_callback<3>().set(FUNC(ngen386_state::dma_3_dack_r));
m_dmac->out_iow_callback<0>().set(FUNC(ngen386_state::dma_0_dack_w));
m_dmac->out_iow_callback<1>().set(FUNC(ngen386_state::dma_1_dack_w));
m_dmac->out_iow_callback<2>().set(FUNC(ngen386_state::dma_2_dack_w));
m_dmac->out_iow_callback<3>().set(FUNC(ngen386_state::dma_3_dack_w));
// I/O board
UPD7201(config, m_iouart, 0); // clocked by PIT channel 2?
m_iouart->out_txda_callback().set("rs232_a", FUNC(rs232_port_device::write_txd));
m_iouart->out_txdb_callback().set("rs232_b", FUNC(rs232_port_device::write_txd));
m_iouart->out_dtra_callback().set("rs232_a", FUNC(rs232_port_device::write_dtr));
m_iouart->out_dtrb_callback().set("rs232_b", FUNC(rs232_port_device::write_dtr));
m_iouart->out_rtsa_callback().set("rs232_a", FUNC(rs232_port_device::write_rts));
m_iouart->out_rtsb_callback().set("rs232_b", FUNC(rs232_port_device::write_rts));
rs232_port_device &rs232a(RS232_PORT(config, "rs232_a", default_rs232_devices, nullptr));
rs232a.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxa_w));
rs232a.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsa_w));
rs232a.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcda_w));
rs232a.ri_handler().set(m_iouart, FUNC(upd7201_device::ria_w));
rs232_port_device &rs232b(RS232_PORT(config, "rs232_b", default_rs232_devices, nullptr));
rs232b.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxb_w));
rs232b.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsb_w));
rs232b.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcdb_w));
rs232b.ri_handler().set(m_iouart, FUNC(upd7201_device::rib_w));
// TODO: SCN2652 MPCC (not implemented), used for RS-422 cluster communications?
// video board
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_SIZE(720,348)
MCFG_SCREEN_VISIBLE_AREA(0,719,0,347)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_UPDATE_DEVICE("crtc",mc6845_device, screen_update)
MC6845(config, m_crtc, 19980000 / 9); // divisor unknown -- /9 gives 60Hz output, so likely correct
m_crtc->set_screen("screen");
m_crtc->set_show_border_area(false);
m_crtc->set_char_width(9);
m_crtc->set_update_row_callback(FUNC(ngen386_state::crtc_update_row), this);
// keyboard UART (patent says i8251 is used for keyboard communications, it is located on the video board)
I8251(config, m_viduart, 0); // main clock unknown, Rx/Tx clocks are 19.53kHz
// m_viduart->txempty_handler().set("pic", FUNC(pic8259_device::ir4_w));
m_viduart->txd_handler().set("keyboard", FUNC(rs232_port_device::write_txd));
rs232_port_device &kbd(RS232_PORT(config, "keyboard", keyboard, "ngen"));
kbd.rxd_handler().set(m_viduart, FUNC(i8251_device::write_rxd));
MCFG_DEVICE_ADD("refresh_clock", CLOCK, 19200*16) // should be 19530Hz
MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE(*this, ngen386_state,timer_clk_out))
// floppy disk / hard disk module (WD2797 FDC, WD1010 HDC, plus an 8253 timer for each)
WD2797(config, m_fdc, 20_MHz_XTAL / 20);
m_fdc->intrq_wr_callback().set(FUNC(ngen386_state::fdc_irq_w));
//m_fdc->drq_wr_callback().set(m_i386cpu, FUNC(i80186_cpu_device_device::drq1_w));
m_fdc->set_force_ready(true);
PIT8253(config, m_fdc_timer, 0);
m_fdc_timer->set_clk<0>(0);
m_fdc_timer->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir5_w)); // clocked on FDC data register access
m_fdc_timer->set_clk<1>(20_MHz_XTAL / 20);
// m_fdc_timer->out_handler<1>().set(m_pic, FUNC(pic8259_device::ir5_w)); // 1MHz
m_fdc_timer->set_clk<2>(20_MHz_XTAL / 20);
// m_fdc_timer->out_handler<2>().set(m_pic, FUNC(pic8259_device::ir5_w));
// TODO: WD1010 HDC (not implemented), use WD2010 for now
WD2010(config, m_hdc, 20_MHz_XTAL / 4);
m_hdc->out_intrq_callback().set(m_pic, FUNC(pic8259_device::ir2_w));
m_hdc->in_bcs_callback().set(FUNC(ngen386_state::hd_buffer_r));
m_hdc->out_bcs_callback().set(FUNC(ngen386_state::hd_buffer_w));
m_hdc->in_drdy_callback().set_constant(1);
m_hdc->in_index_callback().set_constant(1);
m_hdc->in_wf_callback().set_constant(1);
m_hdc->in_tk000_callback().set_constant(1);
m_hdc->in_sc_callback().set_constant(1);
PIT8253(config, m_hdc_timer, 0);
m_hdc_timer->set_clk<2>(20_MHz_XTAL / 10); // 2MHz
MCFG_FLOPPY_DRIVE_ADD("fdc:0", ngen_floppies, "525qd", floppy_image_device::default_floppy_formats)
MCFG_HARDDISK_ADD("hard0")
MACHINE_CONFIG_END
MACHINE_CONFIG_START(ngen386_state::_386i)
ngen386(config);
MCFG_DEVICE_MODIFY("i386cpu")
MCFG_DEVICE_PROGRAM_MAP(ngen386i_mem)
MACHINE_CONFIG_END
ROM_START( ngen )
ROM_REGION( 0x2000, "bios", 0)
ROM_LOAD16_BYTE( "72-00414_80186_cpu.bin", 0x000000, 0x001000, CRC(e1387a03) SHA1(ddca4eba67fbf8b731a8009c14f6b40edcbc3279) ) // bootstrap ROM v8.4
ROM_LOAD16_BYTE( "72-00415_80186_cpu.bin", 0x000001, 0x001000, CRC(a6dde7d9) SHA1(b4d15c1bce31460ab5b92ff43a68c15ac5485816) )
ROM_REGION16_LE( 0x2000, "vram", ROMREGION_ERASE00 )
ROM_REGION16_LE( 0x2000, "fontram", ROMREGION_ERASE00 )
ROM_REGION( 0x1000, "disk", 0)
ROM_LOAD( "72-00422_10mb_disk.bin", 0x000000, 0x001000, CRC(f5b046b6) SHA1(b303c6f6aa40504016de9826879bc316e44389aa) )
ROM_REGION( 0x20, "disk_prom", 0)
ROM_LOAD( "72-00422_10mb_disk_15d.bin", 0x000000, 0x000020, CRC(121ee494) SHA1(9a8d3c336cc7378a71f9d48c99f88515eb236fbf) )
ROM_END
// not sure just how similar these systems are to the 80186 model, but are here at the moment to document the dumps
ROM_START( ngenb38 )
ROM_REGION( 0x2000, "bios", 0)
ROM_LOAD16_BYTE( "72-168_fpc_386_cpu.bin", 0x000000, 0x001000, CRC(250a3b68) SHA1(49c070514bac264fa4892f284f7d2c852ae6605d) )
ROM_LOAD16_BYTE( "72-167_fpc_386_cpu.bin", 0x000001, 0x001000, CRC(4010cc4e) SHA1(74a3024d605569056484d08b63f19fbf8eaf31c6) )
ROM_REGION16_LE( 0x2000, "vram", ROMREGION_ERASE00 )
ROM_REGION16_LE( 0x2000, "fontram", ROMREGION_ERASE00 )
ROM_END
ROM_START( 386i )
ROM_REGION( 0x4000, "bios", 0)
ROM_LOAD16_BYTE( "72-1561o_386i_cpu.bin", 0x000000, 0x002000, CRC(b5efd768) SHA1(8b250d47d9c6eb82e1afaeb2244d8c4134ecbc47) )
ROM_LOAD16_BYTE( "72-1562e_386i_cpu.bin", 0x000001, 0x002000, CRC(002d0d3a) SHA1(31de8592999377db9251acbeff348390a2d2602a) )
ROM_REGION16_LE( 0x2000, "vram", ROMREGION_ERASE00 )
ROM_REGION16_LE( 0x2000, "fontram", ROMREGION_ERASE00 )
ROM_REGION( 0x2000, "video", 0)
ROM_LOAD( "72-1630_gc-104_vga.bin", 0x000000, 0x002000, CRC(4e4d8ebe) SHA1(50c96ccb4d0bd1beb2d1aee0d18b2c462d25fc8f) )
ROM_END
COMP( 1983, ngen, 0, 0, ngen, ngen, ngen_state, empty_init, "Convergent Technologies", "NGEN CP-001", MACHINE_IS_SKELETON )
COMP( 1991, ngenb38, ngen, 0, ngen386, ngen, ngen386_state, empty_init, "Financial Products Corp.", "B28/38", MACHINE_IS_SKELETON )
COMP( 1990, 386i, ngen, 0, _386i, ngen, ngen386_state, empty_init, "Convergent Technologies", "386i", MACHINE_IS_SKELETON )