// license:GPL-2.0+ // copyright-holders:Brandon Munger /****************************************************************************** * * Rolm CBX 9751 Driver * * This driver attempts to emulate the following models: * * Model 10 * * Model 20 * * Model 40 * * Model 50 * * Model 70 * * The following software releases are known: * * 9004 * * 9005 * * The basis of this driver was influenced by the zexall.c driver by * Jonathan Gevaryahu and Robbbert. * * * Special Thanks to: * * Stephen Stair (sgstair) - for help with reverse engineering, * programming, and emulation * * Felipe Sanches (FSanches) - for help with MESS and emulation * * Michael Zapf (mizapf) - for building the HDC9234/HDC9224 * driver that makes this driver possible * * Memory map: * * 0x00000000 - 0x00ffffff : RAM 12MB to 16MB known, up to 128MB? * * 0x08000000 - 0x0800ffff : PROM Region * ******************************************************************************/ /* Core includes */ #include "emu.h" #include "cpu/m68000/m68000.h" #include "machine/terminal.h" #include "bus/scsi/scsi.h" #include "machine/wd33c93.h" #include "machine/pdc.h" #include "softlist.h" #define TERMINAL_TAG "terminal" /* Log defines */ #define TRACE_FDC 0 #define TRACE_HDC 0 #define TRACE_SMIOC 0 #define TRACE_CPU_REG 0 #define TRACE_LED 0 #define TRACE_DMA 0 class r9751_state : public driver_device { public: r9751_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_pdc(*this, "pdc"), m_wd33c93(*this, "wd33c93"), m_terminal(*this, TERMINAL_TAG), m_main_ram(*this, "main_ram") { } DECLARE_READ32_MEMBER(r9751_mmio_5ff_r); DECLARE_WRITE32_MEMBER(r9751_mmio_5ff_w); DECLARE_READ32_MEMBER(r9751_mmio_ff01_r); DECLARE_WRITE32_MEMBER(r9751_mmio_ff01_w); DECLARE_READ32_MEMBER(r9751_mmio_ff05_r); DECLARE_WRITE32_MEMBER(r9751_mmio_ff05_w); DECLARE_READ32_MEMBER(r9751_mmio_fff8_r); DECLARE_WRITE32_MEMBER(r9751_mmio_fff8_w); DECLARE_READ8_MEMBER(pdc_dma_r); DECLARE_WRITE8_MEMBER(pdc_dma_w); DECLARE_DRIVER_INIT(r9751); private: required_device m_maincpu; required_device m_pdc; required_device m_wd33c93; required_device m_terminal; required_shared_ptr m_main_ram; m68000_base_device* ptr_m68000; // Begin registers UINT32 reg_ff050004; UINT32 reg_fff80040; UINT32 fdd_dest_address; // 5FF080B0 UINT32 fdd_cmd_complete; UINT32 smioc_out_addr; UINT32 smioc_dma_bank; UINT32 fdd_dma_bank; attotime timer_32khz_last; // End registers address_space *m_mem; // functions UINT32 swap_uint32( UINT32 val ); UINT32 debug_a6(); UINT32 debug_a5(); UINT32 debug_a5_20(); virtual void machine_reset() override; }; UINT32 r9751_state::swap_uint32( UINT32 val ) { val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); return (val << 16) | (val >> 16); } UINT32 r9751_state::debug_a6() { return m_maincpu->space(AS_PROGRAM).read_dword(ptr_m68000->dar[14] + 4); } UINT32 r9751_state::debug_a5() { return m_maincpu->space(AS_PROGRAM).read_dword(ptr_m68000->dar[13]); } UINT32 r9751_state::debug_a5_20() { return m_maincpu->space(AS_PROGRAM).read_dword(ptr_m68000->dar[13] + 0x20); } READ8_MEMBER(r9751_state::pdc_dma_r) { /* This callback function takes the value written to 0xFF01000C as the bank offset */ UINT32 address = (fdd_dma_bank & 0x7FFFF800) + (offset&0x3FFFF); if(TRACE_DMA) logerror("DMA READ: %08X DATA: %08X\n", address, m_maincpu->space(AS_PROGRAM).read_byte(address)); return m_maincpu->space(AS_PROGRAM).read_byte(address); } WRITE8_MEMBER(r9751_state::pdc_dma_w) { /* This callback function takes the value written to 0xFF01000C as the bank offset */ UINT32 address = (fdd_dma_bank & 0x7FFFF800) + (m_pdc->fdd_68k_dma_address&0x3FFFF); m_maincpu->space(AS_PROGRAM).write_byte(address,data); if(TRACE_DMA) logerror("DMA WRITE: %08X DATA: %08X\n", address,data); } DRIVER_INIT_MEMBER(r9751_state,r9751) { reg_ff050004 = 0; reg_fff80040 = 0; fdd_dest_address = 0; fdd_cmd_complete = 0; fdd_dma_bank = 0; smioc_out_addr = 0; smioc_dma_bank = 0; m_mem = &m_maincpu->space(AS_PROGRAM); m_maincpu->interface(ptr_m68000); } void r9751_state::machine_reset() { UINT8 *rom = memregion("prom")->base(); UINT32 *ram = m_main_ram; memcpy(ram, rom, 8); m_maincpu->reset(); m_pdc->reset(); } /****************************************************************************** External board communication registers [0x5FF00000 - 0x5FFFFFFF] ******************************************************************************/ READ32_MEMBER( r9751_state::r9751_mmio_5ff_r ) { UINT32 data; switch(offset << 2) { /* PDC HDD region (0x24, device 9) */ case 0x0824: /* HDD Command result code */ return 0x10; case 0x3024: /* HDD SCSI command completed successfully */ data = 0x1; if(TRACE_HDC) logerror("SCSI HDD command completion status - Read: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); return data; /* SMIOC region (0x98, device 26) */ case 0x0898: /* Serial status or DMA status */ return 0x40; /* PDC FDD region (0xB0, device 44 */ case 0x08B0: /* FDD Command result code */ return 0x10; case 0x10B0: /* Clear 5FF030B0 ?? */ if(TRACE_FDC) logerror("--- FDD 0x5FF010B0 READ (0)\n"); return 0; case 0x30B0: /* FDD command completion status */ data = (m_pdc->reg_p5 << 8) + m_pdc->reg_p4; if(TRACE_FDC && data != 0) logerror("--- SCSI FDD command completion status - Read: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); return data; default: if(TRACE_FDC || TRACE_HDC || TRACE_SMIOC) logerror("Instruction: %08x READ MMIO(%08x): %08x & %08x\n", space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000, 0, mem_mask); return 0; } } WRITE32_MEMBER( r9751_state::r9751_mmio_5ff_w ) { UINT8 data_b0, data_b1; /* Unknown mask */ if (mem_mask != 0xFFFFFFFF) logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0x5FF00000, space.machine().firstcpu->pc()); switch(offset << 2) { /* PDC HDD region (0x24, device 9 */ case 0x0224: /* HDD SCSI read command */ if(TRACE_HDC) logerror("@@@ HDD Command: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); break; case 0x8024: /* HDD SCSI read command */ if(TRACE_HDC) logerror("@@@ HDD Command: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); break; case 0xC024: /* HDD SCSI read command */ if(TRACE_HDC) logerror("@@@ HDD Command: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); break; /* SMIOC region (0x98, device 26) */ case 0x4098: /* Serial DMA Command */ switch(data) { case 0x4100: /* Send byte to serial */ if(TRACE_SMIOC) logerror("Serial byte: %02X PC: %08X\n", m_mem->read_dword(smioc_out_addr), space.machine().firstcpu->pc()); m_terminal->write(space,0,m_mem->read_dword(smioc_out_addr)); break; default: if(TRACE_SMIOC) logerror("Unknown serial DMA command: %X\n", data); } break; case 0xC098: /* Serial DMA output address */ //smioc_out_addr = data * 2; smioc_out_addr = (smioc_dma_bank & 0x7FFFF800) | ((data&0x3FF)<<1); if(TRACE_SMIOC) logerror("Serial output address: %08X PC: %08X\n", smioc_out_addr, space.machine().firstcpu->pc()); break; /* PDC FDD region (0xB0, device 44 */ case 0x01B0: /* FDD SCSI read command */ if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); break; case 0x02B0: /* FDD SCSI read command */ if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); break; case 0x04B0: /* FDD RESET PDC */ if(TRACE_FDC) logerror("PDC RESET, PC: %08X DATA: %08X\n", space.machine().firstcpu->pc(),data); m_pdc->reset(); break; case 0x08B0: /* FDD SCSI read command */ if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); break; case 0x41B0: /* Unknown - Probably old style commands */ if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000); /* Clear FDD Command completion status 0x5FF030B0 (PDC 0x4, 0x5) */ m_pdc->reg_p4 = 0; m_pdc->reg_p5 = 0; data_b0 = data & 0xFF; data_b1 = (data & 0xFF00) >> 8; m_pdc->reg_p0 = data_b0; m_pdc->reg_p1 = data_b1; m_pdc->reg_p38 |= 0x2; /* Set bit 1 on port 38 register, PDC polls this port looking for a command */ if(TRACE_FDC) logerror("--- FDD Old Command: %02X and %02X\n", data_b0, data_b1); break; case 0xC0B0: case 0xC1B0: /* fdd_dest_address register */ fdd_dest_address = data << 1; if(TRACE_FDC) logerror("--- FDD destination address: %08X PC: %08X Register: %08X (A6+4): %08X\n", (fdd_dma_bank & 0x7FFFF800) + (fdd_dest_address&0x3FFFF), space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000, debug_a6()); data_b0 = data & 0xFF; data_b1 = (data & 0xFF00) >> 8; m_pdc->reg_p6 = data_b0; m_pdc->reg_p7 = data_b1; m_pdc->reg_p38 |= 0x2; // Set bit 1 on port 38 register, PDC polls this port looking for a command if(TRACE_FDC)logerror("--- FDD SET PDC Port 38: %X\n",m_pdc->reg_p38); break; case 0x80B0: /* FDD command address register */ UINT32 fdd_scsi_command; UINT32 fdd_scsi_command2; unsigned char c_fdd_scsi_command[8]; // Array for SCSI command int scsi_lba; // FDD LBA location here, extracted from command /* Clear FDD Command completion status 0x5FF030B0 (PDC 0x4, 0x5) */ m_pdc->reg_p4 = 0; m_pdc->reg_p5 = 0; /* Send FDD SCSI command location address to PDC 0x2, 0x3 */ if(TRACE_FDC) logerror("--- FDD command address: %08X PC: %08X Register: %08X (A6+4): %08X A4: %08X (A5): %08X (A5+20): %08X\n", (fdd_dma_bank & 0x7FFFF800) + ((data << 1)&0x3FFFF), space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000, debug_a6(), ptr_m68000->dar[12], debug_a5(), debug_a5_20()); data_b0 = data & 0xFF; data_b1 = (data & 0xFF00) >> 8; m_pdc->reg_p2 = data_b0; m_pdc->reg_p3 = data_b1; fdd_scsi_command = swap_uint32(m_mem->read_dword((fdd_dma_bank & 0x7FFFF800) + ((data << 1)&0x3FFFF))); fdd_scsi_command2 = swap_uint32(m_mem->read_dword(((fdd_dma_bank & 0x7FFFF800) + ((data << 1)&0x3FFFF))+4)); memcpy(c_fdd_scsi_command,&fdd_scsi_command,4); memcpy(c_fdd_scsi_command+4,&fdd_scsi_command2,4); if(TRACE_FDC) { logerror("--- FDD SCSI Command: "); for(int i = 0; i < 8; i++) logerror("%02X ", c_fdd_scsi_command[i]); logerror("\n"); } scsi_lba = c_fdd_scsi_command[3] | (c_fdd_scsi_command[2]<<8) | ((c_fdd_scsi_command[1]&0x1F)<<16); if(TRACE_FDC) logerror("--- FDD SCSI LBA: %i\n", scsi_lba); break; default: if(TRACE_FDC || TRACE_HDC || TRACE_SMIOC) logerror("Instruction: %08x WRITE MMIO(%08x): %08x & %08x (A6+4): %08X\n", space.machine().firstcpu->pc(), offset << 2 | 0x5FF00000, data, mem_mask, debug_a6()); } } /****************************************************************************** CPU board registers [0xFF010000 - 0xFF06FFFF] ******************************************************************************/ READ32_MEMBER( r9751_state::r9751_mmio_ff01_r ) { //UINT32 data; switch(offset << 2) { default: //return data; return 0; } } WRITE32_MEMBER( r9751_state::r9751_mmio_ff01_w ) { /* Unknown mask */ if (mem_mask != 0xFFFFFFFF) logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFF010000, space.machine().firstcpu->pc()); switch(offset << 2) { case 0x000C: /* FDD DMA Offset */ fdd_dma_bank = data; if(TRACE_DMA) logerror("Banking register(FDD): %08X PC: %08X Data: %08X\n", offset << 2 | 0xFF010000, space.machine().firstcpu->pc(), data); return; case 0x0010: /* SMIOC DMA Offset */ smioc_dma_bank = data; if(TRACE_DMA) logerror("Banking register(SMIOC): %08X PC: %08X Data: %08X\n", offset << 2 | 0xFF010000, space.machine().firstcpu->pc(), data); return; default: if(TRACE_DMA) logerror("Banking register(Unknown): %08X PC: %08X Data: %08X\n", offset << 2 | 0xFF010000, space.machine().firstcpu->pc(), data); return; } } READ32_MEMBER( r9751_state::r9751_mmio_ff05_r ) { UINT32 data; switch(offset << 2) { case 0x0004: return reg_ff050004; case 0x0300: return 0x1B | (1<<0x14); case 0x0320: /* Some type of counter */ return (machine().time() - timer_32khz_last).as_ticks(32768) & 0xFFFF; case 0x0584: return 0; case 0x0610: return 0xabacabac; case 0x0014: return 0x80; default: data = 0; if(TRACE_CPU_REG) logerror("Instruction: %08x READ MMIO(%08x): %08x & %08x\n", space.machine().firstcpu->pc(), offset << 2 | 0xFF050000, data, mem_mask); return data; } } WRITE32_MEMBER( r9751_state::r9751_mmio_ff05_w ) { /* Unknown mask */ if (mem_mask != 0xFFFFFFFF) logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFF050000, space.machine().firstcpu->pc()); switch(offset << 2) { case 0x0004: reg_ff050004 = data; return; case 0x000C: /* CPU LED hex display indicator */ if(TRACE_LED) logerror("\n*** LED: %02x, Instruction: %08x ***\n\n", data, space.machine().firstcpu->pc()); return; case 0x0320: timer_32khz_last = machine().time(); return; default: if(TRACE_CPU_REG) logerror("Instruction: %08x WRITE MMIO(%08x): %08x & %08x\n", space.machine().firstcpu->pc(), offset << 2 | 0xFF050000, data, mem_mask); return; } } READ32_MEMBER( r9751_state::r9751_mmio_fff8_r ) { UINT32 data; switch(offset << 2) { case 0x0040: return reg_fff80040; default: data = 0; if(TRACE_CPU_REG) logerror("Instruction: %08x READ MMIO(%08x): %08x & %08x\n", space.machine().firstcpu->pc(), offset << 2 | 0xFFF80000, data, mem_mask); return data; } } WRITE32_MEMBER( r9751_state::r9751_mmio_fff8_w ) { /* Unknown mask */ if (mem_mask != 0xFFFFFFFF) logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFFF80000, space.machine().firstcpu->pc()); switch(offset << 2) { case 0x0040: reg_fff80040 = data; return; default: if(TRACE_CPU_REG) logerror("Instruction: %08x WRITE MMIO(%08x): %08x & %08x\n", space.machine().firstcpu->pc(), offset << 2 | 0xFFF80000, data, mem_mask); } } /****************************************************************************** Address Maps ******************************************************************************/ static ADDRESS_MAP_START(r9751_mem, AS_PROGRAM, 32, r9751_state) //ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x00000000,0x00ffffff) AM_RAM AM_SHARE("main_ram") // 16MB AM_RANGE(0x08000000,0x0800ffff) AM_ROM AM_REGION("prom", 0) AM_RANGE(0x5FF00000,0x5FFFFFFF) AM_READWRITE(r9751_mmio_5ff_r, r9751_mmio_5ff_w) AM_RANGE(0xFF010000,0xFF01FFFF) AM_READWRITE(r9751_mmio_ff01_r, r9751_mmio_ff01_w) AM_RANGE(0xFF050000,0xFF06FFFF) AM_READWRITE(r9751_mmio_ff05_r, r9751_mmio_ff05_w) AM_RANGE(0xFFF80000,0xFFF8FFFF) AM_READWRITE(r9751_mmio_fff8_r, r9751_mmio_fff8_w) //AM_RANGE(0xffffff00,0xffffffff) AM_RAM // Unknown area ADDRESS_MAP_END /****************************************************************************** Input Ports ******************************************************************************/ static INPUT_PORTS_START( r9751 ) INPUT_PORTS_END /****************************************************************************** Machine Drivers ******************************************************************************/ static MACHINE_CONFIG_START( r9751, r9751_state ) /* basic machine hardware */ MCFG_CPU_ADD("maincpu", M68030, 20000000) MCFG_CPU_PROGRAM_MAP(r9751_mem) MCFG_QUANTUM_TIME(attotime::from_hz(60)) /* video hardware */ MCFG_DEVICE_ADD(TERMINAL_TAG, GENERIC_TERMINAL, 0) /* disk hardware */ MCFG_DEVICE_ADD("pdc", PDC, 0) MCFG_PDC_R_CB(READ8(r9751_state, pdc_dma_r)) MCFG_PDC_W_CB(WRITE8(r9751_state, pdc_dma_w)) MCFG_DEVICE_ADD("scsi", SCSI_PORT, 0) //MCFG_SCSIDEV_ADD("scsi:" SCSI_PORT_DEVICE1, "cdrom", SCSICD, SCSI_ID_1) MCFG_DEVICE_ADD("wd33c93", WD33C93, 0) MCFG_LEGACY_SCSI_PORT("scsi") //MCFG_WD33C93_IRQ_CB(WRITELINE(r9751_state,scsi_irq)) /* software list */ MCFG_SOFTWARE_LIST_ADD("flop_list","r9751") MACHINE_CONFIG_END /****************************************************************************** ROM Definitions ******************************************************************************/ ROM_START(r9751) ROM_REGION32_BE(0x00010000, "prom", 0) ROM_SYSTEM_BIOS(0, "prom34", "PROM Version 3.4") ROMX_LOAD( "p-n_98d4643__abaco_v3.4__(49fe7a)__j221.27512.bin", 0x0000, 0x10000, CRC(9fb19a85) SHA1(c861e15a2fc9a4ef689c2034c53fbb36f17f7da6), ROM_GROUPWORD | ROM_BIOS(1) ) // Label: "P/N 98D4643 // ABACO V3.4 // (49FE7A) // J221" 27128 @Unknown ROM_END /****************************************************************************** Drivers ******************************************************************************/ /* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */ COMP( 1988, r9751, 0, 0, r9751, r9751, r9751_state, r9751, "ROLM Systems, Inc.", "ROLM 9751 Model 10", MACHINE_NO_SOUND | MACHINE_NOT_WORKING )