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 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;
477 for(uint32 addr=0x0006; addr<0x97F7; addr++)
479 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
481 if (sy > 5 && sy < 246)
483 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
484 uint8 sb = gram[addr];
486 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
487 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
491 paletteDirty = false;
494 RenderScreenBuffer(); // 1 frame = 1/60 sec ~ 16667 cycles
495 clockFrameStart = mainCPU.clock;
497 // Wait for next frame...
498 while (SDL_GetTicks() - startTicks < 16)
501 startTicks = SDL_GetTicks();
502 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
505 static void ScanlineCallback(void)
508 if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
509 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
511 mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
513 if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
514 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
518 The problem is that this is already asserted above, by virtue of the fact that
519 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
520 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
521 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
523 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
525 // if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09)) // Do COUNT240 IRQ (if enabled!)
526 // mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
528 // This should set everything between $CB00-CBFF...
529 // gram[0xCB00] += 8; // Update video counter...
530 // gram[0xCB00] += 4; // Update video counter...
532 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
537 ; With correct timing, but no color cycling
539 --> Start of frame...
540 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
545 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
546 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
554 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
555 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
556 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
559 ; With incorrect timing, but has color cycling
561 --> Start of frame...
562 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
563 At $1609. $6E: 00 ; Color cycling...
568 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
569 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
570 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
571 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
579 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
580 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
581 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
588 0000-8FFF ROM (for Blaster, 0000-3FFF is a bank of 12 ROMs)
589 0000-97FF Video RAM Bank switched with ROM (96FF for Blaster)
591 0xBB00 Blaster only, Color 0 for each line (256 entry)
592 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
593 Do something else with the bit 1, I do not know what
597 C000-C00F color_registers (16 bytes of BBGGGRRR)
599 C804 widget_pia_dataa (widget = I/O board)
600 C805 widget_pia_ctrla
601 C806 widget_pia_datab
602 C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
603 controls if Table or Joust)
604 bits 5-3 = 110 = player 2
605 bits 5-3 = 111 = player 1
612 bit 2 |-6 bits to sound board
617 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
620 C900 rom_enable_scr_ctrl Switch between video ram and rom at 0000-97FF
624 C804 widget_pia_dataa (widget = I/O board)
634 C806 widget_pia_datab
642 bit 7 0 = Upright 1 = Table
647 bit 2 Right Coin (High Score Reset in schematics)
648 bit 3 High Score Reset (Left Coin in schematics)
649 bit 4 Left Coin (Center Coin in schematics)
650 bit 5 Center Coin (Right Coin in schematics)
652 bit 7 Hand Shake from sound board
658 static MEMORY_READ_START( williams_readmem )
659 { 0x0000, 0x97ff, MRA_BANK1 },
660 { 0x9800, 0xbfff, MRA_RAM },
661 { 0xc804, 0xc807, pia_0_r },
662 { 0xc80c, 0xc80f, pia_1_r },
663 { 0xcb00, 0xcb00, williams_video_counter_r },
664 { 0xcc00, 0xcfff, MRA_RAM },
665 { 0xd000, 0xffff, MRA_ROM },
669 static MEMORY_WRITE_START( williams_writemem )
670 { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
671 { 0x9800, 0xbfff, MWA_RAM },
672 { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
673 { 0xc804, 0xc807, pia_0_w },
674 { 0xc80c, 0xc80f, pia_1_w },
675 { 0xc900, 0xc900, williams_vram_select_w },
676 { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
677 { 0xcbff, 0xcbff, watchdog_reset_w },
678 { 0xcc00, 0xcfff, MWA_RAM },
679 { 0xd000, 0xffff, MWA_ROM },
682 static MEMORY_READ_START( sound_readmem )
683 { 0x0000, 0x007f, MRA_RAM },
684 { 0x0400, 0x0403, pia_2_r },
685 { 0x8400, 0x8403, pia_2_r }, // used by Colony 7, perhaps others?
686 { 0xb000, 0xffff, MRA_ROM }, // most games start at $F000, Sinistar starts at $B000
690 static MEMORY_WRITE_START( sound_writemem )
691 { 0x0000, 0x007f, MWA_RAM },
692 { 0x0400, 0x0403, pia_2_w },
693 { 0x8400, 0x8403, pia_2_w }, // used by Colony 7, perhaps others?
694 { 0xb000, 0xffff, MWA_ROM }, // most games start at $F000, Sinistar starts at $B000
697 MACHINE_INIT( williams )
702 // reset the ticket dispenser (Lotto Fun)
703 ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
705 // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
706 timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
708 // also set a timer to go off on scanline 240
709 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
713 static void williams_va11_callback(int scanline)
715 // the IRQ signal comes into CB1, and is set to VA11
716 pia_1_cb1_w(0, scanline & 0x20);
718 // update the screen while we're here
719 force_partial_update(scanline - 1);
721 // set a timer for the next update
723 if (scanline >= 256) scanline = 0;
724 timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
728 static void williams_count240_off_callback(int param)
730 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
735 static void williams_count240_callback(int param)
737 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
740 // set a timer to turn it off once the scanline counter resets
741 timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
743 // set a timer for next frame
744 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
748 static void williams_main_irq(int state)
750 // IRQ to the main CPU
751 cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
755 static void williams_main_firq(int state)
757 // FIRQ to the main CPU
758 cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
762 static void williams_snd_irq(int state)
764 // IRQ to the sound CPU
765 cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
769 READ_HANDLER( williams_video_counter_r )
771 return cpu_getscanline() & 0xFC;
775 // Special PIA 0 for Stargate, to handle the controls
776 struct pia6821_interface stargate_pia_0_intf =
778 //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
779 //outputs: A/B,CA/B2 / 0, 0, 0, 0,
783 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
784 struct pia6821_interface williams_pia_1_intf =
786 //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
787 //outputs: A/B,CA/B2 / 0, williams_snd_cmd_w, 0, 0,
788 //irqs : A/B / williams_main_irq, williams_main_irq
791 // Generic PIA 2, maps to DAC data in and sound IRQs
792 struct pia6821_interface williams_snd_pia_intf =
794 //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
795 //outputs: A/B,CA/B2 / DAC_0_data_w, 0, 0, 0,
796 //irqs : A/B / williams_snd_irq, williams_snd_irq
799 static DRIVER_INIT( stargate )
801 // CMOS configuration
802 CONFIGURE_CMOS(0xCC00, 0x400);
805 CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
809 int cpu_getscanline(void)
811 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
814 *************************************
816 * Returns time until given scanline
818 *************************************
820 double cpu_getscanlinetime(int scanline)
822 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
823 double abstime = timer_get_time();
826 // if we're already past the computed time, count it for the next frame
827 if (abstime >= scantime)
828 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
830 // compute how long from now until that time
831 result = scantime - abstime;
833 // if it's small, just count a whole frame
834 if (result < TIME_IN_NSEC(1))
835 result = TIME_IN_HZ(Machine->drv->frames_per_second);
839 *************************************
841 * Returns time for one scanline
843 *************************************
845 double cpu_getscanlineperiod(void)
847 return scanline_period;
851 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
852 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
853 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
855 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
856 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
857 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
859 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
860 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
861 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
862 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
864 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
865 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
866 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
867 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
869 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
870 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
871 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
872 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
874 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
875 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
876 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
877 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
879 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
880 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
881 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
882 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
884 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
885 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
886 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
888 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
889 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
890 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
891 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
898 bit 2 |-6 bits to sound board
903 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
906 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
911 00 -> $C80D = PIA2 -> DDR active
912 00 -> $C80C = PIA2 DDR -> All input?
920 #define PIA_IRQ1 (0x80)
921 #define PIA_IRQ2 (0x40)
923 #define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
924 #define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
925 #define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
926 #define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
927 #define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
928 #define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
929 #define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
930 #define C2_SET(c) ( (((c) >> 3) & 0x01))
931 #define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
932 #define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
933 #define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
934 #define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
935 #define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
936 #define C2_INPUT(c) (!(((c) >> 5) & 0x01))
938 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
940 pia6821_state *p = get_token(device);
942 /* limit the data to 0 or 1 */
943 data = data ? TRUE : FALSE;
945 LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
947 /* the new state has caused a transition */
948 if ((p->in_ca1 != data) &&
949 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
951 LOG(("PIA #%s: CA1 triggering\n", device->tag));
956 /* update externals */
957 update_interrupts(device);
959 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
960 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
961 set_out_ca2(device, TRUE);
964 /* set the new value for CA1 */
966 p->in_ca1_pushed = TRUE;
969 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
971 pia6821_state *p = get_token(device);
973 /* limit the data to 0 or 1 */
976 LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
978 /* the new state has caused a transition */
979 if ((p->in_cb1 != data) &&
980 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
982 LOG(("PIA #%s: CB1 triggering\n", device->tag));
987 /* update externals */
988 update_interrupts(device);
990 /* If CB2 is configured as a write-strobe output which is reset by a CB1
991 transition, this reset will only happen when a read from port B implicitly
992 clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
993 is different from what happens with port A. */
996 /* set the new value for CB1 */
998 p->in_cb1_pushed = TRUE;
1001 static void update_interrupts(const device_config *device)
1003 pia6821_state *p = get_token(device);
1006 /* start with IRQ A */
1007 new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1009 if (new_state != p->irq_a_state)
1011 p->irq_a_state = new_state;
1012 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1016 new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1018 if (new_state != p->irq_b_state)
1020 p->irq_b_state = new_state;
1021 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1025 static void control_b_w(const device_config *device, UINT8 data)
1027 pia6821_state *p = get_token(device);
1030 /* bit 7 and 6 are read only */
1033 LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1035 /* update the control register */
1038 if (C2_SET_MODE(p->ctl_b))
1039 /* set/reset mode - bit value determines the new output */
1040 temp = C2_SET(p->ctl_b);
1042 /* strobe mode - output is always high unless strobed */
1045 set_out_cb2(device, temp);
1047 /* update externals */
1048 update_interrupts(device);
1051 static void control_a_w(const device_config *device, UINT8 data)
1053 pia6821_state *p = get_token(device);
1055 /* bit 7 and 6 are read only */
1058 LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1060 /* update the control register */
1063 /* CA2 is configured as output */
1064 if (C2_OUTPUT(p->ctl_a))
1068 if (C2_SET_MODE(p->ctl_a))
1069 /* set/reset mode - bit value determines the new output */
1070 temp = C2_SET(p->ctl_a);
1072 /* strobe mode - output is always high unless strobed */
1075 set_out_ca2(device, temp);
1078 /* update externals */
1079 update_interrupts(device);
1085 B7 B6 B5 B4 B3 B2 B1 B0
1086 --------------------------------------------------
1087 IRQA(B)1 IRQA(B)2 CA(B)2 Ctrl DDR CA(B)1 Ctrl
1089 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1090 DDR: 0: DDR selected, 1: Output register selected
1091 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1092 B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1093 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1095 Entering main loop...
1096 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1097 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1098 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1099 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1100 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1101 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1102 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1103 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1104 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1105 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1106 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1107 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1108 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1109 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1110 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1111 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1112 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1113 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1114 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1115 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1116 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1117 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1118 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B] --> Clear IRQAs
1119 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9] --> Clear IRQAs
1121 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
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 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1125 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1126 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1127 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1128 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1129 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
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]