From f9903caa1a202b23a5ade79a3aa629a614da194a Mon Sep 17 00:00:00 2001 From: Jonathan Gevaryahu Date: Thu, 6 Sep 2012 20:08:05 +0000 Subject: [PATCH] (MESS) VK100: Correctly hooked up vsync interrupt to crtc instead of video subsystem; Figured out the low two bits of SYSTAT_A from tracing and hooked both up, and updated the SYSTAT_A documentation comments. Additional documentation comments for the SMC COM5016T baud rate divider. Made the DU/DVM/DIR/WOPS 8*4bit register file an actual 4-entry array, to simplify address decoding later. [Lord Nightmare] --- src/mess/drivers/vk100.c | 111 ++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 43 deletions(-) diff --git a/src/mess/drivers/vk100.c b/src/mess/drivers/vk100.c index 032f690921b..95adf03a910 100644 --- a/src/mess/drivers/vk100.c +++ b/src/mess/drivers/vk100.c @@ -4,13 +4,14 @@ 12/05/2009 Skeleton driver. 28/07/2009 added Guru-readme(TM) + 08/01/2012 Fleshed out driver. Todo: * fix vector generator hardware enough to pass the startup self test the tests are described on page 6-5 thru 6-8 of the tech reference * hook up the direction and sync prom to the sync counter * figure out the correct meaning of systat b register - needed for communications selftest - * hook up smc_5016t baud generator to i8251 rx and tx clocks - begun + * hook up smc com5016t baud generator to i8251 rx and tx clocks - begun Tony DiCenzo, now the director of standards and architecture at Oracle, was on the team that developed the VK100 see http://startup.nmnaturalhistory.org/visitorstories/view.php?ii=79 @@ -154,6 +155,7 @@ public: UINT8* m_trans; UINT8* m_pattern; UINT8* m_dir; + UINT8 m_vsync; // vsync pin of crtc UINT16 m_vgX; UINT16 m_vgY; UINT16 m_vgERR; // error register can cause carries which need to be caught @@ -163,10 +165,11 @@ public: UINT8 m_vgPMUL; // reload value for PMUL_Count UINT8 m_vgPMUL_Count; UINT8 m_vgDownCount; // down counter = number of pixels, loaded from vgDU on execute - UINT8 m_vgDU; - UINT8 m_vgDVM; - UINT8 m_vgDIR; - UINT8 m_vgWOPS; +#define VG_DU m_regfile[0] +#define VG_DVM m_regfile[1] +#define VG_DIR m_regfile[2] +#define VG_WOPS m_regfile[3] + UINT8 m_regfile[4]; UINT8 m_VG_MODE; // 2 bits, latched on EXEC UINT8 m_vgGO; // activated on next SYNC pulse after EXEC UINT8 m_ACTS; @@ -243,7 +246,7 @@ static void vram_write(running_machine &machine, UINT8 data) block |= data<<(nybbleNum*4); // write the new part // NOTE: this next part may have to be made conditional on VG_MODE // check if the attribute nybble is supposed to be modified, and if so do so - if (state->m_vgWOPS&0x08) block = (block&0x0FFF)|(((UINT16)state->m_vgWOPS&0xF0)<<8); + if (state->VG_WOPS&0x08) block = (block&0x0FFF)|(((UINT16)state->VG_WOPS&0xF0)<<8); state->m_vram[(EA<<1)+1] = block&0xFF; // write block back to vram state->m_vram[(EA<<1)] = (block&0xFF00)>>8; // '' } @@ -253,7 +256,7 @@ static TIMER_CALLBACK( execute_vg ) vk100_state *state = machine.driver_data(); UINT8 thisNyb = vram_read(machine); // read in the nybble // pattern rom addressing is a complex mess. see the pattern rom def later in this file. - UINT8 newNyb = state->m_pattern[((state->m_vgPAT&state->m_vgPAT_Mask)?0x200:0)|((state->m_vgWOPS&7)<<6)|((state->m_vgX&3)<<4)|thisNyb]; // calculate new nybble based on pattern rom + UINT8 newNyb = state->m_pattern[((state->m_vgPAT&state->m_vgPAT_Mask)?0x200:0)|((state->VG_WOPS&7)<<6)|((state->m_vgX&3)<<4)|thisNyb]; // calculate new nybble based on pattern rom // finally write the block back to ram depending on the VG_MODE (sort of a hack until we get the vector and synd and dir roms all hooked up) switch (state->m_VG_MODE) { @@ -305,7 +308,7 @@ static TIMER_CALLBACK( execute_vg ) */ //UINT8 direction_rom = state->m_dir[]; // HACK: we need the proper direction rom dump for this! - switch(state->m_vgDIR&0x7) + switch(state->VG_DIR&0x7) { case 0: state->m_vgX++; @@ -423,27 +426,27 @@ WRITE8_MEMBER(vk100_state::vgPMUL) /* port 0x60: "DU" load vg vector major register */ WRITE8_MEMBER(vk100_state::vgDU) { - m_vgDU = data; + VG_DU = data; #ifdef VG60_VERBOSE - logerror("VG: 0x60: DU Reg loaded with %02X\n", m_vgDU); + logerror("VG: 0x60: DU Reg loaded with %02X\n", VG_DU); #endif } /* port 0x61: "DVM" load vg vector minor register */ WRITE8_MEMBER(vk100_state::vgDVM) { - m_vgDVM = data; + VG_DVM = data; #ifdef VG60_VERBOSE - logerror("VG: 0x61: DVM Reg loaded with %02X\n", m_vgDVM); + logerror("VG: 0x61: DVM Reg loaded with %02X\n", VG_DVM); #endif } /* port 0x62: "DIR" load vg Direction register */ WRITE8_MEMBER(vk100_state::vgDIR) { - m_vgDIR = data; + VG_DIR = data; #ifdef VG60_VERBOSE - logerror("VG: 0x62: DIR Reg loaded with %02X\n", m_vgDIR); + logerror("VG: 0x62: DIR Reg loaded with %02X\n", VG_DIR); #endif } @@ -455,10 +458,10 @@ WRITE8_MEMBER(vk100_state::vgDIR) */ WRITE8_MEMBER(vk100_state::vgWOPS) { - m_vgWOPS = data; + VG_WOPS = data; #ifdef VG60_VERBOSE static const char *const functions[] = { "Overlay", "Replace", "Complement", "Erase" }; - logerror("VG: 0x64: WOPS Reg loaded with %02X: KGRB %d%d%d%d, AttrChange %d, Function %s, Negate %d\n", data, (m_vgWOPS>>7)&1, (m_vgWOPS>>6)&1, (m_vgWOPS>>5)&1, (m_vgWOPS>>4)&1, (m_vgWOPS>>3)&1, functions[(m_vgWOPS>>1)&3], m_vgWOPS&1); + logerror("VG: 0x64: WOPS Reg loaded with %02X: KGRB %d%d%d%d, AttrChange %d, Function %s, Negate %d\n", data, (VG_WOPS>>7)&1, (VG_WOPS>>6)&1, (VG_WOPS>>5)&1, (VG_WOPS>>4)&1, (VG_WOPS>>3)&1, functions[(VG_WOPS>>1)&3], VG_WOPS&1); #endif } @@ -475,7 +478,7 @@ WRITE8_MEMBER(vk100_state::vgEX) #endif m_vgPMUL_Count = m_vgPMUL; // load PMUL_Count m_vgPAT_Mask = 0x80; - m_vgDownCount = m_vgDU; // set down counter to length of major vector + m_vgDownCount = VG_DU; // set down counter to length of major vector m_VG_MODE = offset&3; m_vgGO = 1; machine().scheduler().timer_set(attotime::zero, FUNC(execute_vg)); @@ -501,9 +504,32 @@ WRITE8_MEMBER(vk100_state::KBDW) #endif } -/* port 0x6C: "BAUD" controls the smc5016t dual baud generator which +/* port 0x6C: "BAUD" controls the smc com5016t dual baud generator which * controls the divisors for the rx and tx clocks on the 8251 from the - * 5.0688Mhz cpu xtal : + 5.0688Mhz cpu xtal. + It has 5v,12v on pins 2 and 9, pin 10 is NC. + * A later part that replaced this on the market is SMC COM8116(T)/8136(T), which + was a 5v-only part (pin 9 and 10 are NC, 10 is a clock out on the 8136. + * Note that even on the SMC COM5016T version, SMC would allow the user + to mask their own dividers on custom ordered chips if desired. + * The COM8116(T)/8136(T) came it at least 4 mask rom types meant for different + input clocks: + -000 or no mark for 5.0688Mhz (which exactly matches the table below) + -003 is for 6.01835MHz + -005 is for 4.915200Mhz + -006 is for 5.0688Mhz but omits the 2000 baud entry, instead has 200, + and output frequencies are 2x as fast (meant for a 32X clock uart) + -013 is for 2.76480MHz + -013A is for 5.52960MHz + (several other unknown refclock masks appear on partscalper sites) + GI also made a clone of the 8116 5v chip called the AY-5-8116(T)/8136(T) + which had at least two masks: -000/no mark and -005, matching speeds above + WD made the WD1943 which is similarly 5v compatible, with -00, -05, -06 masks + The COM8046(T) has 5 bits for selection instead of 4, but still expects + a 5.0688MHz reference clock, and the second half of the table matches the + values below; the first half of the table is the values below /2, rounded + down (for uarts which need a clock rate of 32x baud instead of 16x). + WD's BR1941 is also functionally compatible but uses 5v,12v,-5v on pins 2,9,10 * The baud divisor lookup table has 16 entries, but only entries 2,5,6,7,A,C,E,F are documented/used in the vk100 tech manual * The others are based on page 13 of http://www.hartetechnologies.com/manuals/Tarbell/Tarbell%20Z80%20CPU%20Board%20Model%203033.pdf * D C B A Divisor Expected Baud @@ -537,19 +563,18 @@ WRITE8_MEMBER(vk100_state::BAUD) } /* port 0x40-0x47: "SYSTAT A"; various status bits, poorly documented in the tech manual - * /GO BIT3 BIT2 BIT1 BIT0 Dip ? ? - * Switch - * d7 d6 d5 d4 d3 d2 d1 d0 - * bit3, 2, 1, 0 are the last 4 bits read by the vector generator from VRAM - * note: if the vector generator is not running, reading SYSTAT A will - * supposedly FORCE the vector generator to read one nybble from the - * current x,y! (how does this work schematicwise??? it may just read - * the last nybble read by the constantly running sync counter, in - * which case the hack here sort of works as well) - * this appears to be the only way the vram can be READ by the cpu - d2 is where the dipswitch values are read from, based on the offset - d1 is unknown - d0 is unknown + * /GO BIT3 BIT2 BIT1 BIT0 Dip RST7.5 GND + * Switch VSYNC + * d7 d6 d5 d4 d3 d2 d1 d0 + bit3, 2, 1, 0 are the 4 bits output from the VRAM 12->4 multiplexer + which are also inputs to the pattern rom; they are constantly updated + by the sync rom and related circuitry. + This is the only way the vram can be read by the cpu. + d7 is from the /Q output of the GO latch + d6,5,4,3 are from the 74ls298 at ic4 (right edge of pcb) + d2 is where the dipswitch values are read from, based on the offset + d1 is connected to 8085 rst7.5 (pin 7) and crtc pin 40 (VSYNC) [verified via tracing] + d0 is tied to GND [verified via tracing] 31D reads and checks d7 in a loop 205 reads, xors with 0x55 (from reg D), ANDS result with 0x78 and branches if it is not zero (checking for bit pattern 1010?) @@ -562,7 +587,7 @@ READ8_MEMBER(vk100_state::SYSTAT_A) #ifdef SYSTAT_A_VERBOSE if (cpu_get_pc(m_maincpu) != 0x31D) logerror("0x%04X: SYSTAT_A Read!\n", cpu_get_pc(m_maincpu)); #endif - return ((m_vgGO?0:1)<<7)|(vram_read(machine())<<3)|(((ioport("SWITCHES")->read()>>dipswitchLUT[offset])&1)?0x4:0)|0x3; + return ((m_vgGO?0:1)<<7)|(vram_read(machine())<<3)|(((ioport("SWITCHES")->read()>>dipswitchLUT[offset])&1)?0x4:0)|(m_vsync?0x2:0); } /* port 0x48: "SYSTAT B"; NOT documented in the tech manual at all. @@ -853,6 +878,7 @@ static MACHINE_RESET( vk100 ) output_set_value("hardcopy_led", 1); output_set_value("l1_led", 1); output_set_value("l2_led", 1); + state->m_vsync = 0; state->m_vgX = 0; state->m_vgY = 0; state->m_vgERR = 0; @@ -862,10 +888,10 @@ static MACHINE_RESET( vk100 ) state->m_vgPMUL = 0; state->m_vgPMUL_Count = 0; state->m_vgDownCount = 0; - state->m_vgDU = 0; - state->m_vgDVM = 0; - state->m_vgDIR = 0; - state->m_vgWOPS = 0; + state->VG_DU = 0; + state->VG_DVM = 0; + state->VG_DIR = 0; + state->VG_WOPS = 0; state->m_VG_MODE = 0; state->m_vgGO = 0; state->m_ACTS = 1; @@ -873,11 +899,11 @@ static MACHINE_RESET( vk100 ) state->m_TXDivisor = 6336; } -static INTERRUPT_GEN( vk100_vertical_interrupt ) +static WRITE_LINE_DEVICE_HANDLER(crtc_vsync) { - vk100_state *state = device->machine().driver_data(); - device_set_input_line(state->m_maincpu, I8085_RST75_LINE, ASSERT_LINE); - device_set_input_line(state->m_maincpu, I8085_RST75_LINE, CLEAR_LINE); + vk100_state *m_state = device->machine().driver_data(); + device_set_input_line(m_state->m_maincpu, I8085_RST75_LINE, state? ASSERT_LINE : CLEAR_LINE); + m_state->m_vsync = state; } static WRITE_LINE_DEVICE_HANDLER(i8251_rxrdy_int) @@ -958,7 +984,7 @@ static const mc6845_interface mc6845_intf = DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, - DEVCB_NULL, + DEVCB_LINE(crtc_vsync), NULL }; @@ -982,7 +1008,6 @@ static MACHINE_CONFIG_START( vk100, vk100_state ) MCFG_CPU_ADD("maincpu", I8085A, XTAL_5_0688MHz) MCFG_CPU_PROGRAM_MAP(vk100_mem) MCFG_CPU_IO_MAP(vk100_io) - MCFG_CPU_VBLANK_INT("screen", vk100_vertical_interrupt) MCFG_MACHINE_RESET(vk100)