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) (No, didn't)
42 #define SOUNDROM "ROMs/sg.snd"
43 #define CMOS "cmos.ram"
44 #define SAVESTATE "sg2.state"
46 #define FRAME_DURATION_IN_CYCLES (M6809_CLOCK_SPEED_IN_HZ / 60.0)
47 #define SCANLINE_DURATION_IN_CYCLES (FRAME_DURATION_IN_CYCLES / 256.0)
48 // Interesting... This (1/16) causes the machine to crash in the demo (if run from clean start, otherwise it FUs demo)!
49 // (1/32) fucks up the demo...
50 // (1/64) works. Weird!
51 //1/64 no more... but 1/128, 1/32 and 1/16 don't work either!
52 //#define SG2_PIA_CALLBACK_DURATION ((FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC) / 16.0)
53 #define SG2_PIA_CALLBACK_DURATION ((FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC) / 64.0)
57 uint8_t gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
61 uint32_t palette[256];
62 bool paletteDirty = false;
66 static bool running = true; // Machine running state flag...
67 static uint32_t startTicks;
68 static const uint8_t * keys; // SDL raw keyboard matrix
69 static uint64_t clockFrameStart; // V6809 clock at the start of the frame
71 // Function prototypes
73 uint8_t RdMem6809(uint16_t addr);
74 void WrMem6809(uint16_t addr, uint8_t b);
75 uint8_t RdMem6808(uint16_t addr);
76 void WrMem6808(uint16_t addr, uint8_t b);
77 bool LoadImg(const char * filename, uint8_t * ram, int size);
79 bool LoadMachineState(void);
80 void SaveMachineState(void);
82 // Local timer callback functions
84 static void FrameCallback(void);
85 static void ScanlineCallback(void);
91 int main(int /*argc*/, char * /*argv*/[])
93 InitLog("stargem2.log");
94 WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
95 WriteLog("(C) 2013 Underground Software\n\n");
99 // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
100 for(uint32_t i=0; i<256; i++)
102 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
103 (((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151) << 24)
104 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 16)
105 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 8) | 0xFF;
107 ((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151)
108 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 8)
109 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 16) | 0xFF000000;
113 memset(gram, 0, 0x10000);
114 memset(grom, 0, 0x10000);
115 memset(sram, 0, 0x10000);
116 memset(srom, 0, 0x10000);
118 // Set up V6809 & V6808 execution contexts
120 memset(&mainCPU, 0, sizeof(V6809REGS));
121 mainCPU.RdMem = RdMem6809;
122 mainCPU.WrMem = WrMem6809;
123 mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
125 memset(&soundCPU, 0, sizeof(V6808REGS));
126 soundCPU.RdMem = RdMem6808;
127 soundCPU.WrMem = WrMem6808;
128 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
131 "ROMs/01", "ROMs/02", "ROMs/03", "ROMs/04", "ROMs/05", "ROMs/06",
132 "ROMs/07", "ROMs/08", "ROMs/09", "ROMs/10", "ROMs/11", "ROMs/12"
135 for(int i=0; i<12; i++)
137 uint32_t baseAddress = i * 0x1000;
140 baseAddress += 0x4000;
142 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
144 WriteLog("Could not open file '%s'!\n", ROMs[i]);
149 if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
151 WriteLog("Could not open file '%s'!\n", SOUNDROM);
155 WriteLog("Stargate ROM images loaded...\n");
156 WriteLog("About to initialize video...\n");
160 cout << "Aborting!" << endl;
164 // Have to do this *after* video init but *before* sound init...!
165 WriteLog("About to load machine state...");
167 if (!LoadMachineState())
168 WriteLog("Machine state file not found!\n");
172 if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
173 WriteLog("CMOS RAM not found!\n");
175 WriteLog("About to intialize audio...\n");
177 keys = SDL_GetKeyboardState(NULL);
178 srom[0xF800] = 0x37; // Fix checksum so ST works...
179 running = true; // Set running status...
181 InitializeEventList(); // Clear the event list before we use it...
182 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
183 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
184 clockFrameStart = mainCPU.clock;
185 startTicks = SDL_GetTicks();
187 WriteLog("Entering main loop...\n");
191 double timeToNextEvent = GetTimeToNextEvent();
192 Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
198 WriteLog("$C900 = $%02X (0=RAM)\n", gram[0xC900]);
199 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);
202 /*uint16_t pc = mainCPU.pc;//0x15BA;
203 for(int i=0; i<200; i++)
204 //while (pc < 0x9000)
206 pc += Decode6809(pc);
213 pc += Decode6809(pc);
228 // Load a file into RAM/ROM image space
230 bool LoadImg(const char * filename, uint8_t * ram, int size)
232 FILE * fp = fopen(filename, "rb");
237 size_t ignoredResult = fread(ram, 1, size, fp);
248 FILE * fp = fopen(CMOS, "wb");
252 size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
256 WriteLog("CMOS RAM not saved!\n");
260 // Load state save file
262 bool LoadMachineState(void)
264 FILE * fp = fopen(SAVESTATE, "rb");
269 // This is kinda crappy--we don't do any sanity checking here!!!
270 size_t ignoredResult = fread(gram, 1, 0x10000, fp);
271 ignoredResult = fread(sram, 1, 0x10000, fp);
272 ignoredResult = fread(&mainCPU, 1, sizeof(V6809REGS), fp);
273 ignoredResult = fread(&soundCPU, 1, sizeof(V6808REGS), fp);
276 // Set up backbuffer... ;-)
277 for(uint16_t i=0x0006; i<0x97F8; i++) // Screen memory
278 WrMem6809(i, gram[i]);
280 for(uint16_t i=0xC000; i<=0xC00F; i++) // Palette memory
281 WrMem6809(i, gram[i]);
285 mainCPU.RdMem = RdMem6809; // Make sure our function pointers are
286 mainCPU.WrMem = WrMem6809; // pointing to the right places!
287 soundCPU.RdMem = RdMem6808;
288 soundCPU.WrMem = WrMem6808;
289 mainCPU.clock = 0; // Zero out our clocks...
291 mainCPU.clockOverrun = 0; // And overrun values...
292 //notyet soundCPU.clockOverrun = 0;
298 // Save state save file
300 void SaveMachineState(void)
302 FILE * fp = fopen(SAVESTATE, "wb");
306 size_t ignoredResult = fwrite(gram, 1, 0x10000, fp);
307 ignoredResult = fwrite(sram, 1, 0x10000, fp);
308 ignoredResult = fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
309 ignoredResult = fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
313 WriteLog("Machine state not saved!\n");
317 // 6809 memory functions
321 char piaRegsName[4][10] = { "PORTA", "PACTL", "PORTB", "PBCTL" };
323 uint8_t RdMem6809(uint16_t addr)
327 if (addr >= 0x9000 && addr <= 0xCFFF) // No ROM between $9000 - $CFFF...
331 if (!gram[0xC900] && addr <= 0x8FFF) // Check RAM $C900 bank switch
337 // A wee kludge (though I doubt it reads from anywhere other than $CB00)...
338 if ((addr & 0xFF00) == 0xCB00)
340 b = gram[0xCB00] & 0xFC; // Only bits 2-7 are connected...
343 //Interesting, this code ALSO fucks up the demo...
344 //Except when the correct code is called in the scanline callback function...
345 uint32_t elapsedCycles = (uint32_t)(GetCurrentV6809Clock() - clockFrameStart);
346 uint32_t scanline = (uint32_t)((double)elapsedCycles / SCANLINE_DURATION_IN_CYCLES);
347 //Changes here don't seem to do much...
348 // uint32_t scanline = (uint32_t)(((double)elapsedCycles * M6809_CYCLE_IN_USEC) / 70.0);
349 b = (uint8_t)scanline & 0xFC; // Only bits 2-7 are connected...
354 if ((addr == 0xC80C) && (gram[0xC80D] & 0x04)) // Read PORTA and DDR is set to Output
356 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
357 //OK, this ALSO fucks up the execution of the demo...
358 // Which means that the timing is still off. :-/
359 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
362 if ((addr == 0xC80E) && (gram[0xC80F] & 0x04)) // Read PORTB and DDR is set to Output
364 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
365 //OK, this ALSO fucks up the execution of the demo...
366 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; // Then clear the IRQ
370 /*extern uint16_t pcr;
371 //if (addr >= 0xC000 && addr <= 0xCBFF)
373 WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
375 /*if (addr >= 0xC80C && addr <= 0xC80F)
376 WriteLog("V6809 RdMem: Reading PIA (%s) address %04X [<-%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
381 void WrMem6809(uint16_t addr, uint8_t b)
384 //extern V6809REGS regs;
385 //if (addr >= 0xC800 && addr <= 0xCBFE)
386 //if (addr == 0xC80F || addr == 0xC80D)
387 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
388 //if (addr == 0xC80E)
389 /*if (addr >= 0xC800 && addr <= 0xC80F)
390 WriteLog("V6809 WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, mainCPU.pc, gram[0xCB00]);//*/
394 if (addr >= 0x0006 && addr < 0x97F7) // 304 pixels 152-128=24-16=8
396 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
397 uint16_t sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
399 if (sy > 5 && sy < 246)
401 uint32_t saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
402 scrBuffer[saddr + 0] = palette[color[b >> 4]];
403 scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
406 else if (addr >= 0xC000 && addr <= 0xC00F)
408 // This approach doesn't take the BG color to the edges of the screen
409 color[addr - 0xC000] = b;
412 else if (addr == 0xC80E)
414 sram[0x0402] = b; // Connect PIAs in 6809 & 6808
415 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ; // Start sound IRQ
419 //if (addr >= 0xC80C && addr <= 0xC80F)
421 WriteLog("V6809 WrMem: Writing PIA (%s) address %04X [->%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
426 // 6808 memory functions
429 uint8_t RdMem6808(uint16_t addr)
431 return (addr < 0xF000 ? sram[addr] : srom[addr]);
434 void WrMem6808(uint16_t addr, uint8_t b)
438 // A total guess, but let's try it...
439 //It probably depends on how the PIA is configured, so this is most likely wrong.
440 // It is wrong: IRQs are cleared on PIA PORTx reads!
441 // if (addr == 0x0401)
442 // soundCPU.cpuFlags &= ~V6808_ASSERT_LINE_IRQ;
445 static void FrameCallback(void)
447 SDL_PumpEvents(); // Force key events into the buffer.
448 gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
450 if (keys[SDL_SCANCODE_ESCAPE])
451 running = false; // ESC to exit...
453 if (keys[settings.keyBindings[S_KEY_FIRE]]) gram[0xC804] |= 0x01;
454 if (keys[settings.keyBindings[S_KEY_THRUST]]) gram[0xC804] |= 0x02;
455 if (keys[settings.keyBindings[S_KEY_SMARTBOMB]]) gram[0xC804] |= 0x04;
456 if (keys[settings.keyBindings[S_KEY_HYPERSPACE]]) gram[0xC804] |= 0x08;
457 if (keys[settings.keyBindings[S_KEY_2P_START]]) gram[0xC804] |= 0x10;
458 if (keys[settings.keyBindings[S_KEY_1P_START]]) gram[0xC804] |= 0x20;
459 if (keys[settings.keyBindings[S_KEY_REVERSE]]) gram[0xC804] |= 0x40;
460 if (keys[settings.keyBindings[S_KEY_DOWN]]) gram[0xC804] |= 0x80;
462 if (keys[settings.keyBindings[S_KEY_UP]]) gram[0xC806] |= 0x01;
463 if (keys[settings.keyBindings[S_KEY_INVISO]]) gram[0xC806] |= 0x02;
465 if (keys[settings.keyBindings[S_KEY_AUTO_UP]]) gram[0xC80C] |= 0x01;
466 if (keys[settings.keyBindings[S_KEY_ADVANCE]]) gram[0xC80C] |= 0x02;
467 if (keys[settings.keyBindings[S_KEY_RIGHT_COIN]]) gram[0xC80C] |= 0x04;
468 if (keys[settings.keyBindings[S_KEY_HS_RESET]]) gram[0xC80C] |= 0x08;
469 if (keys[settings.keyBindings[S_KEY_LEFT_COIN]]) gram[0xC80C] |= 0x10;
470 if (keys[settings.keyBindings[S_KEY_CENTER_COIN]]) gram[0xC80C] |= 0x20;
471 if (keys[settings.keyBindings[S_KEY_SLAM_SWITCH]]) gram[0xC80C] |= 0x40;
473 if (keys[SDL_SCANCODE_F5]) // Sound CPU self-test (F5)
474 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
475 if (keys[SDL_SCANCODE_F6]) // Reset the 6808 (F6)
476 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
477 //Temp, for testing...
480 if (keys[SDL_SCANCODE_F9])
485 for(uint32_t addr=0x0006; addr<0x97F7; addr++)
487 uint16_t sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
489 if (sy > 5 && sy < 246)
491 uint32_t saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
492 uint8_t sb = gram[addr];
494 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
495 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
499 paletteDirty = false;
502 static bool fullscreenDebounce = false;
504 if (keys[SDL_SCANCODE_F12])
506 if (!fullscreenDebounce)
509 fullscreenDebounce = true;
513 fullscreenDebounce = false;
515 RenderScreenBuffer(); // 1 frame = 1/60 sec ~ 16667 cycles
516 clockFrameStart = mainCPU.clock;
518 // Wait for next frame...
519 while (SDL_GetTicks() - startTicks < 16)
522 startTicks = SDL_GetTicks();
523 SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
526 static void ScanlineCallback(void)
529 if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
530 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
532 mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
534 if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
535 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
539 The problem is that this is already asserted above, by virtue of the fact that
540 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
541 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
542 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
544 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
546 // if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09)) // Do COUNT240 IRQ (if enabled!)
547 // mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
549 // This should set everything between $CB00-CBFF...
550 // gram[0xCB00] += 8; // Update video counter...
551 // gram[0xCB00] += 4; // Update video counter...
553 SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
558 ; With correct timing, but no color cycling
560 --> Start of frame...
561 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
566 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
567 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
575 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
576 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
577 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
580 ; With incorrect timing, but has color cycling
582 --> Start of frame...
583 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
584 At $1609. $6E: 00 ; Color cycling...
589 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
590 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
591 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
592 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
600 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
601 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
602 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
609 0000-8FFF ROM (for Blaster, 0000-3FFF is a bank of 12 ROMs)
610 0000-97FF Video RAM Bank switched with ROM (96FF for Blaster)
612 0xBB00 Blaster only, Color 0 for each line (256 entry)
613 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
614 Do something else with the bit 1, I do not know what
618 C000-C00F color_registers (16 bytes of BBGGGRRR)
620 C804 widget_pia_dataa (widget = I/O board)
621 C805 widget_pia_ctrla
622 C806 widget_pia_datab
623 C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
624 controls if Table or Joust)
625 bits 5-3 = 110 = player 2
626 bits 5-3 = 111 = player 1
633 bit 2 |-6 bits to sound board
638 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
641 C900 rom_enable_scr_ctrl Switch between video ram and rom at 0000-97FF
645 C804 widget_pia_dataa (widget = I/O board)
655 C806 widget_pia_datab
663 bit 7 0 = Upright 1 = Table
668 bit 2 Right Coin (High Score Reset in schematics)
669 bit 3 High Score Reset (Left Coin in schematics)
670 bit 4 Left Coin (Center Coin in schematics)
671 bit 5 Center Coin (Right Coin in schematics)
673 bit 7 Hand Shake from sound board
679 static MEMORY_READ_START( williams_readmem )
680 { 0x0000, 0x97ff, MRA_BANK1 },
681 { 0x9800, 0xbfff, MRA_RAM },
682 { 0xc804, 0xc807, pia_0_r },
683 { 0xc80c, 0xc80f, pia_1_r },
684 { 0xcb00, 0xcb00, williams_video_counter_r },
685 { 0xcc00, 0xcfff, MRA_RAM },
686 { 0xd000, 0xffff, MRA_ROM },
690 static MEMORY_WRITE_START( williams_writemem )
691 { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
692 { 0x9800, 0xbfff, MWA_RAM },
693 { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
694 { 0xc804, 0xc807, pia_0_w },
695 { 0xc80c, 0xc80f, pia_1_w },
696 { 0xc900, 0xc900, williams_vram_select_w },
697 { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
698 { 0xcbff, 0xcbff, watchdog_reset_w },
699 { 0xcc00, 0xcfff, MWA_RAM },
700 { 0xd000, 0xffff, MWA_ROM },
703 static MEMORY_READ_START( sound_readmem )
704 { 0x0000, 0x007f, MRA_RAM },
705 { 0x0400, 0x0403, pia_2_r },
706 { 0x8400, 0x8403, pia_2_r }, // used by Colony 7, perhaps others?
707 { 0xb000, 0xffff, MRA_ROM }, // most games start at $F000, Sinistar starts at $B000
711 static MEMORY_WRITE_START( sound_writemem )
712 { 0x0000, 0x007f, MWA_RAM },
713 { 0x0400, 0x0403, pia_2_w },
714 { 0x8400, 0x8403, pia_2_w }, // used by Colony 7, perhaps others?
715 { 0xb000, 0xffff, MWA_ROM }, // most games start at $F000, Sinistar starts at $B000
718 MACHINE_INIT( williams )
723 // reset the ticket dispenser (Lotto Fun)
724 ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
726 // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
727 timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
729 // also set a timer to go off on scanline 240
730 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
734 static void williams_va11_callback(int scanline)
736 // the IRQ signal comes into CB1, and is set to VA11
737 pia_1_cb1_w(0, scanline & 0x20);
739 // update the screen while we're here
740 force_partial_update(scanline - 1);
742 // set a timer for the next update
744 if (scanline >= 256) scanline = 0;
745 timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
749 static void williams_count240_off_callback(int param)
751 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
756 static void williams_count240_callback(int param)
758 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
761 // set a timer to turn it off once the scanline counter resets
762 timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
764 // set a timer for next frame
765 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
769 static void williams_main_irq(int state)
771 // IRQ to the main CPU
772 cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
776 static void williams_main_firq(int state)
778 // FIRQ to the main CPU
779 cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
783 static void williams_snd_irq(int state)
785 // IRQ to the sound CPU
786 cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
790 READ_HANDLER( williams_video_counter_r )
792 return cpu_getscanline() & 0xFC;
796 // Special PIA 0 for Stargate, to handle the controls
797 struct pia6821_interface stargate_pia_0_intf =
799 //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
800 //outputs: A/B,CA/B2 / 0, 0, 0, 0,
804 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
805 struct pia6821_interface williams_pia_1_intf =
807 //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
808 //outputs: A/B,CA/B2 / 0, williams_snd_cmd_w, 0, 0,
809 //irqs : A/B / williams_main_irq, williams_main_irq
812 // Generic PIA 2, maps to DAC data in and sound IRQs
813 struct pia6821_interface williams_snd_pia_intf =
815 //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
816 //outputs: A/B,CA/B2 / DAC_0_data_w, 0, 0, 0,
817 //irqs : A/B / williams_snd_irq, williams_snd_irq
820 static DRIVER_INIT( stargate )
822 // CMOS configuration
823 CONFIGURE_CMOS(0xCC00, 0x400);
826 CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
830 int cpu_getscanline(void)
832 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
835 *************************************
837 * Returns time until given scanline
839 *************************************
841 double cpu_getscanlinetime(int scanline)
843 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
844 double abstime = timer_get_time();
847 // if we're already past the computed time, count it for the next frame
848 if (abstime >= scantime)
849 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
851 // compute how long from now until that time
852 result = scantime - abstime;
854 // if it's small, just count a whole frame
855 if (result < TIME_IN_NSEC(1))
856 result = TIME_IN_HZ(Machine->drv->frames_per_second);
860 *************************************
862 * Returns time for one scanline
864 *************************************
866 double cpu_getscanlineperiod(void)
868 return scanline_period;
872 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
873 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
874 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
876 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
877 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
878 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
880 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
881 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
882 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
883 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
885 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
886 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
887 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
888 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
890 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
891 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
892 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
893 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
895 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
896 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
897 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
898 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
900 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
901 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
902 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
903 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
905 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
906 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
907 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
909 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
910 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
911 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
912 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
919 bit 2 |-6 bits to sound board
924 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
927 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
932 00 -> $C80D = PIA2 -> DDR active
933 00 -> $C80C = PIA2 DDR -> All input?
941 #define PIA_IRQ1 (0x80)
942 #define PIA_IRQ2 (0x40)
944 #define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
945 #define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
946 #define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
947 #define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
948 #define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
949 #define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
950 #define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
951 #define C2_SET(c) ( (((c) >> 3) & 0x01))
952 #define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
953 #define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
954 #define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
955 #define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
956 #define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
957 #define C2_INPUT(c) (!(((c) >> 5) & 0x01))
959 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
961 pia6821_state *p = get_token(device);
963 /* limit the data to 0 or 1 */
964 data = data ? TRUE : FALSE;
966 LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
968 /* the new state has caused a transition */
969 if ((p->in_ca1 != data) &&
970 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
972 LOG(("PIA #%s: CA1 triggering\n", device->tag));
977 /* update externals */
978 update_interrupts(device);
980 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
981 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
982 set_out_ca2(device, TRUE);
985 /* set the new value for CA1 */
987 p->in_ca1_pushed = TRUE;
990 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
992 pia6821_state *p = get_token(device);
994 /* limit the data to 0 or 1 */
997 LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
999 /* the new state has caused a transition */
1000 if ((p->in_cb1 != data) &&
1001 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
1003 LOG(("PIA #%s: CB1 triggering\n", device->tag));
1008 /* update externals */
1009 update_interrupts(device);
1011 /* If CB2 is configured as a write-strobe output which is reset by a CB1
1012 transition, this reset will only happen when a read from port B implicitly
1013 clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
1014 is different from what happens with port A. */
1017 /* set the new value for CB1 */
1019 p->in_cb1_pushed = TRUE;
1022 static void update_interrupts(const device_config *device)
1024 pia6821_state *p = get_token(device);
1027 /* start with IRQ A */
1028 new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1030 if (new_state != p->irq_a_state)
1032 p->irq_a_state = new_state;
1033 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1037 new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1039 if (new_state != p->irq_b_state)
1041 p->irq_b_state = new_state;
1042 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1046 static void control_b_w(const device_config *device, UINT8 data)
1048 pia6821_state *p = get_token(device);
1051 /* bit 7 and 6 are read only */
1054 LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1056 /* update the control register */
1059 if (C2_SET_MODE(p->ctl_b))
1060 /* set/reset mode - bit value determines the new output */
1061 temp = C2_SET(p->ctl_b);
1063 /* strobe mode - output is always high unless strobed */
1066 set_out_cb2(device, temp);
1068 /* update externals */
1069 update_interrupts(device);
1072 static void control_a_w(const device_config *device, UINT8 data)
1074 pia6821_state *p = get_token(device);
1076 /* bit 7 and 6 are read only */
1079 LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1081 /* update the control register */
1084 /* CA2 is configured as output */
1085 if (C2_OUTPUT(p->ctl_a))
1089 if (C2_SET_MODE(p->ctl_a))
1090 /* set/reset mode - bit value determines the new output */
1091 temp = C2_SET(p->ctl_a);
1093 /* strobe mode - output is always high unless strobed */
1096 set_out_ca2(device, temp);
1099 /* update externals */
1100 update_interrupts(device);
1106 B7 B6 B5 B4 B3 B2 B1 B0
1107 --------------------------------------------------
1108 IRQA(B)1 IRQA(B)2 CA(B)2 Ctrl DDR CA(B)1 Ctrl
1110 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1111 DDR: 0: DDR selected, 1: Output register selected
1112 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1113 B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1114 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1116 Entering main loop...
1117 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1118 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1119 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1120 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1121 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1122 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1123 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1124 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1125 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1126 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1127 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1128 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1129 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1130 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1131 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1132 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1133 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1134 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1135 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1136 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1137 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1138 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1139 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B] --> Clear IRQAs
1140 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9] --> Clear IRQAs
1142 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1143 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1144 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1145 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1146 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1147 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1148 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1149 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1150 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1151 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1152 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1153 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1154 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1155 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1156 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1157 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1158 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1159 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]