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"
44 #define FRAME_DURATION_IN_CYCLES (M6809_CLOCK_SPEED_IN_HZ / 60.0)
45 #define SCANLINE_DURATION_IN_CYCLES (FRAME_DURATION_IN_CYCLES / 256.0)
46 // Interesting... This (1/16) causes the machine to crash in the demo (if run from clean start, otherwise it FUs demo)!
47 // (1/32) fucks up the demo...
48 // (1/64) works. Weird!
49 //#define SG2_PIA_CALLBACK_DURATION ((FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC) / 16.0)
50 #define SG2_PIA_CALLBACK_DURATION ((FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC) / 64.0)
54 uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
59 bool paletteDirty = false;
63 static bool running = true; // Machine running state flag...
64 static uint32 startTicks;
65 static uint8 * keys; // SDL raw keyboard matrix
66 static uint64 clockFrameStart; // V6809 clock at the start of the frame
68 // Function prototypes
70 uint8 RdMem6809(uint16 addr);
71 void WrMem6809(uint16 addr, uint8 b);
72 uint8 RdMem6808(uint16 addr);
73 void WrMem6808(uint16 addr, uint8 b);
74 bool LoadImg(const char * filename, uint8 * ram, int size);
76 bool LoadMachineState(void);
77 void SaveMachineState(void);
79 // Local timer callback functions
81 static void FrameCallback(void);
82 static void ScanlineCallback(void);
88 int main(int /*argc*/, char * /*argv*/[])
90 InitLog("stargem2.log");
91 WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
92 WriteLog("(C) 2009 Underground Software\n\n");
96 // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
97 for(uint32 i=0; i<256; i++)
99 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
100 (((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151) << 24)
101 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 16)
102 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 8) | 0xFF;
104 ((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151)
105 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 8)
106 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 16) | 0xFF000000;
110 memset(gram, 0, 0x10000);
111 memset(grom, 0, 0x10000);
112 memset(sram, 0, 0x10000);
113 memset(srom, 0, 0x10000);
115 // Set up V6809 & V6808 execution contexts
117 memset(&mainCPU, 0, sizeof(V6809REGS));
118 mainCPU.RdMem = RdMem6809;
119 mainCPU.WrMem = WrMem6809;
120 mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
122 memset(&soundCPU, 0, sizeof(V6808REGS));
123 soundCPU.RdMem = RdMem6808;
124 soundCPU.WrMem = WrMem6808;
125 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
128 "ROMs/01", "ROMs/02", "ROMs/03", "ROMs/04", "ROMs/05", "ROMs/06",
129 "ROMs/07", "ROMs/08", "ROMs/09", "ROMs/10", "ROMs/11", "ROMs/12"
132 for(int i=0; i<12; i++)
134 uint32 baseAddress = i * 0x1000;
137 baseAddress += 0x4000;
139 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
141 WriteLog("Could not open file '%s'!\n", ROMs[i]);
146 if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
148 WriteLog("Could not open file '%s'!\n", SOUNDROM);
152 WriteLog("Stargate ROM images loaded...\n");
153 WriteLog("About to initialize video...\n");
157 cout << "Aborting!" << endl;
161 // Have to do this *after* video init but *before* sound init...!
162 WriteLog("About to load machine state...");
164 if (!LoadMachineState())
165 WriteLog("Machine state file not found!\n");
169 if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
170 WriteLog("CMOS RAM not found!\n");
172 WriteLog("About to intialize audio...\n");
174 keys = SDL_GetKeyState(NULL);
175 srom[0xF800] = 0x37; // Fix checksum so ST works...
176 running = true; // Set running status...
178 InitializeEventList(); // Clear the event list before we use it...
179 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
180 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
181 clockFrameStart = mainCPU.clock;
182 startTicks = SDL_GetTicks();
184 WriteLog("Entering main loop...\n");
188 double timeToNextEvent = GetTimeToNextEvent();
189 Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
195 WriteLog("$C900 = $%02X (0=RAM)\n", gram[0xC900]);
196 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);
199 /*uint16 pc = mainCPU.pc;//0x15BA;
200 for(int i=0; i<200; i++)
201 //while (pc < 0x9000)
203 pc += Decode6809(pc);
210 pc += Decode6809(pc);
225 // Load a file into RAM/ROM image space
227 bool LoadImg(const char * filename, uint8 * ram, int size)
229 FILE * fp = fopen(filename, "rb");
234 size_t ignoredResult = fread(ram, 1, size, fp);
245 FILE * fp = fopen(CMOS, "wb");
249 size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
253 WriteLog("CMOS RAM not saved!\n");
257 // Load state save file
259 bool LoadMachineState(void)
261 FILE * fp = fopen(SAVESTATE, "rb");
266 // This is kinda crappy--we don't do any sanity checking here!!!
267 size_t ignoredResult = fread(gram, 1, 0x10000, fp);
268 ignoredResult = fread(sram, 1, 0x10000, fp);
269 ignoredResult = fread(&mainCPU, 1, sizeof(V6809REGS), fp);
270 ignoredResult = fread(&soundCPU, 1, sizeof(V6808REGS), fp);
273 // Set up backbuffer... ;-)
274 for(uint16 i=0x0006; i<0x97F8; i++) // Screen memory
275 WrMem6809(i, gram[i]);
277 for(uint16 i=0xC000; i<=0xC00F; i++) // Palette memory
278 WrMem6809(i, gram[i]);
282 mainCPU.RdMem = RdMem6809; // Make sure our function pointers are
283 mainCPU.WrMem = WrMem6809; // pointing to the right places!
284 soundCPU.RdMem = RdMem6808;
285 soundCPU.WrMem = WrMem6808;
286 mainCPU.clock = 0; // Zero out our clocks...
288 mainCPU.clockOverrun = 0; // And overrun values...
289 //notyet soundCPU.clockOverrun = 0;
295 // Save state save file
297 void SaveMachineState(void)
299 FILE * fp = fopen(SAVESTATE, "wb");
303 size_t ignoredResult = fwrite(gram, 1, 0x10000, fp);
304 ignoredResult = fwrite(sram, 1, 0x10000, fp);
305 ignoredResult = fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
306 ignoredResult = fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
310 WriteLog("Machine state not saved!\n");
314 // 6809 memory functions
318 char piaRegsName[4][10] = { "PORTA", "PACTL", "PORTB", "PBCTL" };
320 uint8 RdMem6809(uint16 addr)
324 if (addr >= 0x9000 && addr <= 0xCFFF) // No ROM between $9000 - $CFFF...
328 if (!gram[0xC900] && addr <= 0x8FFF) // Check RAM $C900 bank switch
334 // A wee kludge (though I doubt it reads from anywhere other than $CB00)...
335 if ((addr & 0xFF00) == 0xCB00)
337 b = gram[0xCB00] & 0xFC; // Only bits 2-7 are connected...
340 //Interesting, this code ALSO fucks up the demo...
341 //Except when the correct code is called in the scanline callback function...
342 uint32 elapsedCycles = (uint32)(GetCurrentV6809Clock() - clockFrameStart);
343 uint32 scanline = (uint32)((double)elapsedCycles / SCANLINE_DURATION_IN_CYCLES);
344 //Changes here don't seem to do much...
345 // uint32 scanline = (uint32)(((double)elapsedCycles * M6809_CYCLE_IN_USEC) / 70.0);
346 b = (uint8)scanline & 0xFC; // Only bits 2-7 are connected...
351 if ((addr == 0xC80C) && (gram[0xC80D] & 0x04)) // Read PORTA and DDR is set to Output
353 ClearLine(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
354 //OK, this ALSO fucks up the execution of the demo...
355 // Which means that the timing is still off. :-/
356 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
359 if ((addr == 0xC80E) && (gram[0xC80F] & 0x04)) // Read PORTB and DDR is set to Output
361 ClearLine(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
362 //OK, this ALSO fucks up the execution of the demo...
363 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
368 //if (addr >= 0xC000 && addr <= 0xCBFF)
370 WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
372 /*if (addr >= 0xC80C && addr <= 0xC80F)
373 WriteLog("V6809 RdMem: Reading PIA (%s) address %04X [<-%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
378 void WrMem6809(uint16 addr, uint8 b)
381 //extern V6809REGS regs;
382 //if (addr >= 0xC800 && addr <= 0xCBFE)
383 //if (addr == 0xC80F || addr == 0xC80D)
384 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
385 //if (addr == 0xC80E)
386 /*if (addr >= 0xC800 && addr <= 0xC80F)
387 WriteLog("V6809 WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, mainCPU.pc, gram[0xCB00]);//*/
391 if (addr >= 0x0006 && addr < 0x97F7) // 304 pixels 152-128=24-16=8
393 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
394 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
396 if (sy > 5 && sy < 246)
398 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
399 scrBuffer[saddr + 0] = palette[color[b >> 4]];
400 scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
403 else if (addr >= 0xC000 && addr <= 0xC00F)
405 // This approach doesn't take the BG color to the edges of the screen
406 color[addr - 0xC000] = b;
409 else if (addr == 0xC80E)
411 sram[0x0402] = b; // Connect PIAs in 6809 & 6808
412 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ; // Start sound IRQ
416 //if (addr >= 0xC80C && addr <= 0xC80F)
418 WriteLog("V6809 WrMem: Writing PIA (%s) address %04X [->%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
423 // 6808 memory functions
426 uint8 RdMem6808(uint16 addr)
428 return (addr < 0xF000 ? sram[addr] : srom[addr]);
431 void WrMem6808(uint16 addr, uint8 b)
435 // A total guess, but let's try it...
436 //It probably depends on how the PIA is configured, so this is most likely wrong.
437 // It is wrong: IRQs are cleared on PIA PORTx reads!
438 // if (addr == 0x0401)
439 // soundCPU.cpuFlags &= ~V6808_ASSERT_LINE_IRQ;
442 static void FrameCallback(void)
444 SDL_PumpEvents(); // Force key events into the buffer.
445 gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
447 if (keys[SDLK_ESCAPE])
448 running = false; // ESC to exit...
450 //Convert this stuff to use the settings module... !!! FIX !!!
451 if (keys[SDLK_SEMICOLON])
452 gram[0xC804] |= 0x01; // Fire (;)
454 gram[0xC804] |= 0x02; // Thrust (L)
455 if (keys[SDLK_SPACE])
456 gram[0xC804] |= 0x04; // Smart Bomb (space)
457 if (keys[SDLK_BACKSPACE])
458 gram[0xC804] |= 0x08; // Hyperspace (BkSp)
460 gram[0xC804] |= 0x10; // Two Player Start (2)
462 gram[0xC804] |= 0x20; // One Player Start (1)
463 if (keys[SDLK_RETURN])
464 gram[0xC804] |= 0x40; // Reverse (Enter)
466 gram[0xC804] |= 0x80; // Down (F)
469 gram[0xC806] |= 0x01; // Up (R)
471 gram[0xC806] |= 0x02; // Inviso (A)
474 gram[0xC80C] |= 0x01; // Auto up (F1)
476 gram[0xC80C] |= 0x02; // Advance (F2)
478 gram[0xC80C] |= 0x04; // Right Coin (5)
480 gram[0xC80C] |= 0x08; // High Score Reset (F3)
482 gram[0xC80C] |= 0x10; // Left Coin (3)
484 gram[0xC80C] |= 0x20; // Center Coin (4)
486 gram[0xC80C] |= 0x40; // Slam Switch (F4)
488 if (keys[SDLK_F5]) // Sound CPU self-test (F5)
489 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
490 if (keys[SDLK_F6]) // Reset the 6808 (F6)
491 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
495 for(uint32 addr=0x0006; addr<0x97F7; addr++)
497 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
499 if (sy > 5 && sy < 246)
501 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
502 uint8 sb = gram[addr];
504 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
505 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
509 paletteDirty = false;
512 RenderScreenBuffer(); // 1 frame = 1/60 sec ~ 16667 cycles
513 clockFrameStart = mainCPU.clock;
515 // Wait for next frame...
516 while (SDL_GetTicks() - startTicks < 16)
519 startTicks = SDL_GetTicks();
520 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
523 static void ScanlineCallback(void)
526 if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
527 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
529 mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
531 if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
532 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
536 The problem is that this is already asserted above, by virtue of the fact that
537 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
538 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
539 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
541 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
543 // if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09)) // Do COUNT240 IRQ (if enabled!)
544 // mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
546 // This should set everything between $CB00-CBFF...
547 // gram[0xCB00] += 8; // Update video counter...
548 // gram[0xCB00] += 4; // Update video counter...
550 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
555 ; With correct timing, but no color cycling
557 --> Start of frame...
558 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
563 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
564 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
572 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
573 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
574 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
577 ; With incorrect timing, but has color cycling
579 --> Start of frame...
580 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
581 At $1609. $6E: 00 ; Color cycling...
586 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
587 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
588 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
589 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
597 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
598 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
599 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
606 0000-8FFF ROM (for Blaster, 0000-3FFF is a bank of 12 ROMs)
607 0000-97FF Video RAM Bank switched with ROM (96FF for Blaster)
609 0xBB00 Blaster only, Color 0 for each line (256 entry)
610 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
611 Do something else with the bit 1, I do not know what
615 C000-C00F color_registers (16 bytes of BBGGGRRR)
617 C804 widget_pia_dataa (widget = I/O board)
618 C805 widget_pia_ctrla
619 C806 widget_pia_datab
620 C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
621 controls if Table or Joust)
622 bits 5-3 = 110 = player 2
623 bits 5-3 = 111 = player 1
630 bit 2 |-6 bits to sound board
635 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
638 C900 rom_enable_scr_ctrl Switch between video ram and rom at 0000-97FF
642 C804 widget_pia_dataa (widget = I/O board)
652 C806 widget_pia_datab
660 bit 7 0 = Upright 1 = Table
665 bit 2 Right Coin (High Score Reset in schematics)
666 bit 3 High Score Reset (Left Coin in schematics)
667 bit 4 Left Coin (Center Coin in schematics)
668 bit 5 Center Coin (Right Coin in schematics)
670 bit 7 Hand Shake from sound board
676 static MEMORY_READ_START( williams_readmem )
677 { 0x0000, 0x97ff, MRA_BANK1 },
678 { 0x9800, 0xbfff, MRA_RAM },
679 { 0xc804, 0xc807, pia_0_r },
680 { 0xc80c, 0xc80f, pia_1_r },
681 { 0xcb00, 0xcb00, williams_video_counter_r },
682 { 0xcc00, 0xcfff, MRA_RAM },
683 { 0xd000, 0xffff, MRA_ROM },
687 static MEMORY_WRITE_START( williams_writemem )
688 { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
689 { 0x9800, 0xbfff, MWA_RAM },
690 { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
691 { 0xc804, 0xc807, pia_0_w },
692 { 0xc80c, 0xc80f, pia_1_w },
693 { 0xc900, 0xc900, williams_vram_select_w },
694 { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
695 { 0xcbff, 0xcbff, watchdog_reset_w },
696 { 0xcc00, 0xcfff, MWA_RAM },
697 { 0xd000, 0xffff, MWA_ROM },
700 static MEMORY_READ_START( sound_readmem )
701 { 0x0000, 0x007f, MRA_RAM },
702 { 0x0400, 0x0403, pia_2_r },
703 { 0x8400, 0x8403, pia_2_r }, // used by Colony 7, perhaps others?
704 { 0xb000, 0xffff, MRA_ROM }, // most games start at $F000, Sinistar starts at $B000
708 static MEMORY_WRITE_START( sound_writemem )
709 { 0x0000, 0x007f, MWA_RAM },
710 { 0x0400, 0x0403, pia_2_w },
711 { 0x8400, 0x8403, pia_2_w }, // used by Colony 7, perhaps others?
712 { 0xb000, 0xffff, MWA_ROM }, // most games start at $F000, Sinistar starts at $B000
715 MACHINE_INIT( williams )
720 // reset the ticket dispenser (Lotto Fun)
721 ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
723 // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
724 timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
726 // also set a timer to go off on scanline 240
727 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
731 static void williams_va11_callback(int scanline)
733 // the IRQ signal comes into CB1, and is set to VA11
734 pia_1_cb1_w(0, scanline & 0x20);
736 // update the screen while we're here
737 force_partial_update(scanline - 1);
739 // set a timer for the next update
741 if (scanline >= 256) scanline = 0;
742 timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
746 static void williams_count240_off_callback(int param)
748 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
753 static void williams_count240_callback(int param)
755 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
758 // set a timer to turn it off once the scanline counter resets
759 timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
761 // set a timer for next frame
762 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
766 static void williams_main_irq(int state)
768 // IRQ to the main CPU
769 cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
773 static void williams_main_firq(int state)
775 // FIRQ to the main CPU
776 cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
780 static void williams_snd_irq(int state)
782 // IRQ to the sound CPU
783 cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
787 READ_HANDLER( williams_video_counter_r )
789 return cpu_getscanline() & 0xFC;
793 // Special PIA 0 for Stargate, to handle the controls
794 struct pia6821_interface stargate_pia_0_intf =
796 //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
797 //outputs: A/B,CA/B2 / 0, 0, 0, 0,
801 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
802 struct pia6821_interface williams_pia_1_intf =
804 //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
805 //outputs: A/B,CA/B2 / 0, williams_snd_cmd_w, 0, 0,
806 //irqs : A/B / williams_main_irq, williams_main_irq
809 // Generic PIA 2, maps to DAC data in and sound IRQs
810 struct pia6821_interface williams_snd_pia_intf =
812 //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
813 //outputs: A/B,CA/B2 / DAC_0_data_w, 0, 0, 0,
814 //irqs : A/B / williams_snd_irq, williams_snd_irq
817 static DRIVER_INIT( stargate )
819 // CMOS configuration
820 CONFIGURE_CMOS(0xCC00, 0x400);
823 CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
827 int cpu_getscanline(void)
829 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
832 *************************************
834 * Returns time until given scanline
836 *************************************
838 double cpu_getscanlinetime(int scanline)
840 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
841 double abstime = timer_get_time();
844 // if we're already past the computed time, count it for the next frame
845 if (abstime >= scantime)
846 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
848 // compute how long from now until that time
849 result = scantime - abstime;
851 // if it's small, just count a whole frame
852 if (result < TIME_IN_NSEC(1))
853 result = TIME_IN_HZ(Machine->drv->frames_per_second);
857 *************************************
859 * Returns time for one scanline
861 *************************************
863 double cpu_getscanlineperiod(void)
865 return scanline_period;
869 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
870 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
871 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
873 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
874 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
875 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
877 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
878 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
879 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
880 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
882 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
883 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
884 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
885 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
887 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
888 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
889 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
890 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
892 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
893 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
894 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
895 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
897 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
898 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
899 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
900 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
902 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
903 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
904 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
906 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
907 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
908 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
909 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
916 bit 2 |-6 bits to sound board
921 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
924 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
929 00 -> $C80D = PIA2 -> DDR active
930 00 -> $C80C = PIA2 DDR -> All input?
938 #define PIA_IRQ1 (0x80)
939 #define PIA_IRQ2 (0x40)
941 #define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
942 #define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
943 #define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
944 #define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
945 #define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
946 #define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
947 #define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
948 #define C2_SET(c) ( (((c) >> 3) & 0x01))
949 #define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
950 #define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
951 #define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
952 #define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
953 #define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
954 #define C2_INPUT(c) (!(((c) >> 5) & 0x01))
956 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
958 pia6821_state *p = get_token(device);
960 /* limit the data to 0 or 1 */
961 data = data ? TRUE : FALSE;
963 LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
965 /* the new state has caused a transition */
966 if ((p->in_ca1 != data) &&
967 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
969 LOG(("PIA #%s: CA1 triggering\n", device->tag));
974 /* update externals */
975 update_interrupts(device);
977 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
978 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
979 set_out_ca2(device, TRUE);
982 /* set the new value for CA1 */
984 p->in_ca1_pushed = TRUE;
987 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
989 pia6821_state *p = get_token(device);
991 /* limit the data to 0 or 1 */
994 LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
996 /* the new state has caused a transition */
997 if ((p->in_cb1 != data) &&
998 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
1000 LOG(("PIA #%s: CB1 triggering\n", device->tag));
1005 /* update externals */
1006 update_interrupts(device);
1008 /* If CB2 is configured as a write-strobe output which is reset by a CB1
1009 transition, this reset will only happen when a read from port B implicitly
1010 clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
1011 is different from what happens with port A. */
1014 /* set the new value for CB1 */
1016 p->in_cb1_pushed = TRUE;
1019 static void update_interrupts(const device_config *device)
1021 pia6821_state *p = get_token(device);
1024 /* start with IRQ A */
1025 new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1027 if (new_state != p->irq_a_state)
1029 p->irq_a_state = new_state;
1030 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1034 new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1036 if (new_state != p->irq_b_state)
1038 p->irq_b_state = new_state;
1039 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1043 static void control_b_w(const device_config *device, UINT8 data)
1045 pia6821_state *p = get_token(device);
1048 /* bit 7 and 6 are read only */
1051 LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1053 /* update the control register */
1056 if (C2_SET_MODE(p->ctl_b))
1057 /* set/reset mode - bit value determines the new output */
1058 temp = C2_SET(p->ctl_b);
1060 /* strobe mode - output is always high unless strobed */
1063 set_out_cb2(device, temp);
1065 /* update externals */
1066 update_interrupts(device);
1069 static void control_a_w(const device_config *device, UINT8 data)
1071 pia6821_state *p = get_token(device);
1073 /* bit 7 and 6 are read only */
1076 LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1078 /* update the control register */
1081 /* CA2 is configured as output */
1082 if (C2_OUTPUT(p->ctl_a))
1086 if (C2_SET_MODE(p->ctl_a))
1087 /* set/reset mode - bit value determines the new output */
1088 temp = C2_SET(p->ctl_a);
1090 /* strobe mode - output is always high unless strobed */
1093 set_out_ca2(device, temp);
1096 /* update externals */
1097 update_interrupts(device);
1103 B7 B6 B5 B4 B3 B2 B1 B0
1104 --------------------------------------------------
1105 IRQA(B)1 IRQA(B)2 CA(B)2 Ctrl DDR CA(B)1 Ctrl
1107 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1108 DDR: 0: DDR selected, 1: Output register selected
1109 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1110 B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1111 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1113 Entering main loop...
1114 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1115 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1116 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1117 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1118 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1119 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1120 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1121 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1122 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1123 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1124 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1125 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1126 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1127 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1128 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1129 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1130 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1131 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1132 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1133 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1134 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1135 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1136 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B] --> Clear IRQAs
1137 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9] --> Clear IRQAs
1139 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1140 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1141 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1142 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1143 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1144 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1145 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1146 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1147 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1148 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1149 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1150 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1151 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1152 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1153 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1154 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1155 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1156 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]