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
40 #define SOUNDROM "ROMs/sg.snd"
41 #define CMOS "cmos.ram"
42 #define SAVESTATE "sg2.state"
46 uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
51 bool paletteDirty = false;
55 static bool running = true; // Machine running state flag...
56 static uint32 startTicks;
57 static uint8 * keys; // SDL raw keyboard matrix
59 // Local timer callback functions
61 static void FrameCallback(void);
62 static void ScanlineCallback(void);
66 // 6809 memory functions
70 char piaRegsName[4][10] = { "PORTA", "PACTL", "PORTB", "PBCTL" };
72 uint8 RdMem6809(uint16 addr)
76 if (addr >= 0x9000 && addr <= 0xCFFF) // No ROM between $9000 - $CFFF...
80 if (!gram[0xC900] && addr <= 0x8FFF) // Check RAM $C900 bank switch
86 // A wee kludge (though I doubt it reads from anywhere other than $CB00)...
87 if ((addr & 0xFF00) == 0xCB00)
88 b = gram[0xCB00] & 0xFC; // Only bits 2-7 are connected...
91 if ((addr == 0xC80C) && (gram[0xC80D] & 0x04)) // Read PORTA and DDR is set to Output
92 ClearLine(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
94 if ((addr == 0xC80E) && (gram[0xC80F] & 0x04)) // Read PORTB and DDR is set to Output
95 ClearLine(V6809_ASSERT_LINE_IRQ); // Then clear the IRQ
99 //if (addr >= 0xC000 && addr <= 0xCBFF)
101 WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
103 /*if (addr >= 0xC80C && addr <= 0xC80F)
104 WriteLog("V6809 RdMem: Reading PIA (%s) address %04X [<-%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
109 void WrMem6809(uint16 addr, uint8 b)
112 //extern V6809REGS regs;
113 //if (addr >= 0xC800 && addr <= 0xCBFE)
114 //if (addr == 0xC80F || addr == 0xC80D)
115 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
116 //if (addr == 0xC80E)
117 /*if (addr >= 0xC800 && addr <= 0xC80F)
118 WriteLog("V6809 WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, mainCPU.pc, gram[0xCB00]);//*/
122 if (addr > 0x0006 && addr < 0x97F7) // 304 pixels 152-128=24-16=8
124 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
125 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
127 if (sy > 5 && sy < 246)
129 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
130 //Hmm. This approach won't work with palette color cycling...
132 scrBuffer[saddr + 0] = b >> 4;
133 scrBuffer[saddr + 1] = b & 0x0F;
135 scrBuffer[saddr + 0] = palette[color[b >> 4]];
136 scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
140 else if (addr >= 0xC000 && addr <= 0xC00F)
141 //Let's see if we can fix the color cycling here... [DONE]
143 color[addr - 0xC000] = b; // color[] from VIDEO.CPP (not any more!)
146 // A better strategy here would probably be to set a flag when the color register changes,
147 // then change it before doing the render.
148 // ALSO: This approach doesn't take the color to the edges of the screen
149 #warning "This should only touch memory right before a render. !!! FIX !!!"
150 color[addr - 0xC000] = b;
153 for(uint32 addr=0x0007; addr<0x97F7; addr++)
155 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
157 if (sy > 5 && sy < 246)
159 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
160 uint8 sb = gram[addr];
162 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
163 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
171 else if (addr == 0xC80E)
173 sram[0x0402] = b; // Connect PIAs in 6809 & 6808
174 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ; // Start sound IRQ
178 //if (addr >= 0xC80C && addr <= 0xC80F)
180 WriteLog("V6809 WrMem: Writing PIA (%s) address %04X [->%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
185 // 6808 memory functions
188 uint8 RdMem6808(uint16 addr)
190 return (addr < 0xF000 ? sram[addr] : srom[addr]);
193 void WrMem6808(uint16 addr, uint8 b)
197 // A total guess, but let's try it...
198 //It probably depends on how the PIA is configured, so this is most likely wrong.
199 // if (addr == 0x0401)
200 // soundCPU.cpuFlags &= ~V6808_ASSERT_LINE_IRQ;
204 // Load a file into RAM/ROM image space
206 bool LoadImg(const char * filename, uint8 * ram, int size)
208 FILE * fp = fopen(filename, "rb");
213 size_t ignoredResult = fread(ram, 1, size, fp);
224 FILE * fp = fopen(CMOS, "wb");
228 size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
232 WriteLog("CMOS RAM not saved!\n");
236 // Load state save file
238 bool LoadMachineState(void)
240 FILE * fp = fopen(SAVESTATE, "rb");
245 // This is kinda crappy--we don't do any sanity checking here!!!
246 size_t ignoredResult = fread(gram, 1, 0x10000, fp);
247 ignoredResult = fread(sram, 1, 0x10000, fp);
248 ignoredResult = fread(&mainCPU, 1, sizeof(V6809REGS), fp);
249 ignoredResult = fread(&soundCPU, 1, sizeof(V6808REGS), fp);
252 for(int i=0x0006; i<0x97F8; i++) // Set up backbuffer... ;-)
253 WrMem6809(i, gram[i]);
255 mainCPU.RdMem = RdMem6809; // Make sure our function pointers are
256 mainCPU.WrMem = WrMem6809; // pointing to the right places!
257 soundCPU.RdMem = RdMem6808;
258 soundCPU.WrMem = WrMem6808;
264 // Save state save file
266 void SaveMachineState(void)
268 FILE * fp = fopen(SAVESTATE, "wb");
272 size_t ignoredResult = fwrite(gram, 1, 0x10000, fp);
273 ignoredResult = fwrite(sram, 1, 0x10000, fp);
274 ignoredResult = fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
275 ignoredResult = fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
279 WriteLog("Machine state not saved!\n");
285 int main(int /*argc*/, char * /*argv*/[])
287 InitLog("stargem2.log");
288 WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
292 // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
293 for(uint32 i=0; i<256; i++)
295 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
296 (((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151) << 24)
297 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 16)
298 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 8) | 0xFF;
300 ((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151)
301 | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 8)
302 | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 16) | 0xFF000000;
306 // for(long i=0; i<0x10000; i++)
307 // gram[i] = grom[i] = sram[i] = srom[i] = 0;
308 memset(gram, 0, 0x10000);
309 memset(grom, 0, 0x10000);
310 memset(sram, 0, 0x10000);
311 memset(srom, 0, 0x10000);
313 // Set up V6809 & V6808 execution contexts
315 memset(&mainCPU, 0, sizeof(V6809REGS));
316 mainCPU.RdMem = RdMem6809;
317 mainCPU.WrMem = WrMem6809;
318 mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
320 memset(&soundCPU, 0, sizeof(V6808REGS));
321 soundCPU.RdMem = RdMem6808;
322 soundCPU.WrMem = WrMem6808;
323 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
326 "ROMs/01", "ROMs/02", "ROMs/03", "ROMs/04", "ROMs/05", "ROMs/06",
327 "ROMs/07", "ROMs/08", "ROMs/09", "ROMs/10", "ROMs/11", "ROMs/12"
330 for(int i=0; i<12; i++)
332 uint32 baseAddress = i * 0x1000;
335 baseAddress += 0x4000;
338 WriteLog("Loading ROM image '%s' at $%04X...\n", ROMs[i], baseAddress);
340 // if (!LoadImg(ROMs[i], grom + (i * 0x1000), 0x1000))
341 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
343 WriteLog("Could not open file '%s'!\n", ROMs[i]);
348 if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
350 WriteLog("Could not open file '%s'!\n", SOUNDROM);
354 WriteLog("Stargate ROM images loaded...\n");
355 WriteLog("About to initialize video...\n");
359 cout << "Aborting!" << endl;
363 // Have to do this *after* video init but *before* sound init...!
364 WriteLog("About to load machine state...");
366 if (!LoadMachineState())
367 WriteLog("Machine state file not found!\n");
371 if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
372 WriteLog("CMOS RAM not found!\n");
374 WriteLog("About to intialize audio...\n");
376 // uint8 * keys = SDL_GetKeyState(NULL);
377 keys = SDL_GetKeyState(NULL);
379 // running = true; // Set running status...
380 srom[0xF800] = 0x37; // Fix checksum so ST works...
385 //This didn't work--it still acted like the old way (interrupt @ VC = 0)
386 //gram[0xCB00] = 64*3;
388 WriteLog("Entering main loop...\n");
391 SDL_PumpEvents(); // Force key events into the buffer.
392 gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
394 if (keys[SDLK_ESCAPE])
395 running = false; // ESC to exit...
397 if (keys[SDLK_SEMICOLON])
398 gram[0xC804] |= 0x01; // Fire (;)
400 gram[0xC804] |= 0x02; // Thrust (L)
401 if (keys[SDLK_SPACE])
402 gram[0xC804] |= 0x04; // Smart Bomb (space)
403 if (keys[SDLK_BACKSPACE])
404 gram[0xC804] |= 0x08; // Hyperspace (BkSp)
406 gram[0xC804] |= 0x10; // Two Player Start (2)
408 gram[0xC804] |= 0x20; // One Player Start (1)
409 if (keys[SDLK_RETURN])
410 gram[0xC804] |= 0x40; // Reverse (Enter)
412 gram[0xC804] |= 0x80; // Down (F)
415 gram[0xC806] |= 0x01; // Up (R)
417 gram[0xC806] |= 0x02; // Inviso (A)
420 gram[0xC80C] |= 0x01; // Auto up (F1)
422 gram[0xC80C] |= 0x02; // Advance (F2)
424 gram[0xC80C] |= 0x04; // Right Coin (5)
426 gram[0xC80C] |= 0x08; // High Score Reset (F3)
428 gram[0xC80C] |= 0x10; // Left Coin (3)
430 gram[0xC80C] |= 0x20; // Center Coin (4)
432 gram[0xC80C] |= 0x40; // Slam Switch (F4)
434 if (keys[SDLK_F5]) // Sound CPU self-test (F5)
435 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
436 if (keys[SDLK_F6]) // Reset the 6808 (F6)
437 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
440 $CB00 is scanline counter, bits 2-7 (1 frame/240 =69.44... usec)
442 Some places of interest to look at:
444 RdMem: Reading address C80E [=0C, PC=15C3] <- Inside interrupt (read, then discarded)...
445 RdMem: Reading address CB00 [=43, PC=15C6] <- interrupt
446 RdMem: Reading address C80C [=00, PC=0758] <- input (?)
447 RdMem: Reading address C80C [=00, PC=07B9] <- input (?)
448 RdMem: Reading address C806 [=00, PC=078C] <- input
449 RdMem: Reading address C804 [=00, PC=2679] <- input
451 uint32 startTicks = SDL_GetTicks();
452 // long video_clk = 0;
456 //This is where the interrupt mask is restored in CC... Hmm...
457 //This enables interrupts *after* the previous interrupt has occurred... Hmm.
458 //Could $C80F (rom_pia_ctrlb) be the IRQ inhibit? Yes, it is!
460 // the IRQ signal comes into CB1, and is set to VA11
461 pia_1_cb1_w(0, scanline & 0x20);
463 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
466 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
470 //WriteLog("--> Start of frame...\n");
471 for(int i=0; i<3; i++)
473 //Not sure, but this *might* fix IRQ problem...
474 //Checking the PIA IRQ mask for an IRQ seems to work OK. Now if only the timing elsewhere was right...
475 if (gram[0xC80F] & 0x01)
476 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
478 Execute6809(&mainCPU, 4000);
479 mainCPU.clock -= 4000; // Remove 4K ticks from clock (in case it overflowed)
480 //Not sure, but this *might* fix IRQ problem...
481 //Checking the PIA IRQ mask for an IRQ seems to work OK. Now if only the timing elsewhere was right...
482 /* if (gram[0xC80F] & 0x01)
483 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
485 gram[0xCB00] += 64; // Update video counter...
489 /*if (gram[0xC80E] & 0x01)
490 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
493 Execute6809(&mainCPU, 3000);
494 mainCPU.clock -= 3000; // Remove 3K ticks from clock (in case it overflowed)
495 //Not sure, but this *might* fix IRQ problem...
496 //if (gram[0xC80F] & 0x01)
497 //This isn't the right port on the PIA, but it does seem to make it through the demo now...
498 //Lesse if this works... Seems to!
499 if (gram[0xC80D] & 0x01) // Do COUNT240 IRQ (if enabled!)
500 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
502 /*if (gram[0xC80F] & 0x01)
503 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
504 gram[0xCB00] = 0; //*/
506 gram[0xCB00] += 48; // Update video counter...
508 Execute6809(&mainCPU, 1000);
509 mainCPU.clock -= 1000; // Remove 1K ticks from clock (in case it overflowed)
511 //Ok, this is the interrupt it's looking for, but still...
512 //if (gram[0xC80F] & 0x01)
513 // mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
515 gram[0xCB00] += 16; // Update video counter...
517 // RenderScreenBuffer();
518 RenderScreenBuffer2(); // 1 frame = 16667 cycles
519 // WriteLog("Main: Rendered back buffer. [6809 PC=%04X]\n", pcr);
521 Execute6809(&mainCPU, 667); // Do QnD VBLANK
523 // 1/60 sec = ? ms (16.6 ms)
524 while (SDL_GetTicks() - startTicks < 16); // Wait for next frame...
527 //Very interesting! It's the palette rotation that's slowing it down!
528 //Fixed now, but this allows the color rotation while the wrong timing is in effect...
529 /*for(int i=0; i<16; i++)
530 WrMem(0xC000 + i, gram[0x9C26 + i]);//*/
533 /*uint16 pc = 0x15BA;
534 for(int i=0; i<200; i++)
535 //while (pc < 0x9000)
536 pc += Decode6809(pc);//*/
540 running = true; // Set running status...
542 InitializeEventList(); // Clear the event list before we use it...
543 SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
544 // SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 s intervals
545 // SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
546 SetCallbackTime(ScanlineCallback, 520.83333334/2.0); // Set scanline callback at 1/64 of frame
547 // SetCallbackTime(ScanlineCallback, 520.83333334*32.00); // Set scanline callback at 1/32 of frame
548 startTicks = SDL_GetTicks();
550 WriteLog("Entering main loop...\n");
554 double timeToNextEvent = GetTimeToNextEvent();
555 Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
556 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
557 //(Fix so that this is not a requirement!)
558 mainCPU.clock -= USEC_TO_M6809_CYCLES(timeToNextEvent);
564 WriteLog("$C900 = $%02X (0=RAM)\n", gram[0xC900]);
565 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);
568 /*uint16 pc = mainCPU.pc;//0x15BA;
569 for(int i=0; i<200; i++)
570 //while (pc < 0x9000)
572 pc += Decode6809(pc);
579 pc += Decode6809(pc);
595 static void FrameCallback(void)
597 SDL_PumpEvents(); // Force key events into the buffer.
598 gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
600 if (keys[SDLK_ESCAPE])
601 running = false; // ESC to exit...
603 //Convert this stuff to use the settings module... !!! FIX !!!
604 if (keys[SDLK_SEMICOLON])
605 gram[0xC804] |= 0x01; // Fire (;)
607 gram[0xC804] |= 0x02; // Thrust (L)
608 if (keys[SDLK_SPACE])
609 gram[0xC804] |= 0x04; // Smart Bomb (space)
610 if (keys[SDLK_BACKSPACE])
611 gram[0xC804] |= 0x08; // Hyperspace (BkSp)
613 gram[0xC804] |= 0x10; // Two Player Start (2)
615 gram[0xC804] |= 0x20; // One Player Start (1)
616 if (keys[SDLK_RETURN])
617 gram[0xC804] |= 0x40; // Reverse (Enter)
619 gram[0xC804] |= 0x80; // Down (F)
622 gram[0xC806] |= 0x01; // Up (R)
624 gram[0xC806] |= 0x02; // Inviso (A)
627 gram[0xC80C] |= 0x01; // Auto up (F1)
629 gram[0xC80C] |= 0x02; // Advance (F2)
631 gram[0xC80C] |= 0x04; // Right Coin (5)
633 gram[0xC80C] |= 0x08; // High Score Reset (F3)
635 gram[0xC80C] |= 0x10; // Left Coin (3)
637 gram[0xC80C] |= 0x20; // Center Coin (4)
639 gram[0xC80C] |= 0x40; // Slam Switch (F4)
641 if (keys[SDLK_F5]) // Sound CPU self-test (F5)
642 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
643 if (keys[SDLK_F6]) // Reset the 6808 (F6)
644 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
648 for(int i=0; i<16; i++)
649 WrMem6809(0xC000 + i, gram[0x9C26 + i]);//*/
654 for(uint32 addr=0x0007; addr<0x97F7; addr++)
656 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
658 if (sy > 5 && sy < 246)
660 uint32 saddr = 8 + sx + ((sy - 6) * 320); // Calc screen address
661 uint8 sb = gram[addr];
663 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
664 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
668 paletteDirty = false;
671 RenderScreenBuffer(); // 1 frame = 1/60 sec ~ 16667 cycles
672 SetCallbackTime(FrameCallback, 16666.66666667);
674 //Hmm. Yield some time?
675 //This works, but doesn't seem to yield much CPU--maybe 10%
677 //The following works much better--yields as much as 50%
678 // Wait for next frame...
679 while (SDL_GetTicks() - startTicks < 16)
682 startTicks = SDL_GetTicks();
685 static void ScanlineCallback(void)
687 // CA1 of PIA 1 maps to $C80C-F... <-- Count240 is in PIA1...
688 // What about COUNT240???
689 // COUNT240 asserts between scanlines 240-256, and clears everywhere else. so !!! FIX !!!
691 // NOTE that this is writing to CA1!!!
692 pia_1_ca1_w(0, 0); // COUNT240 off
693 pia_1_ca1_w(0, 1); // COUNT240 on
694 pia_1_cb1_w(0, scanline & 0x20); // Signal into CB1
695 // NOTE: The reads to $CB00 are at a granularity of 4, not 8
696 { 0xcb00, 0xcb00, williams_video_counter_r },
697 READ_HANDLER( williams_video_counter_r )
699 return cpu_getscanline() & 0xFC;
702 // mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
704 //wil wok? Yes, but still screws up on the demo...
705 /* if (gram[0xCB00] & 0x20)
706 if (gram[0xC80F] & 0x01)
707 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
709 if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
710 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
712 /* if ((gram[0xCB00] >= 0xF0) && (gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
713 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
715 // Hmm. No. But this *should* do it... Why doesn't it???
717 The problem is that this is already asserted above, by virtue of the fact that
718 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
719 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
720 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
722 if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09)) // Do COUNT240 IRQ (if enabled!)
723 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
725 //Is $C80E COUNT240? Hmm... Doesn't seem to be. Bleh.
726 /* if (gram[0xCB00] >= 240)
729 gram[0xC80E] = 0x00;//*/
730 // gram[0xC80E] = (gram[0xCB00] >= 240 ? 0xFF : 0x00);
732 // This should set everything between $CB00-CBFF...
733 // gram[0xCB00] += 8; // Update video counter...
734 gram[0xCB00] += 4; // Update video counter...
736 // SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
737 SetCallbackTime(ScanlineCallback, 520.83333334/2.0); // Set scanline callback at 1/64 of frame
742 ; With correct timing, but no color cycling
744 --> Start of frame...
745 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
750 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
751 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
759 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
760 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
761 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
764 ; With incorrect timing, but has color cycling
766 --> Start of frame...
767 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
768 At $1609. $6E: 00 ; Color cycling...
773 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
774 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
775 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
776 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
784 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
785 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
786 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
793 0000-8FFF ROM (for Blaster, 0000-3FFF is a bank of 12 ROMs)
794 0000-97FF Video RAM Bank switched with ROM (96FF for Blaster)
796 0xBB00 Blaster only, Color 0 for each line (256 entry)
797 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
798 Do something else with the bit 1, I do not know what
802 C000-C00F color_registers (16 bytes of BBGGGRRR)
804 C804 widget_pia_dataa (widget = I/O board)
805 C805 widget_pia_ctrla
806 C806 widget_pia_datab
807 C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
808 controls if Table or Joust)
809 bits 5-3 = 110 = player 2
810 bits 5-3 = 111 = player 1
817 bit 2 |-6 bits to sound board
822 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
825 C900 rom_enable_scr_ctrl Switch between video ram and rom at 0000-97FF
829 C804 widget_pia_dataa (widget = I/O board)
839 C806 widget_pia_datab
847 bit 7 0 = Upright 1 = Table
852 bit 2 Right Coin (High Score Reset in schematics)
853 bit 3 High Score Reset (Left Coin in schematics)
854 bit 4 Left Coin (Center Coin in schematics)
855 bit 5 Center Coin (Right Coin in schematics)
857 bit 7 Hand Shake from sound board
863 static MEMORY_READ_START( williams_readmem )
864 { 0x0000, 0x97ff, MRA_BANK1 },
865 { 0x9800, 0xbfff, MRA_RAM },
866 { 0xc804, 0xc807, pia_0_r },
867 { 0xc80c, 0xc80f, pia_1_r },
868 { 0xcb00, 0xcb00, williams_video_counter_r },
869 { 0xcc00, 0xcfff, MRA_RAM },
870 { 0xd000, 0xffff, MRA_ROM },
874 static MEMORY_WRITE_START( williams_writemem )
875 { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
876 { 0x9800, 0xbfff, MWA_RAM },
877 { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
878 { 0xc804, 0xc807, pia_0_w },
879 { 0xc80c, 0xc80f, pia_1_w },
880 { 0xc900, 0xc900, williams_vram_select_w },
881 { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
882 { 0xcbff, 0xcbff, watchdog_reset_w },
883 { 0xcc00, 0xcfff, MWA_RAM },
884 { 0xd000, 0xffff, MWA_ROM },
887 static MEMORY_READ_START( sound_readmem )
888 { 0x0000, 0x007f, MRA_RAM },
889 { 0x0400, 0x0403, pia_2_r },
890 { 0x8400, 0x8403, pia_2_r }, // used by Colony 7, perhaps others?
891 { 0xb000, 0xffff, MRA_ROM }, // most games start at $F000, Sinistar starts at $B000
895 static MEMORY_WRITE_START( sound_writemem )
896 { 0x0000, 0x007f, MWA_RAM },
897 { 0x0400, 0x0403, pia_2_w },
898 { 0x8400, 0x8403, pia_2_w }, // used by Colony 7, perhaps others?
899 { 0xb000, 0xffff, MWA_ROM }, // most games start at $F000, Sinistar starts at $B000
902 MACHINE_INIT( williams )
907 // reset the ticket dispenser (Lotto Fun)
908 ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
910 // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
911 timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
913 // also set a timer to go off on scanline 240
914 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
918 static void williams_va11_callback(int scanline)
920 // the IRQ signal comes into CB1, and is set to VA11
921 pia_1_cb1_w(0, scanline & 0x20);
923 // update the screen while we're here
924 force_partial_update(scanline - 1);
926 // set a timer for the next update
928 if (scanline >= 256) scanline = 0;
929 timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
933 static void williams_count240_off_callback(int param)
935 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
940 static void williams_count240_callback(int param)
942 // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
945 // set a timer to turn it off once the scanline counter resets
946 timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
948 // set a timer for next frame
949 timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
953 static void williams_main_irq(int state)
955 // IRQ to the main CPU
956 cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
960 static void williams_main_firq(int state)
962 // FIRQ to the main CPU
963 cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
967 static void williams_snd_irq(int state)
969 // IRQ to the sound CPU
970 cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
974 READ_HANDLER( williams_video_counter_r )
976 return cpu_getscanline() & 0xFC;
980 // Special PIA 0 for Stargate, to handle the controls
981 struct pia6821_interface stargate_pia_0_intf =
983 //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
984 //outputs: A/B,CA/B2 / 0, 0, 0, 0,
988 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
989 struct pia6821_interface williams_pia_1_intf =
991 //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
992 //outputs: A/B,CA/B2 / 0, williams_snd_cmd_w, 0, 0,
993 //irqs : A/B / williams_main_irq, williams_main_irq
996 // Generic PIA 2, maps to DAC data in and sound IRQs
997 struct pia6821_interface williams_snd_pia_intf =
999 //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
1000 //outputs: A/B,CA/B2 / DAC_0_data_w, 0, 0, 0,
1001 //irqs : A/B / williams_snd_irq, williams_snd_irq
1004 static DRIVER_INIT( stargate )
1006 // CMOS configuration
1007 CONFIGURE_CMOS(0xCC00, 0x400);
1009 // PIA configuration
1010 CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
1014 int cpu_getscanline(void)
1016 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
1019 *************************************
1021 * Returns time until given scanline
1023 *************************************
1025 double cpu_getscanlinetime(int scanline)
1027 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
1028 double abstime = timer_get_time();
1031 // if we're already past the computed time, count it for the next frame
1032 if (abstime >= scantime)
1033 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
1035 // compute how long from now until that time
1036 result = scantime - abstime;
1038 // if it's small, just count a whole frame
1039 if (result < TIME_IN_NSEC(1))
1040 result = TIME_IN_HZ(Machine->drv->frames_per_second);
1044 *************************************
1046 * Returns time for one scanline
1048 *************************************
1050 double cpu_getscanlineperiod(void)
1052 return scanline_period;
1056 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
1057 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
1058 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
1060 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
1061 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
1062 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
1064 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
1065 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
1066 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
1067 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
1069 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
1070 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
1071 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
1072 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
1074 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
1075 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
1076 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
1077 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
1079 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
1080 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
1081 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
1082 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
1084 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
1085 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
1086 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
1087 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
1089 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
1090 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
1091 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
1093 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
1094 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
1095 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
1096 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
1103 bit 2 |-6 bits to sound board
1108 bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
1111 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
1116 00 -> $C80D = PIA2 -> DDR active
1117 00 -> $C80C = PIA2 DDR -> All input?
1125 #define PIA_IRQ1 (0x80)
1126 #define PIA_IRQ2 (0x40)
1128 #define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
1129 #define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
1130 #define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
1131 #define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
1132 #define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
1133 #define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
1134 #define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
1135 #define C2_SET(c) ( (((c) >> 3) & 0x01))
1136 #define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
1137 #define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
1138 #define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
1139 #define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
1140 #define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
1141 #define C2_INPUT(c) (!(((c) >> 5) & 0x01))
1143 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
1145 pia6821_state *p = get_token(device);
1147 /* limit the data to 0 or 1 */
1148 data = data ? TRUE : FALSE;
1150 LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
1152 /* the new state has caused a transition */
1153 if ((p->in_ca1 != data) &&
1154 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
1156 LOG(("PIA #%s: CA1 triggering\n", device->tag));
1161 /* update externals */
1162 update_interrupts(device);
1164 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
1165 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
1166 set_out_ca2(device, TRUE);
1169 /* set the new value for CA1 */
1171 p->in_ca1_pushed = TRUE;
1174 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
1176 pia6821_state *p = get_token(device);
1178 /* limit the data to 0 or 1 */
1179 data = data ? 1 : 0;
1181 LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
1183 /* the new state has caused a transition */
1184 if ((p->in_cb1 != data) &&
1185 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
1187 LOG(("PIA #%s: CB1 triggering\n", device->tag));
1192 /* update externals */
1193 update_interrupts(device);
1195 /* If CB2 is configured as a write-strobe output which is reset by a CB1
1196 transition, this reset will only happen when a read from port B implicitly
1197 clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
1198 is different from what happens with port A. */
1201 /* set the new value for CB1 */
1203 p->in_cb1_pushed = TRUE;
1206 static void update_interrupts(const device_config *device)
1208 pia6821_state *p = get_token(device);
1211 /* start with IRQ A */
1212 new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1214 if (new_state != p->irq_a_state)
1216 p->irq_a_state = new_state;
1217 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1221 new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1223 if (new_state != p->irq_b_state)
1225 p->irq_b_state = new_state;
1226 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1230 static void control_b_w(const device_config *device, UINT8 data)
1232 pia6821_state *p = get_token(device);
1235 /* bit 7 and 6 are read only */
1238 LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1240 /* update the control register */
1243 if (C2_SET_MODE(p->ctl_b))
1244 /* set/reset mode - bit value determines the new output */
1245 temp = C2_SET(p->ctl_b);
1247 /* strobe mode - output is always high unless strobed */
1250 set_out_cb2(device, temp);
1252 /* update externals */
1253 update_interrupts(device);
1256 static void control_a_w(const device_config *device, UINT8 data)
1258 pia6821_state *p = get_token(device);
1260 /* bit 7 and 6 are read only */
1263 LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1265 /* update the control register */
1268 /* CA2 is configured as output */
1269 if (C2_OUTPUT(p->ctl_a))
1273 if (C2_SET_MODE(p->ctl_a))
1274 /* set/reset mode - bit value determines the new output */
1275 temp = C2_SET(p->ctl_a);
1277 /* strobe mode - output is always high unless strobed */
1280 set_out_ca2(device, temp);
1283 /* update externals */
1284 update_interrupts(device);
1290 B7 B6 B5 B4 B3 B2 B1 B0
1291 --------------------------------------------------
1292 IRQA(B)1 IRQA(B)2 CA(B)2 Ctrl DDR CA(B)1 Ctrl
1294 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1295 DDR: 0: DDR selected, 1: Output register selected
1296 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1297 B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1298 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1300 Entering main loop...
1301 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1302 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1303 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1304 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1305 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1306 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1307 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1308 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1309 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1310 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1311 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1312 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1313 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1314 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1315 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1316 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1317 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1318 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1319 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1320 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1321 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1322 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1323 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B] --> Clear IRQAs
1324 6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9] --> Clear IRQAs
1326 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1327 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1328 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6] --> Clear IRQBs
1329 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1330 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1331 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1332 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1333 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1334 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1335 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1336 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1337 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1338 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1339 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1340 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1341 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1342 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1343 6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]