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 ClearLineOfCurrentV6809(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 ClearLineOfCurrentV6809(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 if (keys[settings.keyBindings[S_KEY_FIRE]]) gram[0xC804] |= 0x01;
451 if (keys[settings.keyBindings[S_KEY_THRUST]]) gram[0xC804] |= 0x02;
452 if (keys[settings.keyBindings[S_KEY_SMARTBOMB]]) gram[0xC804] |= 0x04;
453 if (keys[settings.keyBindings[S_KEY_HYPERSPACE]]) gram[0xC804] |= 0x08;
454 if (keys[settings.keyBindings[S_KEY_2P_START]]) gram[0xC804] |= 0x10;
455 if (keys[settings.keyBindings[S_KEY_1P_START]]) gram[0xC804] |= 0x20;
456 if (keys[settings.keyBindings[S_KEY_REVERSE]]) gram[0xC804] |= 0x40;
457 if (keys[settings.keyBindings[S_KEY_DOWN]]) gram[0xC804] |= 0x80;
459 if (keys[settings.keyBindings[S_KEY_UP]]) gram[0xC806] |= 0x01;
460 if (keys[settings.keyBindings[S_KEY_INVISO]]) gram[0xC806] |= 0x02;
462 if (keys[settings.keyBindings[S_KEY_AUTO_UP]]) gram[0xC80C] |= 0x01;
463 if (keys[settings.keyBindings[S_KEY_ADVANCE]]) gram[0xC80C] |= 0x02;
464 if (keys[settings.keyBindings[S_KEY_RIGHT_COIN]]) gram[0xC80C] |= 0x04;
465 if (keys[settings.keyBindings[S_KEY_HS_RESET]]) gram[0xC80C] |= 0x08;
466 if (keys[settings.keyBindings[S_KEY_LEFT_COIN]]) gram[0xC80C] |= 0x10;
467 if (keys[settings.keyBindings[S_KEY_CENTER_COIN]]) gram[0xC80C] |= 0x20;
468 if (keys[settings.keyBindings[S_KEY_SLAM_SWITCH]]) gram[0xC80C] |= 0x40;
470 if (keys[SDLK_F5]) // Sound CPU self-test (F5)
471 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
472 if (keys[SDLK_F6]) // Reset the 6808 (F6)
473 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
474 //Temp, for testing...
482 for(uint32 addr=0x0006; addr<0x97F7; addr++)
484 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
486 if (sy > 5 && sy < 246)
488 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
489 uint8 sb = gram[addr];
491 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
492 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
496 paletteDirty = false;
499 RenderScreenBuffer(); // 1 frame = 1/60 sec ~ 16667 cycles
500 clockFrameStart = mainCPU.clock;
502 // Wait for next frame...
503 while (SDL_GetTicks() - startTicks < 16)
506 startTicks = SDL_GetTicks();
507 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
510 static void ScanlineCallback(void)
513 if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
514 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
516 mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
518 if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
519 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
523 The problem is that this is already asserted above, by virtue of the fact that
524 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
525 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
526 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
528 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
530 // if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09)) // Do COUNT240 IRQ (if enabled!)
531 // mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
533 // This should set everything between $CB00-CBFF...
534 // gram[0xCB00] += 8; // Update video counter...
535 // gram[0xCB00] += 4; // Update video counter...
537 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
542 ; With correct timing, but no color cycling
544 --> Start of frame...
545 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
550 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
551 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
559 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
560 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
561 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
564 ; With incorrect timing, but has color cycling
566 --> Start of frame...
567 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
568 At $1609. $6E: 00 ; Color cycling...
573 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
574 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
575 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
576 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
584 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
585 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
586 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
593 0000-8FFF ROM (for Blaster, 0000-3FFF is a bank of 12 ROMs)
594 0000-97FF Video RAM Bank switched with ROM (96FF for Blaster)
596 0xBB00 Blaster only, Color 0 for each line (256 entry)
597 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
598 Do something else with the bit 1, I do not know what
602 C000-C00F color_registers (16 bytes of BBGGGRRR)
604 C804 widget_pia_dataa (widget = I/O board)
605 C805 widget_pia_ctrla
606 C806 widget_pia_datab
607 C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
608 controls if Table or Joust)
609 bits 5-3 = 110 = player 2
610 bits 5-3 = 111 = player 1
617 bit 2 |-6 bits to sound board
622 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
625 C900 rom_enable_scr_ctrl Switch between video ram and rom at 0000-97FF
629 C804 widget_pia_dataa (widget = I/O board)
639 C806 widget_pia_datab
647 bit 7 0 = Upright 1 = Table
652 bit 2 Right Coin (High Score Reset in schematics)
653 bit 3 High Score Reset (Left Coin in schematics)
654 bit 4 Left Coin (Center Coin in schematics)
655 bit 5 Center Coin (Right Coin in schematics)
657 bit 7 Hand Shake from sound board
663 static MEMORY_READ_START( williams_readmem )
664 { 0x0000, 0x97ff, MRA_BANK1 },
665 { 0x9800, 0xbfff, MRA_RAM },
666 { 0xc804, 0xc807, pia_0_r },
667 { 0xc80c, 0xc80f, pia_1_r },
668 { 0xcb00, 0xcb00, williams_video_counter_r },
669 { 0xcc00, 0xcfff, MRA_RAM },
670 { 0xd000, 0xffff, MRA_ROM },
674 static MEMORY_WRITE_START( williams_writemem )
675 { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
676 { 0x9800, 0xbfff, MWA_RAM },
677 { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
678 { 0xc804, 0xc807, pia_0_w },
679 { 0xc80c, 0xc80f, pia_1_w },
680 { 0xc900, 0xc900, williams_vram_select_w },
681 { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
682 { 0xcbff, 0xcbff, watchdog_reset_w },
683 { 0xcc00, 0xcfff, MWA_RAM },
684 { 0xd000, 0xffff, MWA_ROM },
687 static MEMORY_READ_START( sound_readmem )
688 { 0x0000, 0x007f, MRA_RAM },
689 { 0x0400, 0x0403, pia_2_r },
690 { 0x8400, 0x8403, pia_2_r }, // used by Colony 7, perhaps others?
691 { 0xb000, 0xffff, MRA_ROM }, // most games start at $F000, Sinistar starts at $B000
695 static MEMORY_WRITE_START( sound_writemem )
696 { 0x0000, 0x007f, MWA_RAM },
697 { 0x0400, 0x0403, pia_2_w },
698 { 0x8400, 0x8403, pia_2_w }, // used by Colony 7, perhaps others?
699 { 0xb000, 0xffff, MWA_ROM }, // most games start at $F000, Sinistar starts at $B000
702 MACHINE_INIT( williams )
707 // reset the ticket dispenser (Lotto Fun)
708 ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
710 // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
711 timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
713 // also set a timer to go off on scanline 240
714 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
718 static void williams_va11_callback(int scanline)
720 // the IRQ signal comes into CB1, and is set to VA11
721 pia_1_cb1_w(0, scanline & 0x20);
723 // update the screen while we're here
724 force_partial_update(scanline - 1);
726 // set a timer for the next update
728 if (scanline >= 256) scanline = 0;
729 timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
733 static void williams_count240_off_callback(int param)
735 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
740 static void williams_count240_callback(int param)
742 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
745 // set a timer to turn it off once the scanline counter resets
746 timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
748 // set a timer for next frame
749 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
753 static void williams_main_irq(int state)
755 // IRQ to the main CPU
756 cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
760 static void williams_main_firq(int state)
762 // FIRQ to the main CPU
763 cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
767 static void williams_snd_irq(int state)
769 // IRQ to the sound CPU
770 cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
774 READ_HANDLER( williams_video_counter_r )
776 return cpu_getscanline() & 0xFC;
780 // Special PIA 0 for Stargate, to handle the controls
781 struct pia6821_interface stargate_pia_0_intf =
783 //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
784 //outputs: A/B,CA/B2 / 0, 0, 0, 0,
788 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
789 struct pia6821_interface williams_pia_1_intf =
791 //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
792 //outputs: A/B,CA/B2 / 0, williams_snd_cmd_w, 0, 0,
793 //irqs : A/B / williams_main_irq, williams_main_irq
796 // Generic PIA 2, maps to DAC data in and sound IRQs
797 struct pia6821_interface williams_snd_pia_intf =
799 //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
800 //outputs: A/B,CA/B2 / DAC_0_data_w, 0, 0, 0,
801 //irqs : A/B / williams_snd_irq, williams_snd_irq
804 static DRIVER_INIT( stargate )
806 // CMOS configuration
807 CONFIGURE_CMOS(0xCC00, 0x400);
810 CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
814 int cpu_getscanline(void)
816 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
819 *************************************
821 * Returns time until given scanline
823 *************************************
825 double cpu_getscanlinetime(int scanline)
827 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
828 double abstime = timer_get_time();
831 // if we're already past the computed time, count it for the next frame
832 if (abstime >= scantime)
833 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
835 // compute how long from now until that time
836 result = scantime - abstime;
838 // if it's small, just count a whole frame
839 if (result < TIME_IN_NSEC(1))
840 result = TIME_IN_HZ(Machine->drv->frames_per_second);
844 *************************************
846 * Returns time for one scanline
848 *************************************
850 double cpu_getscanlineperiod(void)
852 return scanline_period;
856 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
857 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
858 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
860 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
861 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
862 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
864 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
865 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
866 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
867 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
869 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
870 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
871 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
872 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
874 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
875 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
876 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
877 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
879 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
880 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
881 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
882 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
884 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
885 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
886 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
887 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
889 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
890 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
891 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
893 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
894 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
895 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
896 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
903 bit 2 |-6 bits to sound board
908 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
911 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
916 00 -> $C80D = PIA2 -> DDR active
917 00 -> $C80C = PIA2 DDR -> All input?
925 #define PIA_IRQ1 (0x80)
926 #define PIA_IRQ2 (0x40)
928 #define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
929 #define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
930 #define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
931 #define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
932 #define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
933 #define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
934 #define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
935 #define C2_SET(c) ( (((c) >> 3) & 0x01))
936 #define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
937 #define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
938 #define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
939 #define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
940 #define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
941 #define C2_INPUT(c) (!(((c) >> 5) & 0x01))
943 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
945 pia6821_state *p = get_token(device);
947 /* limit the data to 0 or 1 */
948 data = data ? TRUE : FALSE;
950 LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
952 /* the new state has caused a transition */
953 if ((p->in_ca1 != data) &&
954 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
956 LOG(("PIA #%s: CA1 triggering\n", device->tag));
961 /* update externals */
962 update_interrupts(device);
964 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
965 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
966 set_out_ca2(device, TRUE);
969 /* set the new value for CA1 */
971 p->in_ca1_pushed = TRUE;
974 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
976 pia6821_state *p = get_token(device);
978 /* limit the data to 0 or 1 */
981 LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
983 /* the new state has caused a transition */
984 if ((p->in_cb1 != data) &&
985 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
987 LOG(("PIA #%s: CB1 triggering\n", device->tag));
992 /* update externals */
993 update_interrupts(device);
995 /* If CB2 is configured as a write-strobe output which is reset by a CB1
996 transition, this reset will only happen when a read from port B implicitly
997 clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
998 is different from what happens with port A. */
1001 /* set the new value for CB1 */
1003 p->in_cb1_pushed = TRUE;
1006 static void update_interrupts(const device_config *device)
1008 pia6821_state *p = get_token(device);
1011 /* start with IRQ A */
1012 new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1014 if (new_state != p->irq_a_state)
1016 p->irq_a_state = new_state;
1017 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1021 new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1023 if (new_state != p->irq_b_state)
1025 p->irq_b_state = new_state;
1026 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1030 static void control_b_w(const device_config *device, UINT8 data)
1032 pia6821_state *p = get_token(device);
1035 /* bit 7 and 6 are read only */
1038 LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1040 /* update the control register */
1043 if (C2_SET_MODE(p->ctl_b))
1044 /* set/reset mode - bit value determines the new output */
1045 temp = C2_SET(p->ctl_b);
1047 /* strobe mode - output is always high unless strobed */
1050 set_out_cb2(device, temp);
1052 /* update externals */
1053 update_interrupts(device);
1056 static void control_a_w(const device_config *device, UINT8 data)
1058 pia6821_state *p = get_token(device);
1060 /* bit 7 and 6 are read only */
1063 LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1065 /* update the control register */
1068 /* CA2 is configured as output */
1069 if (C2_OUTPUT(p->ctl_a))
1073 if (C2_SET_MODE(p->ctl_a))
1074 /* set/reset mode - bit value determines the new output */
1075 temp = C2_SET(p->ctl_a);
1077 /* strobe mode - output is always high unless strobed */
1080 set_out_ca2(device, temp);
1083 /* update externals */
1084 update_interrupts(device);
1090 B7 B6 B5 B4 B3 B2 B1 B0
1091 --------------------------------------------------
1092 IRQA(B)1 IRQA(B)2 CA(B)2 Ctrl DDR CA(B)1 Ctrl
1094 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1095 DDR: 0: DDR selected, 1: Output register selected
1096 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1097 B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1098 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1100 Entering main loop...
1101 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1102 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1103 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1104 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1105 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1106 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1107 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1108 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1109 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1110 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1111 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1112 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1113 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1114 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1115 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1116 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1117 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1118 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1119 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1120 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1121 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1122 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1123 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B] --> Clear IRQAs
1124 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9] --> Clear IRQAs
1126 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1127 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1128 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1129 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1130 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1131 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1132 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1133 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1134 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1135 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1136 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1137 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1138 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1139 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1140 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1141 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1142 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1143 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]