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 //1/64 no more... but 1/128, 1/32 and 1/16 don't work either!
50 //#define SG2_PIA_CALLBACK_DURATION ((FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC) / 16.0)
51 #define SG2_PIA_CALLBACK_DURATION ((FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC) / 64.0)
55 uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
60 bool paletteDirty = false;
64 static bool running = true; // Machine running state flag...
65 static uint32 startTicks;
66 static uint8 * keys; // SDL raw keyboard matrix
67 static uint64 clockFrameStart; // V6809 clock at the start of the frame
69 // Function prototypes
71 uint8 RdMem6809(uint16 addr);
72 void WrMem6809(uint16 addr, uint8 b);
73 uint8 RdMem6808(uint16 addr);
74 void WrMem6808(uint16 addr, uint8 b);
75 bool LoadImg(const char * filename, uint8 * ram, int size);
77 bool LoadMachineState(void);
78 void SaveMachineState(void);
80 // Local timer callback functions
82 static void FrameCallback(void);
83 static void ScanlineCallback(void);
89 int main(int /*argc*/, char * /*argv*/[])
91 InitLog("stargem2.log");
92 WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
93 WriteLog("(C) 2009 Underground Software\n\n");
97 // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
98 for(uint32 i=0; i<256; i++)
100 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
101 (((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151) << 24)
102 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 16)
103 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 8) | 0xFF;
105 ((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151)
106 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 8)
107 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 16) | 0xFF000000;
111 memset(gram, 0, 0x10000);
112 memset(grom, 0, 0x10000);
113 memset(sram, 0, 0x10000);
114 memset(srom, 0, 0x10000);
116 // Set up V6809 & V6808 execution contexts
118 memset(&mainCPU, 0, sizeof(V6809REGS));
119 mainCPU.RdMem = RdMem6809;
120 mainCPU.WrMem = WrMem6809;
121 mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
123 memset(&soundCPU, 0, sizeof(V6808REGS));
124 soundCPU.RdMem = RdMem6808;
125 soundCPU.WrMem = WrMem6808;
126 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
129 "ROMs/01", "ROMs/02", "ROMs/03", "ROMs/04", "ROMs/05", "ROMs/06",
130 "ROMs/07", "ROMs/08", "ROMs/09", "ROMs/10", "ROMs/11", "ROMs/12"
133 for(int i=0; i<12; i++)
135 uint32 baseAddress = i * 0x1000;
138 baseAddress += 0x4000;
140 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
142 WriteLog("Could not open file '%s'!\n", ROMs[i]);
147 if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
149 WriteLog("Could not open file '%s'!\n", SOUNDROM);
153 WriteLog("Stargate ROM images loaded...\n");
154 WriteLog("About to initialize video...\n");
158 cout << "Aborting!" << endl;
162 // Have to do this *after* video init but *before* sound init...!
163 WriteLog("About to load machine state...");
165 if (!LoadMachineState())
166 WriteLog("Machine state file not found!\n");
170 if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
171 WriteLog("CMOS RAM not found!\n");
173 WriteLog("About to intialize audio...\n");
175 keys = SDL_GetKeyState(NULL);
176 srom[0xF800] = 0x37; // Fix checksum so ST works...
177 running = true; // Set running status...
179 InitializeEventList(); // Clear the event list before we use it...
180 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
181 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
182 clockFrameStart = mainCPU.clock;
183 startTicks = SDL_GetTicks();
185 WriteLog("Entering main loop...\n");
189 double timeToNextEvent = GetTimeToNextEvent();
190 Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
196 WriteLog("$C900 = $%02X (0=RAM)\n", gram[0xC900]);
197 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);
200 /*uint16 pc = mainCPU.pc;//0x15BA;
201 for(int i=0; i<200; i++)
202 //while (pc < 0x9000)
204 pc += Decode6809(pc);
211 pc += Decode6809(pc);
226 // Load a file into RAM/ROM image space
228 bool LoadImg(const char * filename, uint8 * ram, int size)
230 FILE * fp = fopen(filename, "rb");
235 size_t ignoredResult = fread(ram, 1, size, fp);
246 FILE * fp = fopen(CMOS, "wb");
250 size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
254 WriteLog("CMOS RAM not saved!\n");
258 // Load state save file
260 bool LoadMachineState(void)
262 FILE * fp = fopen(SAVESTATE, "rb");
267 // This is kinda crappy--we don't do any sanity checking here!!!
268 size_t ignoredResult = fread(gram, 1, 0x10000, fp);
269 ignoredResult = fread(sram, 1, 0x10000, fp);
270 ignoredResult = fread(&mainCPU, 1, sizeof(V6809REGS), fp);
271 ignoredResult = fread(&soundCPU, 1, sizeof(V6808REGS), fp);
274 // Set up backbuffer... ;-)
275 for(uint16 i=0x0006; i<0x97F8; i++) // Screen memory
276 WrMem6809(i, gram[i]);
278 for(uint16 i=0xC000; i<=0xC00F; i++) // Palette memory
279 WrMem6809(i, gram[i]);
283 mainCPU.RdMem = RdMem6809; // Make sure our function pointers are
284 mainCPU.WrMem = WrMem6809; // pointing to the right places!
285 soundCPU.RdMem = RdMem6808;
286 soundCPU.WrMem = WrMem6808;
287 mainCPU.clock = 0; // Zero out our clocks...
289 mainCPU.clockOverrun = 0; // And overrun values...
290 //notyet soundCPU.clockOverrun = 0;
296 // Save state save file
298 void SaveMachineState(void)
300 FILE * fp = fopen(SAVESTATE, "wb");
304 size_t ignoredResult = fwrite(gram, 1, 0x10000, fp);
305 ignoredResult = fwrite(sram, 1, 0x10000, fp);
306 ignoredResult = fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
307 ignoredResult = fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
311 WriteLog("Machine state not saved!\n");
315 // 6809 memory functions
319 char piaRegsName[4][10] = { "PORTA", "PACTL", "PORTB", "PBCTL" };
321 uint8 RdMem6809(uint16 addr)
325 if (addr >= 0x9000 && addr <= 0xCFFF) // No ROM between $9000 - $CFFF...
329 if (!gram[0xC900] && addr <= 0x8FFF) // Check RAM $C900 bank switch
335 // A wee kludge (though I doubt it reads from anywhere other than $CB00)...
336 if ((addr & 0xFF00) == 0xCB00)
338 b = gram[0xCB00] & 0xFC; // Only bits 2-7 are connected...
341 //Interesting, this code ALSO fucks up the demo...
342 //Except when the correct code is called in the scanline callback function...
343 uint32 elapsedCycles = (uint32)(GetCurrentV6809Clock() - clockFrameStart);
344 uint32 scanline = (uint32)((double)elapsedCycles / SCANLINE_DURATION_IN_CYCLES);
345 //Changes here don't seem to do much...
346 // uint32 scanline = (uint32)(((double)elapsedCycles * M6809_CYCLE_IN_USEC) / 70.0);
347 b = (uint8)scanline & 0xFC; // Only bits 2-7 are connected...
352 if ((addr == 0xC80C) && (gram[0xC80D] & 0x04)) // Read PORTA and DDR is set to Output
354 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
355 //OK, this ALSO fucks up the execution of the demo...
356 // Which means that the timing is still off. :-/
357 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
360 if ((addr == 0xC80E) && (gram[0xC80F] & 0x04)) // Read PORTB and DDR is set to Output
362 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
363 //OK, this ALSO fucks up the execution of the demo...
364 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
369 //if (addr >= 0xC000 && addr <= 0xCBFF)
371 WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
373 /*if (addr >= 0xC80C && addr <= 0xC80F)
374 WriteLog("V6809 RdMem: Reading PIA (%s) address %04X [<-%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
379 void WrMem6809(uint16 addr, uint8 b)
382 //extern V6809REGS regs;
383 //if (addr >= 0xC800 && addr <= 0xCBFE)
384 //if (addr == 0xC80F || addr == 0xC80D)
385 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
386 //if (addr == 0xC80E)
387 /*if (addr >= 0xC800 && addr <= 0xC80F)
388 WriteLog("V6809 WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, mainCPU.pc, gram[0xCB00]);//*/
392 if (addr >= 0x0006 && addr < 0x97F7) // 304 pixels 152-128=24-16=8
394 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
395 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
397 if (sy > 5 && sy < 246)
399 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
400 scrBuffer[saddr + 0] = palette[color[b >> 4]];
401 scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
404 else if (addr >= 0xC000 && addr <= 0xC00F)
406 // This approach doesn't take the BG color to the edges of the screen
407 color[addr - 0xC000] = b;
410 else if (addr == 0xC80E)
412 sram[0x0402] = b; // Connect PIAs in 6809 & 6808
413 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ; // Start sound IRQ
417 //if (addr >= 0xC80C && addr <= 0xC80F)
419 WriteLog("V6809 WrMem: Writing PIA (%s) address %04X [->%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
424 // 6808 memory functions
427 uint8 RdMem6808(uint16 addr)
429 return (addr < 0xF000 ? sram[addr] : srom[addr]);
432 void WrMem6808(uint16 addr, uint8 b)
436 // A total guess, but let's try it...
437 //It probably depends on how the PIA is configured, so this is most likely wrong.
438 // It is wrong: IRQs are cleared on PIA PORTx reads!
439 // if (addr == 0x0401)
440 // soundCPU.cpuFlags &= ~V6808_ASSERT_LINE_IRQ;
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 if (keys[settings.keyBindings[S_KEY_FIRE]]) gram[0xC804] |= 0x01;
452 if (keys[settings.keyBindings[S_KEY_THRUST]]) gram[0xC804] |= 0x02;
453 if (keys[settings.keyBindings[S_KEY_SMARTBOMB]]) gram[0xC804] |= 0x04;
454 if (keys[settings.keyBindings[S_KEY_HYPERSPACE]]) gram[0xC804] |= 0x08;
455 if (keys[settings.keyBindings[S_KEY_2P_START]]) gram[0xC804] |= 0x10;
456 if (keys[settings.keyBindings[S_KEY_1P_START]]) gram[0xC804] |= 0x20;
457 if (keys[settings.keyBindings[S_KEY_REVERSE]]) gram[0xC804] |= 0x40;
458 if (keys[settings.keyBindings[S_KEY_DOWN]]) gram[0xC804] |= 0x80;
460 if (keys[settings.keyBindings[S_KEY_UP]]) gram[0xC806] |= 0x01;
461 if (keys[settings.keyBindings[S_KEY_INVISO]]) gram[0xC806] |= 0x02;
463 if (keys[settings.keyBindings[S_KEY_AUTO_UP]]) gram[0xC80C] |= 0x01;
464 if (keys[settings.keyBindings[S_KEY_ADVANCE]]) gram[0xC80C] |= 0x02;
465 if (keys[settings.keyBindings[S_KEY_RIGHT_COIN]]) gram[0xC80C] |= 0x04;
466 if (keys[settings.keyBindings[S_KEY_HS_RESET]]) gram[0xC80C] |= 0x08;
467 if (keys[settings.keyBindings[S_KEY_LEFT_COIN]]) gram[0xC80C] |= 0x10;
468 if (keys[settings.keyBindings[S_KEY_CENTER_COIN]]) gram[0xC80C] |= 0x20;
469 if (keys[settings.keyBindings[S_KEY_SLAM_SWITCH]]) gram[0xC80C] |= 0x40;
471 if (keys[SDLK_F5]) // Sound CPU self-test (F5)
472 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
473 if (keys[SDLK_F6]) // Reset the 6808 (F6)
474 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
475 //Temp, for testing...
483 for(uint32 addr=0x0006; addr<0x97F7; addr++)
485 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
487 if (sy > 5 && sy < 246)
489 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
490 uint8 sb = gram[addr];
492 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
493 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
497 paletteDirty = false;
500 RenderScreenBuffer(); // 1 frame = 1/60 sec ~ 16667 cycles
501 clockFrameStart = mainCPU.clock;
503 // Wait for next frame...
504 while (SDL_GetTicks() - startTicks < 16)
507 startTicks = SDL_GetTicks();
508 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
511 static void ScanlineCallback(void)
514 if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
515 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
517 mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
519 if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
520 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
524 The problem is that this is already asserted above, by virtue of the fact that
525 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
526 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
527 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
529 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
531 // if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09)) // Do COUNT240 IRQ (if enabled!)
532 // mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
534 // This should set everything between $CB00-CBFF...
535 // gram[0xCB00] += 8; // Update video counter...
536 // gram[0xCB00] += 4; // Update video counter...
538 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
543 ; With correct timing, but no color cycling
545 --> Start of frame...
546 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
551 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
552 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
560 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
561 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
562 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
565 ; With incorrect timing, but has color cycling
567 --> Start of frame...
568 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
569 At $1609. $6E: 00 ; Color cycling...
574 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
575 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
576 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
577 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
585 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
586 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
587 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
594 0000-8FFF ROM (for Blaster, 0000-3FFF is a bank of 12 ROMs)
595 0000-97FF Video RAM Bank switched with ROM (96FF for Blaster)
597 0xBB00 Blaster only, Color 0 for each line (256 entry)
598 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
599 Do something else with the bit 1, I do not know what
603 C000-C00F color_registers (16 bytes of BBGGGRRR)
605 C804 widget_pia_dataa (widget = I/O board)
606 C805 widget_pia_ctrla
607 C806 widget_pia_datab
608 C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
609 controls if Table or Joust)
610 bits 5-3 = 110 = player 2
611 bits 5-3 = 111 = player 1
618 bit 2 |-6 bits to sound board
623 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
626 C900 rom_enable_scr_ctrl Switch between video ram and rom at 0000-97FF
630 C804 widget_pia_dataa (widget = I/O board)
640 C806 widget_pia_datab
648 bit 7 0 = Upright 1 = Table
653 bit 2 Right Coin (High Score Reset in schematics)
654 bit 3 High Score Reset (Left Coin in schematics)
655 bit 4 Left Coin (Center Coin in schematics)
656 bit 5 Center Coin (Right Coin in schematics)
658 bit 7 Hand Shake from sound board
664 static MEMORY_READ_START( williams_readmem )
665 { 0x0000, 0x97ff, MRA_BANK1 },
666 { 0x9800, 0xbfff, MRA_RAM },
667 { 0xc804, 0xc807, pia_0_r },
668 { 0xc80c, 0xc80f, pia_1_r },
669 { 0xcb00, 0xcb00, williams_video_counter_r },
670 { 0xcc00, 0xcfff, MRA_RAM },
671 { 0xd000, 0xffff, MRA_ROM },
675 static MEMORY_WRITE_START( williams_writemem )
676 { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
677 { 0x9800, 0xbfff, MWA_RAM },
678 { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
679 { 0xc804, 0xc807, pia_0_w },
680 { 0xc80c, 0xc80f, pia_1_w },
681 { 0xc900, 0xc900, williams_vram_select_w },
682 { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
683 { 0xcbff, 0xcbff, watchdog_reset_w },
684 { 0xcc00, 0xcfff, MWA_RAM },
685 { 0xd000, 0xffff, MWA_ROM },
688 static MEMORY_READ_START( sound_readmem )
689 { 0x0000, 0x007f, MRA_RAM },
690 { 0x0400, 0x0403, pia_2_r },
691 { 0x8400, 0x8403, pia_2_r }, // used by Colony 7, perhaps others?
692 { 0xb000, 0xffff, MRA_ROM }, // most games start at $F000, Sinistar starts at $B000
696 static MEMORY_WRITE_START( sound_writemem )
697 { 0x0000, 0x007f, MWA_RAM },
698 { 0x0400, 0x0403, pia_2_w },
699 { 0x8400, 0x8403, pia_2_w }, // used by Colony 7, perhaps others?
700 { 0xb000, 0xffff, MWA_ROM }, // most games start at $F000, Sinistar starts at $B000
703 MACHINE_INIT( williams )
708 // reset the ticket dispenser (Lotto Fun)
709 ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
711 // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
712 timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
714 // also set a timer to go off on scanline 240
715 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
719 static void williams_va11_callback(int scanline)
721 // the IRQ signal comes into CB1, and is set to VA11
722 pia_1_cb1_w(0, scanline & 0x20);
724 // update the screen while we're here
725 force_partial_update(scanline - 1);
727 // set a timer for the next update
729 if (scanline >= 256) scanline = 0;
730 timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
734 static void williams_count240_off_callback(int param)
736 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
741 static void williams_count240_callback(int param)
743 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
746 // set a timer to turn it off once the scanline counter resets
747 timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
749 // set a timer for next frame
750 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
754 static void williams_main_irq(int state)
756 // IRQ to the main CPU
757 cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
761 static void williams_main_firq(int state)
763 // FIRQ to the main CPU
764 cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
768 static void williams_snd_irq(int state)
770 // IRQ to the sound CPU
771 cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
775 READ_HANDLER( williams_video_counter_r )
777 return cpu_getscanline() & 0xFC;
781 // Special PIA 0 for Stargate, to handle the controls
782 struct pia6821_interface stargate_pia_0_intf =
784 //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
785 //outputs: A/B,CA/B2 / 0, 0, 0, 0,
789 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
790 struct pia6821_interface williams_pia_1_intf =
792 //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
793 //outputs: A/B,CA/B2 / 0, williams_snd_cmd_w, 0, 0,
794 //irqs : A/B / williams_main_irq, williams_main_irq
797 // Generic PIA 2, maps to DAC data in and sound IRQs
798 struct pia6821_interface williams_snd_pia_intf =
800 //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
801 //outputs: A/B,CA/B2 / DAC_0_data_w, 0, 0, 0,
802 //irqs : A/B / williams_snd_irq, williams_snd_irq
805 static DRIVER_INIT( stargate )
807 // CMOS configuration
808 CONFIGURE_CMOS(0xCC00, 0x400);
811 CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
815 int cpu_getscanline(void)
817 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
820 *************************************
822 * Returns time until given scanline
824 *************************************
826 double cpu_getscanlinetime(int scanline)
828 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
829 double abstime = timer_get_time();
832 // if we're already past the computed time, count it for the next frame
833 if (abstime >= scantime)
834 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
836 // compute how long from now until that time
837 result = scantime - abstime;
839 // if it's small, just count a whole frame
840 if (result < TIME_IN_NSEC(1))
841 result = TIME_IN_HZ(Machine->drv->frames_per_second);
845 *************************************
847 * Returns time for one scanline
849 *************************************
851 double cpu_getscanlineperiod(void)
853 return scanline_period;
857 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
858 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
859 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
861 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
862 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
863 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
865 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
866 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
867 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
868 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
870 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
871 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
872 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
873 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
875 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
876 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
877 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
878 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
880 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
881 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
882 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
883 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
885 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
886 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
887 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
888 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
890 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
891 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
892 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
894 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
895 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
896 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
897 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
904 bit 2 |-6 bits to sound board
909 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
912 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
917 00 -> $C80D = PIA2 -> DDR active
918 00 -> $C80C = PIA2 DDR -> All input?
926 #define PIA_IRQ1 (0x80)
927 #define PIA_IRQ2 (0x40)
929 #define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
930 #define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
931 #define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
932 #define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
933 #define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
934 #define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
935 #define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
936 #define C2_SET(c) ( (((c) >> 3) & 0x01))
937 #define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
938 #define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
939 #define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
940 #define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
941 #define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
942 #define C2_INPUT(c) (!(((c) >> 5) & 0x01))
944 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
946 pia6821_state *p = get_token(device);
948 /* limit the data to 0 or 1 */
949 data = data ? TRUE : FALSE;
951 LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
953 /* the new state has caused a transition */
954 if ((p->in_ca1 != data) &&
955 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
957 LOG(("PIA #%s: CA1 triggering\n", device->tag));
962 /* update externals */
963 update_interrupts(device);
965 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
966 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
967 set_out_ca2(device, TRUE);
970 /* set the new value for CA1 */
972 p->in_ca1_pushed = TRUE;
975 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
977 pia6821_state *p = get_token(device);
979 /* limit the data to 0 or 1 */
982 LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
984 /* the new state has caused a transition */
985 if ((p->in_cb1 != data) &&
986 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
988 LOG(("PIA #%s: CB1 triggering\n", device->tag));
993 /* update externals */
994 update_interrupts(device);
996 /* If CB2 is configured as a write-strobe output which is reset by a CB1
997 transition, this reset will only happen when a read from port B implicitly
998 clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
999 is different from what happens with port A. */
1002 /* set the new value for CB1 */
1004 p->in_cb1_pushed = TRUE;
1007 static void update_interrupts(const device_config *device)
1009 pia6821_state *p = get_token(device);
1012 /* start with IRQ A */
1013 new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1015 if (new_state != p->irq_a_state)
1017 p->irq_a_state = new_state;
1018 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1022 new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1024 if (new_state != p->irq_b_state)
1026 p->irq_b_state = new_state;
1027 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1031 static void control_b_w(const device_config *device, UINT8 data)
1033 pia6821_state *p = get_token(device);
1036 /* bit 7 and 6 are read only */
1039 LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1041 /* update the control register */
1044 if (C2_SET_MODE(p->ctl_b))
1045 /* set/reset mode - bit value determines the new output */
1046 temp = C2_SET(p->ctl_b);
1048 /* strobe mode - output is always high unless strobed */
1051 set_out_cb2(device, temp);
1053 /* update externals */
1054 update_interrupts(device);
1057 static void control_a_w(const device_config *device, UINT8 data)
1059 pia6821_state *p = get_token(device);
1061 /* bit 7 and 6 are read only */
1064 LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1066 /* update the control register */
1069 /* CA2 is configured as output */
1070 if (C2_OUTPUT(p->ctl_a))
1074 if (C2_SET_MODE(p->ctl_a))
1075 /* set/reset mode - bit value determines the new output */
1076 temp = C2_SET(p->ctl_a);
1078 /* strobe mode - output is always high unless strobed */
1081 set_out_ca2(device, temp);
1084 /* update externals */
1085 update_interrupts(device);
1091 B7 B6 B5 B4 B3 B2 B1 B0
1092 --------------------------------------------------
1093 IRQA(B)1 IRQA(B)2 CA(B)2 Ctrl DDR CA(B)1 Ctrl
1095 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1096 DDR: 0: DDR selected, 1: Output register selected
1097 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1098 B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1099 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1101 Entering main loop...
1102 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1103 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1104 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1105 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1106 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1107 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1108 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1109 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1110 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1111 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1112 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1113 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1114 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1115 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1116 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1117 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1118 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1119 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1120 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1121 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1122 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1123 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1124 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B] --> Clear IRQAs
1125 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9] --> Clear IRQAs
1127 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1128 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1129 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1130 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1131 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1132 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1133 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1134 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1135 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1136 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1137 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1138 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1139 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1140 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1141 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
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]