2 // Stargate Emulator (StarGem2) v2.0 SDL
5 // (C) 2006 Underground Software
7 // JLH = James L. Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 06/15/2006 Added changelog ;-)
12 // JLH 06/15/2006 Switched over to timeslice execution code
13 // JLH 07/15/2009 Solved problem with DEMO mode (IRQ handling)
40 #define SOUNDROM "ROMs/sg.snd"
41 #define CMOS "cmos.ram"
42 #define SAVESTATE "sg2.state"
46 uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
51 bool paletteDirty = false;
55 static bool running = true; // Machine running state flag...
56 static uint32 startTicks;
57 static uint8 * keys; // SDL raw keyboard matrix
58 static uint64 clockFrameStart; // V6809 clock at the start of the frame
60 // Local timer callback functions
62 static void FrameCallback(void);
63 static void ScanlineCallback(void);
67 // 6809 memory functions
71 char piaRegsName[4][10] = { "PORTA", "PACTL", "PORTB", "PBCTL" };
73 uint8 RdMem6809(uint16 addr)
77 if (addr >= 0x9000 && addr <= 0xCFFF) // No ROM between $9000 - $CFFF...
81 if (!gram[0xC900] && addr <= 0x8FFF) // Check RAM $C900 bank switch
87 // A wee kludge (though I doubt it reads from anywhere other than $CB00)...
88 if ((addr & 0xFF00) == 0xCB00)
90 b = gram[0xCB00] & 0xFC; // Only bits 2-7 are connected...
93 //Interesting, this code ALSO fucks up the demo...
94 //Except when the correct code is called in the scanline callback function...
95 uint32 elapsedCycles = (uint32)(GetCurrentV6809Clock() - clockFrameStart);
96 uint32 scanline = (uint32)(((double)elapsedCycles * M6809_CYCLE_IN_USEC) / 65.10416666666667);
97 //Changes here don't seem to do much...
98 // uint32 scanline = (uint32)(((double)elapsedCycles * M6809_CYCLE_IN_USEC) / 70.0);
99 b = (uint8)scanline & 0xFC; // Only bits 2-7 are connected...
104 if ((addr == 0xC80C) && (gram[0xC80D] & 0x04)) // Read PORTA and DDR is set to Output
106 ClearLine(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
107 //OK, this ALSO fucks up the execution of the demo...
108 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
111 if ((addr == 0xC80E) && (gram[0xC80F] & 0x04)) // Read PORTB and DDR is set to Output
113 ClearLine(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
114 //OK, this ALSO fucks up the execution of the demo...
115 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
120 //if (addr >= 0xC000 && addr <= 0xCBFF)
122 WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
124 /*if (addr >= 0xC80C && addr <= 0xC80F)
125 WriteLog("V6809 RdMem: Reading PIA (%s) address %04X [<-%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
130 void WrMem6809(uint16 addr, uint8 b)
133 //extern V6809REGS regs;
134 //if (addr >= 0xC800 && addr <= 0xCBFE)
135 //if (addr == 0xC80F || addr == 0xC80D)
136 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
137 //if (addr == 0xC80E)
138 /*if (addr >= 0xC800 && addr <= 0xC80F)
139 WriteLog("V6809 WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, mainCPU.pc, gram[0xCB00]);//*/
143 if (addr >= 0x0006 && addr < 0x97F7) // 304 pixels 152-128=24-16=8
145 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
146 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
148 if (sy > 5 && sy < 246)
150 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
151 scrBuffer[saddr + 0] = palette[color[b >> 4]];
152 scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
155 else if (addr >= 0xC000 && addr <= 0xC00F)
157 // A better strategy here would probably be to set a flag when the color register changes,
158 // then change it before doing the render.
159 // ALSO: This approach doesn't take the color to the edges of the screen
160 //#warning "This should only touch memory right before a render. !!! FIX !!!"
162 color[addr - 0xC000] = b;
165 for(uint32 addr=0x0006; addr<0x97F7; addr++)
167 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
169 if (sy > 5 && sy < 246)
171 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
172 uint8 sb = gram[addr];
174 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
175 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
182 else if (addr == 0xC80E)
184 sram[0x0402] = b; // Connect PIAs in 6809 & 6808
185 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ; // Start sound IRQ
189 //if (addr >= 0xC80C && addr <= 0xC80F)
191 WriteLog("V6809 WrMem: Writing PIA (%s) address %04X [->%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
196 // 6808 memory functions
199 uint8 RdMem6808(uint16 addr)
201 return (addr < 0xF000 ? sram[addr] : srom[addr]);
204 void WrMem6808(uint16 addr, uint8 b)
208 // A total guess, but let's try it...
209 //It probably depends on how the PIA is configured, so this is most likely wrong.
210 // It is wrong: IRQs are cleared on PIA PORTx reads!
211 // if (addr == 0x0401)
212 // soundCPU.cpuFlags &= ~V6808_ASSERT_LINE_IRQ;
216 // Load a file into RAM/ROM image space
218 bool LoadImg(const char * filename, uint8 * ram, int size)
220 FILE * fp = fopen(filename, "rb");
225 size_t ignoredResult = fread(ram, 1, size, fp);
236 FILE * fp = fopen(CMOS, "wb");
240 size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
244 WriteLog("CMOS RAM not saved!\n");
248 // Load state save file
250 bool LoadMachineState(void)
252 FILE * fp = fopen(SAVESTATE, "rb");
257 // This is kinda crappy--we don't do any sanity checking here!!!
258 size_t ignoredResult = fread(gram, 1, 0x10000, fp);
259 ignoredResult = fread(sram, 1, 0x10000, fp);
260 ignoredResult = fread(&mainCPU, 1, sizeof(V6809REGS), fp);
261 ignoredResult = fread(&soundCPU, 1, sizeof(V6808REGS), fp);
264 // for(int i=0x0006; i<0x97F8; i++) // Set up backbuffer... ;-)
265 // WrMem6809(i, gram[i]);
267 paletteDirty = true; // Set up backbuffer...
269 mainCPU.RdMem = RdMem6809; // Make sure our function pointers are
270 mainCPU.WrMem = WrMem6809; // pointing to the right places!
271 soundCPU.RdMem = RdMem6808;
272 soundCPU.WrMem = WrMem6808;
273 mainCPU.clock = 0; // Zero out our clocks...
280 // Save state save file
282 void SaveMachineState(void)
284 FILE * fp = fopen(SAVESTATE, "wb");
288 size_t ignoredResult = fwrite(gram, 1, 0x10000, fp);
289 ignoredResult = fwrite(sram, 1, 0x10000, fp);
290 ignoredResult = fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
291 ignoredResult = fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
295 WriteLog("Machine state not saved!\n");
301 int main(int /*argc*/, char * /*argv*/[])
303 InitLog("stargem2.log");
304 WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
305 WriteLog("(C) 2009 Underground Software\n\n");
309 // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
310 for(uint32 i=0; i<256; i++)
312 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
313 (((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151) << 24)
314 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 16)
315 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 8) | 0xFF;
317 ((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151)
318 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 8)
319 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 16) | 0xFF000000;
323 memset(gram, 0, 0x10000);
324 memset(grom, 0, 0x10000);
325 memset(sram, 0, 0x10000);
326 memset(srom, 0, 0x10000);
328 // Set up V6809 & V6808 execution contexts
330 memset(&mainCPU, 0, sizeof(V6809REGS));
331 mainCPU.RdMem = RdMem6809;
332 mainCPU.WrMem = WrMem6809;
333 mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
335 memset(&soundCPU, 0, sizeof(V6808REGS));
336 soundCPU.RdMem = RdMem6808;
337 soundCPU.WrMem = WrMem6808;
338 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
341 "ROMs/01", "ROMs/02", "ROMs/03", "ROMs/04", "ROMs/05", "ROMs/06",
342 "ROMs/07", "ROMs/08", "ROMs/09", "ROMs/10", "ROMs/11", "ROMs/12"
345 for(int i=0; i<12; i++)
347 uint32 baseAddress = i * 0x1000;
350 baseAddress += 0x4000;
352 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
354 WriteLog("Could not open file '%s'!\n", ROMs[i]);
359 if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
361 WriteLog("Could not open file '%s'!\n", SOUNDROM);
365 WriteLog("Stargate ROM images loaded...\n");
366 WriteLog("About to initialize video...\n");
370 cout << "Aborting!" << endl;
374 // Have to do this *after* video init but *before* sound init...!
375 WriteLog("About to load machine state...");
377 if (!LoadMachineState())
378 WriteLog("Machine state file not found!\n");
382 if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
383 WriteLog("CMOS RAM not found!\n");
385 WriteLog("About to intialize audio...\n");
387 keys = SDL_GetKeyState(NULL);
388 srom[0xF800] = 0x37; // Fix checksum so ST works...
389 running = true; // Set running status...
391 InitializeEventList(); // Clear the event list before we use it...
392 SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
393 // SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
394 SetCallbackTime(ScanlineCallback, 520.83333334/2.0); // Set scanline callback at 1/64 of frame
395 clockFrameStart = mainCPU.clock;
396 startTicks = SDL_GetTicks();
398 WriteLog("Entering main loop...\n");
402 double timeToNextEvent = GetTimeToNextEvent();
403 Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
404 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
405 //(Fix so that this is not a requirement!)
406 //Very odd... Execution seems to fuck up when using a 64-bit clock...
407 //(i.e., when this is commented out!)
408 // mainCPU.clock -= USEC_TO_M6809_CYCLES(timeToNextEvent);
414 WriteLog("$C900 = $%02X (0=RAM)\n", gram[0xC900]);
415 WriteLog("PC: %04X, X: %04X, Y: %04X, S: %04X, U: %04X, A: %02X, B: %02X, DP: %02X, CC: %02X\n", mainCPU.pc, mainCPU.x, mainCPU.y, mainCPU.s, mainCPU.u, mainCPU.a, mainCPU.b, mainCPU.dp, mainCPU.cc);
418 /*uint16 pc = mainCPU.pc;//0x15BA;
419 for(int i=0; i<200; i++)
420 //while (pc < 0x9000)
422 pc += Decode6809(pc);
429 pc += Decode6809(pc);
443 static void FrameCallback(void)
445 SDL_PumpEvents(); // Force key events into the buffer.
446 gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
448 if (keys[SDLK_ESCAPE])
449 running = false; // ESC to exit...
451 //Convert this stuff to use the settings module... !!! FIX !!!
452 if (keys[SDLK_SEMICOLON])
453 gram[0xC804] |= 0x01; // Fire (;)
455 gram[0xC804] |= 0x02; // Thrust (L)
456 if (keys[SDLK_SPACE])
457 gram[0xC804] |= 0x04; // Smart Bomb (space)
458 if (keys[SDLK_BACKSPACE])
459 gram[0xC804] |= 0x08; // Hyperspace (BkSp)
461 gram[0xC804] |= 0x10; // Two Player Start (2)
463 gram[0xC804] |= 0x20; // One Player Start (1)
464 if (keys[SDLK_RETURN])
465 gram[0xC804] |= 0x40; // Reverse (Enter)
467 gram[0xC804] |= 0x80; // Down (F)
470 gram[0xC806] |= 0x01; // Up (R)
472 gram[0xC806] |= 0x02; // Inviso (A)
475 gram[0xC80C] |= 0x01; // Auto up (F1)
477 gram[0xC80C] |= 0x02; // Advance (F2)
479 gram[0xC80C] |= 0x04; // Right Coin (5)
481 gram[0xC80C] |= 0x08; // High Score Reset (F3)
483 gram[0xC80C] |= 0x10; // Left Coin (3)
485 gram[0xC80C] |= 0x20; // Center Coin (4)
487 gram[0xC80C] |= 0x40; // Slam Switch (F4)
489 if (keys[SDLK_F5]) // Sound CPU self-test (F5)
490 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
491 if (keys[SDLK_F6]) // Reset the 6808 (F6)
492 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
496 for(uint32 addr=0x0006; addr<0x97F7; addr++)
498 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
500 if (sy > 5 && sy < 246)
502 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
503 uint8 sb = gram[addr];
505 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
506 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
510 paletteDirty = false;
513 RenderScreenBuffer(); // 1 frame = 1/60 sec ~ 16667 cycles
514 SetCallbackTime(FrameCallback, 16666.66666667);
515 clockFrameStart = mainCPU.clock;
517 // Wait for next frame...
518 while (SDL_GetTicks() - startTicks < 16)
521 startTicks = SDL_GetTicks();
524 static void ScanlineCallback(void)
527 if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
528 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
530 mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
532 if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
533 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
537 The problem is that this is already asserted above, by virtue of the fact that
538 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
539 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
540 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
542 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
544 // if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09)) // Do COUNT240 IRQ (if enabled!)
545 // mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
547 // This should set everything between $CB00-CBFF...
548 // gram[0xCB00] += 8; // Update video counter...
549 gram[0xCB00] += 4; // Update video counter...
551 // SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
552 SetCallbackTime(ScanlineCallback, 520.83333334/2.0); // Set scanline callback at 1/64 of frame
557 ; With correct timing, but no color cycling
559 --> Start of frame...
560 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
565 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
566 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
574 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
575 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
576 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
579 ; With incorrect timing, but has color cycling
581 --> Start of frame...
582 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
583 At $1609. $6E: 00 ; Color cycling...
588 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
589 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
590 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
591 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
599 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
600 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
601 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
608 0000-8FFF ROM (for Blaster, 0000-3FFF is a bank of 12 ROMs)
609 0000-97FF Video RAM Bank switched with ROM (96FF for Blaster)
611 0xBB00 Blaster only, Color 0 for each line (256 entry)
612 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
613 Do something else with the bit 1, I do not know what
617 C000-C00F color_registers (16 bytes of BBGGGRRR)
619 C804 widget_pia_dataa (widget = I/O board)
620 C805 widget_pia_ctrla
621 C806 widget_pia_datab
622 C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
623 controls if Table or Joust)
624 bits 5-3 = 110 = player 2
625 bits 5-3 = 111 = player 1
632 bit 2 |-6 bits to sound board
637 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
640 C900 rom_enable_scr_ctrl Switch between video ram and rom at 0000-97FF
644 C804 widget_pia_dataa (widget = I/O board)
654 C806 widget_pia_datab
662 bit 7 0 = Upright 1 = Table
667 bit 2 Right Coin (High Score Reset in schematics)
668 bit 3 High Score Reset (Left Coin in schematics)
669 bit 4 Left Coin (Center Coin in schematics)
670 bit 5 Center Coin (Right Coin in schematics)
672 bit 7 Hand Shake from sound board
678 static MEMORY_READ_START( williams_readmem )
679 { 0x0000, 0x97ff, MRA_BANK1 },
680 { 0x9800, 0xbfff, MRA_RAM },
681 { 0xc804, 0xc807, pia_0_r },
682 { 0xc80c, 0xc80f, pia_1_r },
683 { 0xcb00, 0xcb00, williams_video_counter_r },
684 { 0xcc00, 0xcfff, MRA_RAM },
685 { 0xd000, 0xffff, MRA_ROM },
689 static MEMORY_WRITE_START( williams_writemem )
690 { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
691 { 0x9800, 0xbfff, MWA_RAM },
692 { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
693 { 0xc804, 0xc807, pia_0_w },
694 { 0xc80c, 0xc80f, pia_1_w },
695 { 0xc900, 0xc900, williams_vram_select_w },
696 { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
697 { 0xcbff, 0xcbff, watchdog_reset_w },
698 { 0xcc00, 0xcfff, MWA_RAM },
699 { 0xd000, 0xffff, MWA_ROM },
702 static MEMORY_READ_START( sound_readmem )
703 { 0x0000, 0x007f, MRA_RAM },
704 { 0x0400, 0x0403, pia_2_r },
705 { 0x8400, 0x8403, pia_2_r }, // used by Colony 7, perhaps others?
706 { 0xb000, 0xffff, MRA_ROM }, // most games start at $F000, Sinistar starts at $B000
710 static MEMORY_WRITE_START( sound_writemem )
711 { 0x0000, 0x007f, MWA_RAM },
712 { 0x0400, 0x0403, pia_2_w },
713 { 0x8400, 0x8403, pia_2_w }, // used by Colony 7, perhaps others?
714 { 0xb000, 0xffff, MWA_ROM }, // most games start at $F000, Sinistar starts at $B000
717 MACHINE_INIT( williams )
722 // reset the ticket dispenser (Lotto Fun)
723 ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
725 // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
726 timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
728 // also set a timer to go off on scanline 240
729 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
733 static void williams_va11_callback(int scanline)
735 // the IRQ signal comes into CB1, and is set to VA11
736 pia_1_cb1_w(0, scanline & 0x20);
738 // update the screen while we're here
739 force_partial_update(scanline - 1);
741 // set a timer for the next update
743 if (scanline >= 256) scanline = 0;
744 timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
748 static void williams_count240_off_callback(int param)
750 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
755 static void williams_count240_callback(int param)
757 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
760 // set a timer to turn it off once the scanline counter resets
761 timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
763 // set a timer for next frame
764 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
768 static void williams_main_irq(int state)
770 // IRQ to the main CPU
771 cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
775 static void williams_main_firq(int state)
777 // FIRQ to the main CPU
778 cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
782 static void williams_snd_irq(int state)
784 // IRQ to the sound CPU
785 cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
789 READ_HANDLER( williams_video_counter_r )
791 return cpu_getscanline() & 0xFC;
795 // Special PIA 0 for Stargate, to handle the controls
796 struct pia6821_interface stargate_pia_0_intf =
798 //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
799 //outputs: A/B,CA/B2 / 0, 0, 0, 0,
803 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
804 struct pia6821_interface williams_pia_1_intf =
806 //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
807 //outputs: A/B,CA/B2 / 0, williams_snd_cmd_w, 0, 0,
808 //irqs : A/B / williams_main_irq, williams_main_irq
811 // Generic PIA 2, maps to DAC data in and sound IRQs
812 struct pia6821_interface williams_snd_pia_intf =
814 //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
815 //outputs: A/B,CA/B2 / DAC_0_data_w, 0, 0, 0,
816 //irqs : A/B / williams_snd_irq, williams_snd_irq
819 static DRIVER_INIT( stargate )
821 // CMOS configuration
822 CONFIGURE_CMOS(0xCC00, 0x400);
825 CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
829 int cpu_getscanline(void)
831 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
834 *************************************
836 * Returns time until given scanline
838 *************************************
840 double cpu_getscanlinetime(int scanline)
842 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
843 double abstime = timer_get_time();
846 // if we're already past the computed time, count it for the next frame
847 if (abstime >= scantime)
848 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
850 // compute how long from now until that time
851 result = scantime - abstime;
853 // if it's small, just count a whole frame
854 if (result < TIME_IN_NSEC(1))
855 result = TIME_IN_HZ(Machine->drv->frames_per_second);
859 *************************************
861 * Returns time for one scanline
863 *************************************
865 double cpu_getscanlineperiod(void)
867 return scanline_period;
871 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
872 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
873 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
875 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
876 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
877 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
879 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
880 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
881 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
882 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
884 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
885 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
886 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
887 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
889 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
890 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
891 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
892 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
894 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
895 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
896 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
897 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
899 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
900 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
901 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
902 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
904 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
905 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
906 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
908 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
909 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
910 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
911 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
918 bit 2 |-6 bits to sound board
923 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
926 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
931 00 -> $C80D = PIA2 -> DDR active
932 00 -> $C80C = PIA2 DDR -> All input?
940 #define PIA_IRQ1 (0x80)
941 #define PIA_IRQ2 (0x40)
943 #define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
944 #define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
945 #define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
946 #define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
947 #define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
948 #define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
949 #define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
950 #define C2_SET(c) ( (((c) >> 3) & 0x01))
951 #define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
952 #define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
953 #define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
954 #define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
955 #define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
956 #define C2_INPUT(c) (!(((c) >> 5) & 0x01))
958 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
960 pia6821_state *p = get_token(device);
962 /* limit the data to 0 or 1 */
963 data = data ? TRUE : FALSE;
965 LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
967 /* the new state has caused a transition */
968 if ((p->in_ca1 != data) &&
969 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
971 LOG(("PIA #%s: CA1 triggering\n", device->tag));
976 /* update externals */
977 update_interrupts(device);
979 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
980 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
981 set_out_ca2(device, TRUE);
984 /* set the new value for CA1 */
986 p->in_ca1_pushed = TRUE;
989 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
991 pia6821_state *p = get_token(device);
993 /* limit the data to 0 or 1 */
996 LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
998 /* the new state has caused a transition */
999 if ((p->in_cb1 != data) &&
1000 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
1002 LOG(("PIA #%s: CB1 triggering\n", device->tag));
1007 /* update externals */
1008 update_interrupts(device);
1010 /* If CB2 is configured as a write-strobe output which is reset by a CB1
1011 transition, this reset will only happen when a read from port B implicitly
1012 clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
1013 is different from what happens with port A. */
1016 /* set the new value for CB1 */
1018 p->in_cb1_pushed = TRUE;
1021 static void update_interrupts(const device_config *device)
1023 pia6821_state *p = get_token(device);
1026 /* start with IRQ A */
1027 new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1029 if (new_state != p->irq_a_state)
1031 p->irq_a_state = new_state;
1032 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1036 new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1038 if (new_state != p->irq_b_state)
1040 p->irq_b_state = new_state;
1041 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1045 static void control_b_w(const device_config *device, UINT8 data)
1047 pia6821_state *p = get_token(device);
1050 /* bit 7 and 6 are read only */
1053 LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1055 /* update the control register */
1058 if (C2_SET_MODE(p->ctl_b))
1059 /* set/reset mode - bit value determines the new output */
1060 temp = C2_SET(p->ctl_b);
1062 /* strobe mode - output is always high unless strobed */
1065 set_out_cb2(device, temp);
1067 /* update externals */
1068 update_interrupts(device);
1071 static void control_a_w(const device_config *device, UINT8 data)
1073 pia6821_state *p = get_token(device);
1075 /* bit 7 and 6 are read only */
1078 LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1080 /* update the control register */
1083 /* CA2 is configured as output */
1084 if (C2_OUTPUT(p->ctl_a))
1088 if (C2_SET_MODE(p->ctl_a))
1089 /* set/reset mode - bit value determines the new output */
1090 temp = C2_SET(p->ctl_a);
1092 /* strobe mode - output is always high unless strobed */
1095 set_out_ca2(device, temp);
1098 /* update externals */
1099 update_interrupts(device);
1105 B7 B6 B5 B4 B3 B2 B1 B0
1106 --------------------------------------------------
1107 IRQA(B)1 IRQA(B)2 CA(B)2 Ctrl DDR CA(B)1 Ctrl
1109 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1110 DDR: 0: DDR selected, 1: Output register selected
1111 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1112 B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1113 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1115 Entering main loop...
1116 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1117 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1118 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1119 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1120 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1121 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1122 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1123 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1124 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1125 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1126 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1127 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1128 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1129 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1130 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1131 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1132 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1133 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1134 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1135 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1136 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1137 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1138 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B] --> Clear IRQAs
1139 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9] --> Clear IRQAs
1141 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1142 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1143 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1144 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1145 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1146 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1147 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1148 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1149 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1150 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1151 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1152 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1153 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1154 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1155 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1156 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1157 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1158 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]