trunk/src/mess/drivers/vk100.c
| r17683 | r17684 | |
| 4 | 4 | |
| 5 | 5 | 12/05/2009 Skeleton driver. |
| 6 | 6 | 28/07/2009 added Guru-readme(TM) |
| 7 | 08/01/2012 Fleshed out driver. |
| 7 | 8 | |
| 8 | 9 | Todo: |
| 9 | 10 | * fix vector generator hardware enough to pass the startup self test |
| 10 | 11 | the tests are described on page 6-5 thru 6-8 of the tech reference |
| 11 | 12 | * hook up the direction and sync prom to the sync counter |
| 12 | 13 | * figure out the correct meaning of systat b register - needed for communications selftest |
| 13 | | * hook up smc_5016t baud generator to i8251 rx and tx clocks - begun |
| 14 | * hook up smc com5016t baud generator to i8251 rx and tx clocks - begun |
| 14 | 15 | |
| 15 | 16 | Tony DiCenzo, now the director of standards and architecture at Oracle, was on the team that developed the VK100 |
| 16 | 17 | see http://startup.nmnaturalhistory.org/visitorstories/view.php?ii=79 |
| r17683 | r17684 | |
| 154 | 155 | UINT8* m_trans; |
| 155 | 156 | UINT8* m_pattern; |
| 156 | 157 | UINT8* m_dir; |
| 158 | UINT8 m_vsync; // vsync pin of crtc |
| 157 | 159 | UINT16 m_vgX; |
| 158 | 160 | UINT16 m_vgY; |
| 159 | 161 | UINT16 m_vgERR; // error register can cause carries which need to be caught |
| r17683 | r17684 | |
| 163 | 165 | UINT8 m_vgPMUL; // reload value for PMUL_Count |
| 164 | 166 | UINT8 m_vgPMUL_Count; |
| 165 | 167 | UINT8 m_vgDownCount; // down counter = number of pixels, loaded from vgDU on execute |
| 166 | | UINT8 m_vgDU; |
| 167 | | UINT8 m_vgDVM; |
| 168 | | UINT8 m_vgDIR; |
| 169 | | UINT8 m_vgWOPS; |
| 168 | #define VG_DU m_regfile[0] |
| 169 | #define VG_DVM m_regfile[1] |
| 170 | #define VG_DIR m_regfile[2] |
| 171 | #define VG_WOPS m_regfile[3] |
| 172 | UINT8 m_regfile[4]; |
| 170 | 173 | UINT8 m_VG_MODE; // 2 bits, latched on EXEC |
| 171 | 174 | UINT8 m_vgGO; // activated on next SYNC pulse after EXEC |
| 172 | 175 | UINT8 m_ACTS; |
| r17683 | r17684 | |
| 243 | 246 | block |= data<<(nybbleNum*4); // write the new part |
| 244 | 247 | // NOTE: this next part may have to be made conditional on VG_MODE |
| 245 | 248 | // check if the attribute nybble is supposed to be modified, and if so do so |
| 246 | | if (state->m_vgWOPS&0x08) block = (block&0x0FFF)|(((UINT16)state->m_vgWOPS&0xF0)<<8); |
| 249 | if (state->VG_WOPS&0x08) block = (block&0x0FFF)|(((UINT16)state->VG_WOPS&0xF0)<<8); |
| 247 | 250 | state->m_vram[(EA<<1)+1] = block&0xFF; // write block back to vram |
| 248 | 251 | state->m_vram[(EA<<1)] = (block&0xFF00)>>8; // '' |
| 249 | 252 | } |
| r17683 | r17684 | |
| 253 | 256 | vk100_state *state = machine.driver_data<vk100_state>(); |
| 254 | 257 | UINT8 thisNyb = vram_read(machine); // read in the nybble |
| 255 | 258 | // pattern rom addressing is a complex mess. see the pattern rom def later in this file. |
| 256 | | 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 |
| 259 | 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 |
| 257 | 260 | // 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) |
| 258 | 261 | switch (state->m_VG_MODE) |
| 259 | 262 | { |
| r17683 | r17684 | |
| 305 | 308 | */ |
| 306 | 309 | //UINT8 direction_rom = state->m_dir[]; |
| 307 | 310 | // HACK: we need the proper direction rom dump for this! |
| 308 | | switch(state->m_vgDIR&0x7) |
| 311 | switch(state->VG_DIR&0x7) |
| 309 | 312 | { |
| 310 | 313 | case 0: |
| 311 | 314 | state->m_vgX++; |
| r17683 | r17684 | |
| 423 | 426 | /* port 0x60: "DU" load vg vector major register */ |
| 424 | 427 | WRITE8_MEMBER(vk100_state::vgDU) |
| 425 | 428 | { |
| 426 | | m_vgDU = data; |
| 429 | VG_DU = data; |
| 427 | 430 | #ifdef VG60_VERBOSE |
| 428 | | logerror("VG: 0x60: DU Reg loaded with %02X\n", m_vgDU); |
| 431 | logerror("VG: 0x60: DU Reg loaded with %02X\n", VG_DU); |
| 429 | 432 | #endif |
| 430 | 433 | } |
| 431 | 434 | |
| 432 | 435 | /* port 0x61: "DVM" load vg vector minor register */ |
| 433 | 436 | WRITE8_MEMBER(vk100_state::vgDVM) |
| 434 | 437 | { |
| 435 | | m_vgDVM = data; |
| 438 | VG_DVM = data; |
| 436 | 439 | #ifdef VG60_VERBOSE |
| 437 | | logerror("VG: 0x61: DVM Reg loaded with %02X\n", m_vgDVM); |
| 440 | logerror("VG: 0x61: DVM Reg loaded with %02X\n", VG_DVM); |
| 438 | 441 | #endif |
| 439 | 442 | } |
| 440 | 443 | |
| 441 | 444 | /* port 0x62: "DIR" load vg Direction register */ |
| 442 | 445 | WRITE8_MEMBER(vk100_state::vgDIR) |
| 443 | 446 | { |
| 444 | | m_vgDIR = data; |
| 447 | VG_DIR = data; |
| 445 | 448 | #ifdef VG60_VERBOSE |
| 446 | | logerror("VG: 0x62: DIR Reg loaded with %02X\n", m_vgDIR); |
| 449 | logerror("VG: 0x62: DIR Reg loaded with %02X\n", VG_DIR); |
| 447 | 450 | #endif |
| 448 | 451 | } |
| 449 | 452 | |
| r17683 | r17684 | |
| 455 | 458 | */ |
| 456 | 459 | WRITE8_MEMBER(vk100_state::vgWOPS) |
| 457 | 460 | { |
| 458 | | m_vgWOPS = data; |
| 461 | VG_WOPS = data; |
| 459 | 462 | #ifdef VG60_VERBOSE |
| 460 | 463 | static const char *const functions[] = { "Overlay", "Replace", "Complement", "Erase" }; |
| 461 | | 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); |
| 464 | 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); |
| 462 | 465 | #endif |
| 463 | 466 | } |
| 464 | 467 | |
| r17683 | r17684 | |
| 475 | 478 | #endif |
| 476 | 479 | m_vgPMUL_Count = m_vgPMUL; // load PMUL_Count |
| 477 | 480 | m_vgPAT_Mask = 0x80; |
| 478 | | m_vgDownCount = m_vgDU; // set down counter to length of major vector |
| 481 | m_vgDownCount = VG_DU; // set down counter to length of major vector |
| 479 | 482 | m_VG_MODE = offset&3; |
| 480 | 483 | m_vgGO = 1; |
| 481 | 484 | machine().scheduler().timer_set(attotime::zero, FUNC(execute_vg)); |
| r17683 | r17684 | |
| 501 | 504 | #endif |
| 502 | 505 | } |
| 503 | 506 | |
| 504 | | /* port 0x6C: "BAUD" controls the smc5016t dual baud generator which |
| 507 | /* port 0x6C: "BAUD" controls the smc com5016t dual baud generator which |
| 505 | 508 | * controls the divisors for the rx and tx clocks on the 8251 from the |
| 506 | | * 5.0688Mhz cpu xtal : |
| 509 | 5.0688Mhz cpu xtal. |
| 510 | It has 5v,12v on pins 2 and 9, pin 10 is NC. |
| 511 | * A later part that replaced this on the market is SMC COM8116(T)/8136(T), which |
| 512 | was a 5v-only part (pin 9 and 10 are NC, 10 is a clock out on the 8136. |
| 513 | * Note that even on the SMC COM5016T version, SMC would allow the user |
| 514 | to mask their own dividers on custom ordered chips if desired. |
| 515 | * The COM8116(T)/8136(T) came it at least 4 mask rom types meant for different |
| 516 | input clocks: |
| 517 | -000 or no mark for 5.0688Mhz (which exactly matches the table below) |
| 518 | -003 is for 6.01835MHz |
| 519 | -005 is for 4.915200Mhz |
| 520 | -006 is for 5.0688Mhz but omits the 2000 baud entry, instead has 200, |
| 521 | and output frequencies are 2x as fast (meant for a 32X clock uart) |
| 522 | -013 is for 2.76480MHz |
| 523 | -013A is for 5.52960MHz |
| 524 | (several other unknown refclock masks appear on partscalper sites) |
| 525 | GI also made a clone of the 8116 5v chip called the AY-5-8116(T)/8136(T) |
| 526 | which had at least two masks: -000/no mark and -005, matching speeds above |
| 527 | WD made the WD1943 which is similarly 5v compatible, with -00, -05, -06 masks |
| 528 | The COM8046(T) has 5 bits for selection instead of 4, but still expects |
| 529 | a 5.0688MHz reference clock, and the second half of the table matches the |
| 530 | values below; the first half of the table is the values below /2, rounded |
| 531 | down (for uarts which need a clock rate of 32x baud instead of 16x). |
| 532 | WD's BR1941 is also functionally compatible but uses 5v,12v,-5v on pins 2,9,10 |
| 507 | 533 | * 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 |
| 508 | 534 | * The others are based on page 13 of http://www.hartetechnologies.com/manuals/Tarbell/Tarbell%20Z80%20CPU%20Board%20Model%203033.pdf |
| 509 | 535 | * D C B A Divisor Expected Baud |
| r17683 | r17684 | |
| 537 | 563 | } |
| 538 | 564 | |
| 539 | 565 | /* port 0x40-0x47: "SYSTAT A"; various status bits, poorly documented in the tech manual |
| 540 | | * /GO BIT3 BIT2 BIT1 BIT0 Dip ? ? |
| 541 | | * Switch |
| 542 | | * d7 d6 d5 d4 d3 d2 d1 d0 |
| 543 | | * bit3, 2, 1, 0 are the last 4 bits read by the vector generator from VRAM |
| 544 | | * note: if the vector generator is not running, reading SYSTAT A will |
| 545 | | * supposedly FORCE the vector generator to read one nybble from the |
| 546 | | * current x,y! (how does this work schematicwise??? it may just read |
| 547 | | * the last nybble read by the constantly running sync counter, in |
| 548 | | * which case the hack here sort of works as well) |
| 549 | | * this appears to be the only way the vram can be READ by the cpu |
| 550 | | d2 is where the dipswitch values are read from, based on the offset |
| 551 | | d1 is unknown |
| 552 | | d0 is unknown |
| 566 | * /GO BIT3 BIT2 BIT1 BIT0 Dip RST7.5 GND |
| 567 | * Switch VSYNC |
| 568 | * d7 d6 d5 d4 d3 d2 d1 d0 |
| 569 | bit3, 2, 1, 0 are the 4 bits output from the VRAM 12->4 multiplexer |
| 570 | which are also inputs to the pattern rom; they are constantly updated |
| 571 | by the sync rom and related circuitry. |
| 572 | This is the only way the vram can be read by the cpu. |
| 573 | d7 is from the /Q output of the GO latch |
| 574 | d6,5,4,3 are from the 74ls298 at ic4 (right edge of pcb) |
| 575 | d2 is where the dipswitch values are read from, based on the offset |
| 576 | d1 is connected to 8085 rst7.5 (pin 7) and crtc pin 40 (VSYNC) [verified via tracing] |
| 577 | d0 is tied to GND [verified via tracing] |
| 553 | 578 | |
| 554 | 579 | 31D reads and checks d7 in a loop |
| 555 | 580 | 205 reads, xors with 0x55 (from reg D), ANDS result with 0x78 and branches if it is not zero (checking for bit pattern 1010?) |
| r17683 | r17684 | |
| 562 | 587 | #ifdef SYSTAT_A_VERBOSE |
| 563 | 588 | if (cpu_get_pc(m_maincpu) != 0x31D) logerror("0x%04X: SYSTAT_A Read!\n", cpu_get_pc(m_maincpu)); |
| 564 | 589 | #endif |
| 565 | | return ((m_vgGO?0:1)<<7)|(vram_read(machine())<<3)|(((ioport("SWITCHES")->read()>>dipswitchLUT[offset])&1)?0x4:0)|0x3; |
| 590 | return ((m_vgGO?0:1)<<7)|(vram_read(machine())<<3)|(((ioport("SWITCHES")->read()>>dipswitchLUT[offset])&1)?0x4:0)|(m_vsync?0x2:0); |
| 566 | 591 | } |
| 567 | 592 | |
| 568 | 593 | /* port 0x48: "SYSTAT B"; NOT documented in the tech manual at all. |
| r17683 | r17684 | |
| 853 | 878 | output_set_value("hardcopy_led", 1); |
| 854 | 879 | output_set_value("l1_led", 1); |
| 855 | 880 | output_set_value("l2_led", 1); |
| 881 | state->m_vsync = 0; |
| 856 | 882 | state->m_vgX = 0; |
| 857 | 883 | state->m_vgY = 0; |
| 858 | 884 | state->m_vgERR = 0; |
| r17683 | r17684 | |
| 862 | 888 | state->m_vgPMUL = 0; |
| 863 | 889 | state->m_vgPMUL_Count = 0; |
| 864 | 890 | state->m_vgDownCount = 0; |
| 865 | | state->m_vgDU = 0; |
| 866 | | state->m_vgDVM = 0; |
| 867 | | state->m_vgDIR = 0; |
| 868 | | state->m_vgWOPS = 0; |
| 891 | state->VG_DU = 0; |
| 892 | state->VG_DVM = 0; |
| 893 | state->VG_DIR = 0; |
| 894 | state->VG_WOPS = 0; |
| 869 | 895 | state->m_VG_MODE = 0; |
| 870 | 896 | state->m_vgGO = 0; |
| 871 | 897 | state->m_ACTS = 1; |
| r17683 | r17684 | |
| 873 | 899 | state->m_TXDivisor = 6336; |
| 874 | 900 | } |
| 875 | 901 | |
| 876 | | static INTERRUPT_GEN( vk100_vertical_interrupt ) |
| 902 | static WRITE_LINE_DEVICE_HANDLER(crtc_vsync) |
| 877 | 903 | { |
| 878 | | vk100_state *state = device->machine().driver_data<vk100_state>(); |
| 879 | | device_set_input_line(state->m_maincpu, I8085_RST75_LINE, ASSERT_LINE); |
| 880 | | device_set_input_line(state->m_maincpu, I8085_RST75_LINE, CLEAR_LINE); |
| 904 | vk100_state *m_state = device->machine().driver_data<vk100_state>(); |
| 905 | device_set_input_line(m_state->m_maincpu, I8085_RST75_LINE, state? ASSERT_LINE : CLEAR_LINE); |
| 906 | m_state->m_vsync = state; |
| 881 | 907 | } |
| 882 | 908 | |
| 883 | 909 | static WRITE_LINE_DEVICE_HANDLER(i8251_rxrdy_int) |
| r17683 | r17684 | |
| 958 | 984 | DEVCB_NULL, |
| 959 | 985 | DEVCB_NULL, |
| 960 | 986 | DEVCB_NULL, |
| 961 | | DEVCB_NULL, |
| 987 | DEVCB_LINE(crtc_vsync), |
| 962 | 988 | NULL |
| 963 | 989 | }; |
| 964 | 990 | |
| r17683 | r17684 | |
| 982 | 1008 | MCFG_CPU_ADD("maincpu", I8085A, XTAL_5_0688MHz) |
| 983 | 1009 | MCFG_CPU_PROGRAM_MAP(vk100_mem) |
| 984 | 1010 | MCFG_CPU_IO_MAP(vk100_io) |
| 985 | | MCFG_CPU_VBLANK_INT("screen", vk100_vertical_interrupt) |
| 986 | 1011 | |
| 987 | 1012 | MCFG_MACHINE_RESET(vk100) |
| 988 | 1013 | |