]> Shamusworld >> Repos - stargem2/blob - src/stargem2.cpp
More cleanup, chasing down problems with the demo mode
[stargem2] / src / stargem2.cpp
1 //
2 // Stargate Emulator (StarGem2) v2.0 SDL
3 //
4 // by James L. Hammons
5 // (C) 2006 Underground Software
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
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)
14 //
15
16 #include "SDL.h"
17 #include <fstream>
18 #include <string>
19 #include <iomanip>
20 #include <iostream>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <time.h>
24 #include "types.h"
25 #include "log.h"
26 #include "v6808.h"
27 #include "v6809.h"
28 #include "video.h"
29 #include "sound.h"
30 #include "timing.h"
31 #include "settings.h"
32 #include "dis6809.h"
33 #include "dis6808.h"
34
35 //#define __DEBUG__
36 //#define LOG_PIA1_IO
37
38 using namespace std;
39
40 #define SOUNDROM        "ROMs/sg.snd"
41 #define CMOS            "cmos.ram"
42 #define SAVESTATE       "sg2.state"
43
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)
51
52 // Global variables
53
54 uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
55 V6809REGS mainCPU;
56 V6808REGS soundCPU;
57 uint8 color[16];
58 uint32 palette[256];
59 bool paletteDirty = false;
60
61 // Local variables
62
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
67
68 // Function prototypes
69
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);
75 void SaveCMOS(void);
76 bool LoadMachineState(void);
77 void SaveMachineState(void);
78
79 // Local timer callback functions
80
81 static void FrameCallback(void);
82 static void ScanlineCallback(void);
83
84
85 //
86 // Main loop
87 //
88 int main(int /*argc*/, char * /*argv*/[])
89 {
90         InitLog("stargem2.log");
91         WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
92         WriteLog("(C) 2009 Underground Software\n\n");
93
94         LoadSettings();
95
96         // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
97         for(uint32 i=0; i<256; i++)
98                 palette[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;
103 #else
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;
107 #endif
108
109         // Zero out memory
110         memset(gram, 0, 0x10000);
111         memset(grom, 0, 0x10000);
112         memset(sram, 0, 0x10000);
113         memset(srom, 0, 0x10000);
114
115         // Set up V6809 & V6808 execution contexts
116
117         memset(&mainCPU, 0, sizeof(V6809REGS));
118         mainCPU.RdMem = RdMem6809;
119         mainCPU.WrMem = WrMem6809;
120         mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
121
122         memset(&soundCPU, 0, sizeof(V6808REGS));
123         soundCPU.RdMem = RdMem6808;
124         soundCPU.WrMem = WrMem6808;
125         soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
126
127         char ROMs[12][8] = {
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"
130                 };
131
132         for(int i=0; i<12; i++)
133         {
134                 uint32 baseAddress = i * 0x1000;
135
136                 if (i > 8)
137                         baseAddress += 0x4000;
138
139                 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
140                 {
141                         WriteLog("Could not open file '%s'!\n", ROMs[i]);
142                         return -1;
143                 }
144         }
145
146         if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
147         {
148                 WriteLog("Could not open file '%s'!\n", SOUNDROM);
149                 return -1;
150         }
151
152         WriteLog("Stargate ROM images loaded...\n");
153         WriteLog("About to initialize video...\n");
154
155         if (!InitVideo())
156         {
157                 cout << "Aborting!" << endl;
158                 return -1;
159         }
160
161         // Have to do this *after* video init but *before* sound init...!
162         WriteLog("About to load machine state...");
163
164         if (!LoadMachineState())
165                 WriteLog("Machine state file not found!\n");
166         else
167                 WriteLog("done!\n");
168
169         if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
170                 WriteLog("CMOS RAM not found!\n");
171
172         WriteLog("About to intialize audio...\n");
173         SoundInit();
174         keys = SDL_GetKeyState(NULL);
175         srom[0xF800] = 0x37;                                            // Fix checksum so ST works...
176         running = true;                                                         // Set running status...
177
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();
183
184         WriteLog("Entering main loop...\n");
185
186         while (running)
187         {
188                 double timeToNextEvent = GetTimeToNextEvent();
189                 Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
190                 HandleNextEvent();
191         }
192
193 #ifdef __DEBUG__
194 WriteLog("\n");
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);
197 WriteLog("\n");
198
199 /*uint16 pc = mainCPU.pc;//0x15BA;
200 for(int i=0; i<200; i++)
201 //while (pc < 0x9000)
202 {
203         pc += Decode6809(pc);
204         WriteLog("\n");
205 }//*/
206
207 /*uint32 pc = 0;
208 while (pc < 0xFFFF)
209 {
210         pc += Decode6809(pc);
211         WriteLog("\n");
212 }//*/
213 #endif
214
215         SoundDone();
216         VideoDone();
217         SaveCMOS();
218         SaveMachineState();
219         LogDone();
220
221         return 0;
222 }
223
224 //
225 // Load a file into RAM/ROM image space
226 //
227 bool LoadImg(const char * filename, uint8 * ram, int size)
228 {
229         FILE * fp = fopen(filename, "rb");
230
231         if (fp == NULL)
232                 return false;
233
234         size_t ignoredResult = fread(ram, 1, size, fp);
235         fclose(fp);
236
237         return true;
238 }
239
240 //
241 // Save CMOS ram
242 //
243 void SaveCMOS(void)
244 {
245         FILE * fp = fopen(CMOS, "wb");
246
247         if (fp != NULL)
248         {
249                 size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
250                 fclose(fp);
251         }
252         else
253                 WriteLog("CMOS RAM not saved!\n");
254 }
255
256 //
257 // Load state save file
258 //
259 bool LoadMachineState(void)
260 {
261         FILE * fp = fopen(SAVESTATE, "rb");
262
263         if (fp == NULL)
264                 return false;
265
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);
271         fclose(fp);
272
273         // Set up backbuffer... ;-)
274         for(uint16 i=0x0006; i<0x97F8; i++)                                     // Screen memory
275                 WrMem6809(i, gram[i]);
276
277         for(uint16 i=0xC000; i<=0xC00F; i++)                            // Palette memory
278                 WrMem6809(i, gram[i]);
279
280         paletteDirty = true;
281
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...
287         soundCPU.clock = 0;
288         mainCPU.clockOverrun = 0;                                                       // And overrun values...
289 //notyet        soundCPU.clockOverrun = 0;
290
291         return true;
292 }
293
294 //
295 // Save state save file
296 //
297 void SaveMachineState(void)
298 {
299         FILE * fp = fopen(SAVESTATE, "wb");
300
301         if (fp != NULL)
302         {
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);
307                 fclose(fp);
308         }
309         else
310                 WriteLog("Machine state not saved!\n");
311 }
312
313 //
314 // 6809 memory functions
315 //
316
317 #ifdef LOG_PIA1_IO
318 char piaRegsName[4][10] = { "PORTA", "PACTL", "PORTB", "PBCTL" };
319 #endif
320 uint8 RdMem6809(uint16 addr)
321 {
322         uint8 b;
323
324         if (addr >= 0x9000 && addr <= 0xCFFF)           // No ROM between $9000 - $CFFF...
325                 b = gram[addr];
326         else
327         {
328                 if (!gram[0xC900] && addr <= 0x8FFF)    // Check RAM $C900 bank switch
329                         b = gram[addr];
330                 else
331                         b = grom[addr];
332         }
333
334         // A wee kludge (though I doubt it reads from anywhere other than $CB00)...
335         if ((addr & 0xFF00) == 0xCB00)
336 #if 0
337                 b = gram[0xCB00] & 0xFC;                                // Only bits 2-7 are connected...
338 #else
339         {
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...
347         }
348 #endif
349
350         // More kludge...
351         if ((addr == 0xC80C) && (gram[0xC80D] & 0x04))  // Read PORTA and DDR is set to Output
352         {
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
357         }
358
359         if ((addr == 0xC80E) && (gram[0xC80F] & 0x04))  // Read PORTB and DDR is set to Output
360         {
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
364         }
365
366 //temp...
367 /*extern uint16 pcr;
368 //if (addr >= 0xC000 && addr <= 0xCBFF)
369 if (addr == 0x9C59)
370         WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
371 #ifdef LOG_PIA1_IO
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());//*/
374 #endif
375         return b;
376 }
377
378 void WrMem6809(uint16 addr, uint8 b)
379 {
380 //temp...
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]);//*/
388
389         gram[addr] = b;
390
391         if (addr >= 0x0006 && addr < 0x97F7)                    // 304 pixels  152-128=24-16=8
392         {
393                 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
394                 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
395
396                 if (sy > 5 && sy < 246)
397                 {
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]];
401                 }
402         }
403         else if (addr >= 0xC000 && addr <= 0xC00F)
404         {
405 // This approach doesn't take the BG color to the edges of the screen
406                 color[addr - 0xC000] = b;
407                 paletteDirty = true;
408         }
409         else if (addr == 0xC80E)
410         {
411                 sram[0x0402] = b;                                                               // Connect PIAs in 6809 & 6808
412                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ;             // Start sound IRQ
413         }
414
415 #ifdef LOG_PIA1_IO
416 //if (addr >= 0xC80C && addr <= 0xC80F)
417 if (addr == 0xC80D)
418         WriteLog("V6809 WrMem: Writing PIA (%s) address %04X [->%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
419 #endif
420 }
421
422 //
423 // 6808 memory functions
424 //
425
426 uint8 RdMem6808(uint16 addr)
427 {
428         return (addr < 0xF000 ? sram[addr] : srom[addr]);
429 }
430
431 void WrMem6808(uint16 addr, uint8 b)
432 {
433         sram[addr] = b;
434
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;
440 }
441
442 static void FrameCallback(void)
443 {
444         SDL_PumpEvents();                                                       // Force key events into the buffer.
445         gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
446
447         if (keys[SDLK_ESCAPE])
448                 running = false;                                                // ESC to exit...
449
450 //Convert this stuff to use the settings module... !!! FIX !!!
451         if (keys[SDLK_SEMICOLON])
452                 gram[0xC804] |= 0x01;                                   // Fire (;)
453         if (keys[SDLK_l])
454                 gram[0xC804] |= 0x02;                                   // Thrust (L)
455         if (keys[SDLK_SPACE])
456                 gram[0xC804] |= 0x04;                                   // Smart Bomb (space)
457         if (keys[SDLK_BACKSPACE])
458                 gram[0xC804] |= 0x08;                                   // Hyperspace (BkSp)
459         if (keys[SDLK_2])
460                 gram[0xC804] |= 0x10;                                   // Two Player Start (2)
461         if (keys[SDLK_1])
462                 gram[0xC804] |= 0x20;                                   // One Player Start (1)
463         if (keys[SDLK_RETURN])
464                 gram[0xC804] |= 0x40;                                   // Reverse (Enter)
465         if (keys[SDLK_f])
466                 gram[0xC804] |= 0x80;                                   // Down (F)
467
468         if (keys[SDLK_r])
469                 gram[0xC806] |= 0x01;                                   // Up (R)
470         if (keys[SDLK_a])
471                 gram[0xC806] |= 0x02;                                   // Inviso (A)
472
473         if (keys[SDLK_F1])
474                 gram[0xC80C] |= 0x01;                                   // Auto up (F1)
475         if (keys[SDLK_F2])
476                 gram[0xC80C] |= 0x02;                                   // Advance (F2)
477         if (keys[SDLK_5])
478                 gram[0xC80C] |= 0x04;                                   // Right Coin (5)
479         if (keys[SDLK_F3])
480                 gram[0xC80C] |= 0x08;                                   // High Score Reset (F3)
481         if (keys[SDLK_3])
482                 gram[0xC80C] |= 0x10;                                   // Left Coin (3)
483         if (keys[SDLK_4])
484                 gram[0xC80C] |= 0x20;                                   // Center Coin (4)
485         if (keys[SDLK_F4])
486                 gram[0xC80C] |= 0x40;                                   // Slam Switch (F4)
487
488         if (keys[SDLK_F5])                                                      // Sound CPU self-test (F5)
489                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
490         if (keys[SDLK_F6])                                                      // Reset the 6808 (F6)
491                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
492
493         if (paletteDirty)
494         {
495                 for(uint32 addr=0x0006; addr<0x97F7; addr++)
496                 {
497                         uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
498
499                         if (sy > 5 && sy < 246)
500                         {
501                                 uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
502                                 uint8 sb = gram[addr];
503
504                                 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
505                                 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
506                         }
507                 }
508
509                 paletteDirty = false;
510         }
511
512         RenderScreenBuffer();                                           // 1 frame = 1/60 sec ~ 16667 cycles
513         clockFrameStart = mainCPU.clock;
514
515         // Wait for next frame...
516         while (SDL_GetTicks() - startTicks < 16)
517                 SDL_Delay(1);
518
519         startTicks = SDL_GetTicks();
520         SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
521 }
522
523 static void ScanlineCallback(void)
524 {
525 #if 0
526         if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
527                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
528 #else
529         mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
530
531         if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
532                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
533 #endif
534
535 /*
536 The problem is that this is already asserted above, by virtue of the fact that
537 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
538 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
539 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
540
541 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
542 */
543 //      if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09))     // Do COUNT240 IRQ (if enabled!)
544 //              mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
545
546         // This should set everything between $CB00-CBFF...
547 //      gram[0xCB00] += 8;                                                      // Update video counter...
548 //      gram[0xCB00] += 4;                                                      // Update video counter...
549
550         SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
551 }
552
553
554 /*
555 ; With correct timing, but no color cycling
556
557 --> Start of frame...
558 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
559 At $07AD. $6E: 00
560 At $0B66. $6E: 00
561 At $0CF4. $6E: 00
562 At $0CCB. $6E: 00
563 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
564 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
565 At $0718. $6E: 01
566 At $07AD. $6E: 01
567 At $0BB8. $6E: 01
568 At $0927. $6E: 01
569 At $0CF4. $6E: 01
570 At $0B66. $6E: 01
571 At $16C8. $6E: 01
572 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
573 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
574 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
575
576
577 ; With incorrect timing, but has color cycling
578
579 --> Start of frame...
580 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
581 At $1609. $6E: 00                       ; Color cycling...
582 At $07AD. $6E: 00
583 At $0B66. $6E: 00
584 At $0CF4. $6E: 00
585 At $0CCB. $6E: 00
586 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
587 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
588 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
589 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
590 At $0718. $6E: 01
591 At $07AD. $6E: 01
592 At $0BB8. $6E: 01
593 At $0927. $6E: 01
594 At $0CF4. $6E: 01
595 At $0B66. $6E: 01
596 At $16C8. $6E: 01
597 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
598 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
599 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
600
601
602
603         Stargate
604         --------
605
606         0000-8FFF ROM   (for Blaster, 0000-3FFF is a bank of 12 ROMs)
607         0000-97FF Video  RAM Bank switched with ROM (96FF for Blaster)
608         9800-BFFF RAM
609                 0xBB00 Blaster only, Color 0 for each line (256 entry)
610                 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
611                     Do something else with the bit 1, I do not know what
612         C000-CFFF I/O
613         D000-FFFF ROM
614
615         C000-C00F color_registers  (16 bytes of BBGGGRRR)
616
617         C804 widget_pia_dataa (widget = I/O board)
618         C805 widget_pia_ctrla
619         C806 widget_pia_datab
620         C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
621                                controls if Table or Joust)
622               bits 5-3 = 110 = player 2
623               bits 5-3 = 111 = player 1
624
625         C80C rom_pia_dataa
626         C80D rom_pia_ctrla
627         C80E rom_pia_datab
628               bit 0 \
629               bit 1 |
630               bit 2 |-6 bits to sound board
631               bit 3 |
632               bit 4 |
633               bit 5 /
634               bit 6 \
635               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
636         C80F rom_pia_ctrlb
637
638         C900 rom_enable_scr_ctrl  Switch between video ram and rom at 0000-97FF
639
640         Stargate
641         --------
642         C804 widget_pia_dataa (widget = I/O board)
643           bit 0  Fire
644           bit 1  Thrust
645           bit 2  Smart Bomb
646           bit 3  HyperSpace
647           bit 4  2 Players
648           bit 5  1 Player
649           bit 6  Reverse
650           bit 7  Down
651
652         C806 widget_pia_datab
653           bit 0  Up
654           bit 1  Inviso
655           bit 2
656           bit 3
657           bit 4
658           bit 5
659           bit 6
660           bit 7  0 = Upright  1 = Table
661
662         C80C rom_pia_dataa
663           bit 0  Auto Up
664           bit 1  Advance
665           bit 2  Right Coin        (High Score Reset in schematics)
666           bit 3  High Score Reset  (Left Coin in schematics)
667           bit 4  Left Coin         (Center Coin in schematics)
668           bit 5  Center Coin       (Right Coin in schematics)
669           bit 6  Slam Door Tilt
670           bit 7  Hand Shake from sound board
671 */
672
673
674 /*
675
676 static MEMORY_READ_START( williams_readmem )
677         { 0x0000, 0x97ff, MRA_BANK1 },
678         { 0x9800, 0xbfff, MRA_RAM },
679         { 0xc804, 0xc807, pia_0_r },
680         { 0xc80c, 0xc80f, pia_1_r },
681         { 0xcb00, 0xcb00, williams_video_counter_r },
682         { 0xcc00, 0xcfff, MRA_RAM },
683         { 0xd000, 0xffff, MRA_ROM },
684 MEMORY_END
685
686
687 static MEMORY_WRITE_START( williams_writemem )
688         { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
689         { 0x9800, 0xbfff, MWA_RAM },
690         { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
691         { 0xc804, 0xc807, pia_0_w },
692         { 0xc80c, 0xc80f, pia_1_w },
693         { 0xc900, 0xc900, williams_vram_select_w },
694         { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
695         { 0xcbff, 0xcbff, watchdog_reset_w },
696         { 0xcc00, 0xcfff, MWA_RAM },
697         { 0xd000, 0xffff, MWA_ROM },
698 MEMORY_END
699
700 static MEMORY_READ_START( sound_readmem )
701         { 0x0000, 0x007f, MRA_RAM },
702         { 0x0400, 0x0403, pia_2_r },
703         { 0x8400, 0x8403, pia_2_r },    // used by Colony 7, perhaps others?
704         { 0xb000, 0xffff, MRA_ROM },    // most games start at $F000, Sinistar starts at $B000
705 MEMORY_END
706
707
708 static MEMORY_WRITE_START( sound_writemem )
709         { 0x0000, 0x007f, MWA_RAM },
710         { 0x0400, 0x0403, pia_2_w },
711         { 0x8400, 0x8403, pia_2_w },    // used by Colony 7, perhaps others?
712         { 0xb000, 0xffff, MWA_ROM },    // most games start at $F000, Sinistar starts at $B000
713 MEMORY_END
714
715 MACHINE_INIT( williams )
716 {
717         // reset the PIAs
718         pia_reset();
719
720         // reset the ticket dispenser (Lotto Fun)
721         ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
722
723         // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
724         timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
725
726         // also set a timer to go off on scanline 240
727         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
728 }
729
730
731 static void williams_va11_callback(int scanline)
732 {
733         // the IRQ signal comes into CB1, and is set to VA11
734         pia_1_cb1_w(0, scanline & 0x20);
735
736         // update the screen while we're here
737         force_partial_update(scanline - 1);
738
739         // set a timer for the next update
740         scanline += 8;
741         if (scanline >= 256) scanline = 0;
742         timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
743 }
744
745
746 static void williams_count240_off_callback(int param)
747 {
748         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
749         pia_1_ca1_w(0, 0);
750 }
751
752
753 static void williams_count240_callback(int param)
754 {
755         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
756         pia_1_ca1_w(0, 1);
757
758         // set a timer to turn it off once the scanline counter resets
759         timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
760
761         // set a timer for next frame
762         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
763 }
764
765
766 static void williams_main_irq(int state)
767 {
768         // IRQ to the main CPU
769         cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
770 }
771
772
773 static void williams_main_firq(int state)
774 {
775         // FIRQ to the main CPU
776         cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
777 }
778
779
780 static void williams_snd_irq(int state)
781 {
782         // IRQ to the sound CPU
783         cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
784 }
785
786
787 READ_HANDLER( williams_video_counter_r )
788 {
789         return cpu_getscanline() & 0xFC;
790 }
791
792
793 // Special PIA 0 for Stargate, to handle the controls
794 struct pia6821_interface stargate_pia_0_intf =
795 {
796         //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
797         //outputs: A/B,CA/B2       / 0, 0, 0, 0,
798         //irqs   : A/B             / 0, 0
799 };
800
801 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
802 struct pia6821_interface williams_pia_1_intf =
803 {
804         //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
805         //outputs: A/B,CA/B2       / 0, williams_snd_cmd_w, 0, 0,
806         //irqs   : A/B             / williams_main_irq, williams_main_irq
807 };
808
809 // Generic PIA 2, maps to DAC data in and sound IRQs
810 struct pia6821_interface williams_snd_pia_intf =
811 {
812         //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
813         //outputs: A/B,CA/B2       / DAC_0_data_w, 0, 0, 0,
814         //irqs   : A/B             / williams_snd_irq, williams_snd_irq
815 };
816
817 static DRIVER_INIT( stargate )
818 {
819         // CMOS configuration
820         CONFIGURE_CMOS(0xCC00, 0x400);
821
822         // PIA configuration
823         CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
824 }
825
826
827 int cpu_getscanline(void)
828 {
829         return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
830 }
831
832  *************************************
833  *
834  *      Returns time until given scanline
835  *
836  *************************************
837
838 double cpu_getscanlinetime(int scanline)
839 {
840         double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
841         double abstime = timer_get_time();
842         double result;
843
844         // if we're already past the computed time, count it for the next frame
845         if (abstime >= scantime)
846                 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
847
848         // compute how long from now until that time
849         result = scantime - abstime;
850
851         // if it's small, just count a whole frame
852         if (result < TIME_IN_NSEC(1))
853                 result = TIME_IN_HZ(Machine->drv->frames_per_second);
854         return result;
855 }
856
857  *************************************
858  *
859  *      Returns time for one scanline
860  *
861  *************************************
862
863 double cpu_getscanlineperiod(void)
864 {
865         return scanline_period;
866 }
867
868
869 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
870 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
871 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
872
873 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
874 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
875 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
876
877 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
878 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
879 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
880 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
881
882 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
883 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
884 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
885 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
886
887 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
888 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
889 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
890 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
891
892 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
893 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
894 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
895 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
896
897 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
898 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
899 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
900 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
901
902 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
903 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
904 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
905
906 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
907 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
908 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
909 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
910
911         C80C rom_pia_dataa
912         C80D rom_pia_ctrla
913         C80E rom_pia_datab
914               bit 0 \
915               bit 1 |
916               bit 2 |-6 bits to sound board
917               bit 3 |
918               bit 4 |
919               bit 5 /
920               bit 6 \
921               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
922         C80F rom_pia_ctrlb
923
924 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
925
926
927 PIA initialization:
928
929 00 -> $C80D = PIA2     -> DDR active
930 00 -> $C80C = PIA2 DDR -> All input?
931
932
933
934 */
935
936 #if 0
937
938 #define PIA_IRQ1                                (0x80)
939 #define PIA_IRQ2                                (0x40)
940
941 #define IRQ1_ENABLED(c)                 ( (((c) >> 0) & 0x01))
942 #define C1_LOW_TO_HIGH(c)               ( (((c) >> 1) & 0x01))
943 #define C1_HIGH_TO_LOW(c)               (!(((c) >> 1) & 0x01))
944 #define OUTPUT_SELECTED(c)              ( (((c) >> 2) & 0x01))
945 #define IRQ2_ENABLED(c)                 ( (((c) >> 3) & 0x01))
946 #define STROBE_E_RESET(c)               ( (((c) >> 3) & 0x01))
947 #define STROBE_C1_RESET(c)              (!(((c) >> 3) & 0x01))
948 #define C2_SET(c)                               ( (((c) >> 3) & 0x01))
949 #define C2_LOW_TO_HIGH(c)               ( (((c) >> 4) & 0x01))
950 #define C2_HIGH_TO_LOW(c)               (!(((c) >> 4) & 0x01))
951 #define C2_SET_MODE(c)                  ( (((c) >> 4) & 0x01))
952 #define C2_STROBE_MODE(c)               (!(((c) >> 4) & 0x01))
953 #define C2_OUTPUT(c)                    ( (((c) >> 5) & 0x01))
954 #define C2_INPUT(c)                             (!(((c) >> 5) & 0x01))
955
956 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
957 {
958         pia6821_state *p = get_token(device);
959
960         /* limit the data to 0 or 1 */
961         data = data ? TRUE : FALSE;
962
963         LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
964
965         /* the new state has caused a transition */
966         if ((p->in_ca1 != data) &&
967                 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
968         {
969                 LOG(("PIA #%s: CA1 triggering\n", device->tag));
970
971                 /* mark the IRQ */
972                 p->irq_a1 = TRUE;
973
974                 /* update externals */
975                 update_interrupts(device);
976
977                 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
978                 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
979                         set_out_ca2(device, TRUE);
980         }
981
982         /* set the new value for CA1 */
983         p->in_ca1 = data;
984         p->in_ca1_pushed = TRUE;
985 }
986
987 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
988 {
989         pia6821_state *p = get_token(device);
990
991         /* limit the data to 0 or 1 */
992         data = data ? 1 : 0;
993
994         LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
995
996         /* the new state has caused a transition */
997         if ((p->in_cb1 != data) &&
998                 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
999         {
1000                 LOG(("PIA #%s: CB1 triggering\n", device->tag));
1001
1002                 /* mark the IRQ */
1003                 p->irq_b1 = 1;
1004
1005                 /* update externals */
1006                 update_interrupts(device);
1007
1008                 /* If CB2 is configured as a write-strobe output which is reset by a CB1
1009            transition, this reset will only happen when a read from port B implicitly
1010            clears the IRQ B1 flag.  So we handle the CB2 reset there.  Note that this
1011            is different from what happens with port A. */
1012         }
1013
1014         /* set the new value for CB1 */
1015         p->in_cb1 = data;
1016         p->in_cb1_pushed = TRUE;
1017 }
1018
1019 static void update_interrupts(const device_config *device)
1020 {
1021         pia6821_state *p = get_token(device);
1022         int new_state;
1023
1024         /* start with IRQ A */
1025         new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1026
1027         if (new_state != p->irq_a_state)
1028         {
1029                 p->irq_a_state = new_state;
1030                 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1031         }
1032
1033         /* then do IRQ B */
1034         new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1035
1036         if (new_state != p->irq_b_state)
1037         {
1038                 p->irq_b_state = new_state;
1039                 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1040         }
1041 }
1042
1043 static void control_b_w(const device_config *device, UINT8 data)
1044 {
1045         pia6821_state *p = get_token(device);
1046         int temp;
1047
1048         /* bit 7 and 6 are read only */
1049         data &= 0x3f;
1050
1051         LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1052
1053         /* update the control register */
1054         p->ctl_b = data;
1055
1056         if (C2_SET_MODE(p->ctl_b))
1057                 /* set/reset mode - bit value determines the new output */
1058                 temp = C2_SET(p->ctl_b);
1059         else
1060                 /* strobe mode - output is always high unless strobed */
1061                 temp = TRUE;
1062
1063         set_out_cb2(device, temp);
1064
1065         /* update externals */
1066         update_interrupts(device);
1067 }
1068
1069 static void control_a_w(const device_config *device, UINT8 data)
1070 {
1071         pia6821_state *p = get_token(device);
1072
1073         /* bit 7 and 6 are read only */
1074         data &= 0x3f;
1075
1076         LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1077
1078         /* update the control register */
1079         p->ctl_a = data;
1080
1081         /* CA2 is configured as output */
1082         if (C2_OUTPUT(p->ctl_a))
1083         {
1084                 int temp;
1085
1086                 if (C2_SET_MODE(p->ctl_a))
1087                         /* set/reset mode - bit value determines the new output */
1088                         temp = C2_SET(p->ctl_a);
1089                 else
1090                         /* strobe mode - output is always high unless strobed */
1091                         temp = TRUE;
1092
1093                 set_out_ca2(device, temp);
1094         }
1095
1096         /* update externals */
1097         update_interrupts(device);
1098 }
1099
1100
1101 CTRL REGISTER:
1102
1103 B7        B6        B5 B4 B3     B2   B1 B0
1104 --------------------------------------------------
1105 IRQA(B)1  IRQA(B)2  CA(B)2 Ctrl  DDR  CA(B)1 Ctrl
1106
1107 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1108 DDR: 0: DDR selected, 1: Output register selected
1109 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1110                B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1111 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1112
1113 Entering main loop...
1114 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1115 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1116 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1117 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1118 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1119 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1120 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1121 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1122 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1123 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1124 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1125 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1126 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1127 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1128 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1129 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1130 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1131 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1132 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1133 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1134 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1135  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]  --> Clear IRQBs
1136  6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B]  --> Clear IRQAs
1137  6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9]  --> Clear IRQAs
1138
1139 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1140 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1141  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]  --> Clear IRQBs
1142 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1143 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1144  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
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
1158 #endif