]> Shamusworld >> Repos - stargem2/blob - src/stargem2.cpp
Major refactoring of V6809 code--still not 100% at this point
[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                 ClearLineOfCurrentV6809(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                 ClearLineOfCurrentV6809(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         if (keys[settings.keyBindings[S_KEY_FIRE]])                     gram[0xC804] |= 0x01;
451         if (keys[settings.keyBindings[S_KEY_THRUST]])           gram[0xC804] |= 0x02;
452         if (keys[settings.keyBindings[S_KEY_SMARTBOMB]])        gram[0xC804] |= 0x04;
453         if (keys[settings.keyBindings[S_KEY_HYPERSPACE]])       gram[0xC804] |= 0x08;
454         if (keys[settings.keyBindings[S_KEY_2P_START]])         gram[0xC804] |= 0x10;
455         if (keys[settings.keyBindings[S_KEY_1P_START]])         gram[0xC804] |= 0x20;
456         if (keys[settings.keyBindings[S_KEY_REVERSE]])          gram[0xC804] |= 0x40;
457         if (keys[settings.keyBindings[S_KEY_DOWN]])                     gram[0xC804] |= 0x80;
458
459         if (keys[settings.keyBindings[S_KEY_UP]])                       gram[0xC806] |= 0x01;
460         if (keys[settings.keyBindings[S_KEY_INVISO]])           gram[0xC806] |= 0x02;
461
462         if (keys[settings.keyBindings[S_KEY_AUTO_UP]])          gram[0xC80C] |= 0x01;
463         if (keys[settings.keyBindings[S_KEY_ADVANCE]])          gram[0xC80C] |= 0x02;
464         if (keys[settings.keyBindings[S_KEY_RIGHT_COIN]])       gram[0xC80C] |= 0x04;
465         if (keys[settings.keyBindings[S_KEY_HS_RESET]])         gram[0xC80C] |= 0x08;
466         if (keys[settings.keyBindings[S_KEY_LEFT_COIN]])        gram[0xC80C] |= 0x10;
467         if (keys[settings.keyBindings[S_KEY_CENTER_COIN]])      gram[0xC80C] |= 0x20;
468         if (keys[settings.keyBindings[S_KEY_SLAM_SWITCH]])      gram[0xC80C] |= 0x40;
469
470         if (keys[SDLK_F5])                                                      // Sound CPU self-test (F5)
471                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
472         if (keys[SDLK_F6])                                                      // Reset the 6808 (F6)
473                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
474 //Temp, for testing...
475 extern bool disasm;
476 //disasm = true;
477         if (keys[SDLK_F9])
478                 disasm = true;
479
480         if (paletteDirty)
481         {
482                 for(uint32 addr=0x0006; addr<0x97F7; addr++)
483                 {
484                         uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
485
486                         if (sy > 5 && sy < 246)
487                         {
488                                 uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
489                                 uint8 sb = gram[addr];
490
491                                 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
492                                 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
493                         }
494                 }
495
496                 paletteDirty = false;
497         }
498
499         RenderScreenBuffer();                                           // 1 frame = 1/60 sec ~ 16667 cycles
500         clockFrameStart = mainCPU.clock;
501
502         // Wait for next frame...
503         while (SDL_GetTicks() - startTicks < 16)
504                 SDL_Delay(1);
505
506         startTicks = SDL_GetTicks();
507         SetCallbackTime(FrameCallback, FRAME_DURATION_IN_CYCLES * M6809_CYCLE_IN_USEC);
508 }
509
510 static void ScanlineCallback(void)
511 {
512 #if 0
513         if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
514                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
515 #else
516         mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
517
518         if ((RdMem6809(0xCB00) & 0x20) && (gram[0xC80F] & 0x01))
519                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
520 #endif
521
522 /*
523 The problem is that this is already asserted above, by virtue of the fact that
524 240 = $F0 = bit 5 is set! So this does nothing! So obviously, the above IRQ assertion
525 is wrong--just need to figure out how the write of $20 and $00 affects the PBCTRL in the PIA.
526 It looks like Stargate never asserts the COUNT240 IRQ, and could be because of the above...
527
528 Apparently, COUNT240 is unimportant, at least as far as STARGATE is concerned...
529 */
530 //      if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09))     // Do COUNT240 IRQ (if enabled!)
531 //              mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
532
533         // This should set everything between $CB00-CBFF...
534 //      gram[0xCB00] += 8;                                                      // Update video counter...
535 //      gram[0xCB00] += 4;                                                      // Update video counter...
536
537         SetCallbackTime(ScanlineCallback, SG2_PIA_CALLBACK_DURATION);
538 }
539
540
541 /*
542 ; With correct timing, but no color cycling
543
544 --> Start of frame...
545 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
546 At $07AD. $6E: 00
547 At $0B66. $6E: 00
548 At $0CF4. $6E: 00
549 At $0CCB. $6E: 00
550 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
551 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
552 At $0718. $6E: 01
553 At $07AD. $6E: 01
554 At $0BB8. $6E: 01
555 At $0927. $6E: 01
556 At $0CF4. $6E: 01
557 At $0B66. $6E: 01
558 At $16C8. $6E: 01
559 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
560 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
561 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
562
563
564 ; With incorrect timing, but has color cycling
565
566 --> Start of frame...
567 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
568 At $1609. $6E: 00                       ; Color cycling...
569 At $07AD. $6E: 00
570 At $0B66. $6E: 00
571 At $0CF4. $6E: 00
572 At $0CCB. $6E: 00
573 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
574 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
575 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
576 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
577 At $0718. $6E: 01
578 At $07AD. $6E: 01
579 At $0BB8. $6E: 01
580 At $0927. $6E: 01
581 At $0CF4. $6E: 01
582 At $0B66. $6E: 01
583 At $16C8. $6E: 01
584 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
585 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
586 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
587
588
589
590         Stargate
591         --------
592
593         0000-8FFF ROM   (for Blaster, 0000-3FFF is a bank of 12 ROMs)
594         0000-97FF Video  RAM Bank switched with ROM (96FF for Blaster)
595         9800-BFFF RAM
596                 0xBB00 Blaster only, Color 0 for each line (256 entry)
597                 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
598                     Do something else with the bit 1, I do not know what
599         C000-CFFF I/O
600         D000-FFFF ROM
601
602         C000-C00F color_registers  (16 bytes of BBGGGRRR)
603
604         C804 widget_pia_dataa (widget = I/O board)
605         C805 widget_pia_ctrla
606         C806 widget_pia_datab
607         C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
608                                controls if Table or Joust)
609               bits 5-3 = 110 = player 2
610               bits 5-3 = 111 = player 1
611
612         C80C rom_pia_dataa
613         C80D rom_pia_ctrla
614         C80E rom_pia_datab
615               bit 0 \
616               bit 1 |
617               bit 2 |-6 bits to sound board
618               bit 3 |
619               bit 4 |
620               bit 5 /
621               bit 6 \
622               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
623         C80F rom_pia_ctrlb
624
625         C900 rom_enable_scr_ctrl  Switch between video ram and rom at 0000-97FF
626
627         Stargate
628         --------
629         C804 widget_pia_dataa (widget = I/O board)
630           bit 0  Fire
631           bit 1  Thrust
632           bit 2  Smart Bomb
633           bit 3  HyperSpace
634           bit 4  2 Players
635           bit 5  1 Player
636           bit 6  Reverse
637           bit 7  Down
638
639         C806 widget_pia_datab
640           bit 0  Up
641           bit 1  Inviso
642           bit 2
643           bit 3
644           bit 4
645           bit 5
646           bit 6
647           bit 7  0 = Upright  1 = Table
648
649         C80C rom_pia_dataa
650           bit 0  Auto Up
651           bit 1  Advance
652           bit 2  Right Coin        (High Score Reset in schematics)
653           bit 3  High Score Reset  (Left Coin in schematics)
654           bit 4  Left Coin         (Center Coin in schematics)
655           bit 5  Center Coin       (Right Coin in schematics)
656           bit 6  Slam Door Tilt
657           bit 7  Hand Shake from sound board
658 */
659
660
661 /*
662
663 static MEMORY_READ_START( williams_readmem )
664         { 0x0000, 0x97ff, MRA_BANK1 },
665         { 0x9800, 0xbfff, MRA_RAM },
666         { 0xc804, 0xc807, pia_0_r },
667         { 0xc80c, 0xc80f, pia_1_r },
668         { 0xcb00, 0xcb00, williams_video_counter_r },
669         { 0xcc00, 0xcfff, MRA_RAM },
670         { 0xd000, 0xffff, MRA_ROM },
671 MEMORY_END
672
673
674 static MEMORY_WRITE_START( williams_writemem )
675         { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
676         { 0x9800, 0xbfff, MWA_RAM },
677         { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
678         { 0xc804, 0xc807, pia_0_w },
679         { 0xc80c, 0xc80f, pia_1_w },
680         { 0xc900, 0xc900, williams_vram_select_w },
681         { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
682         { 0xcbff, 0xcbff, watchdog_reset_w },
683         { 0xcc00, 0xcfff, MWA_RAM },
684         { 0xd000, 0xffff, MWA_ROM },
685 MEMORY_END
686
687 static MEMORY_READ_START( sound_readmem )
688         { 0x0000, 0x007f, MRA_RAM },
689         { 0x0400, 0x0403, pia_2_r },
690         { 0x8400, 0x8403, pia_2_r },    // used by Colony 7, perhaps others?
691         { 0xb000, 0xffff, MRA_ROM },    // most games start at $F000, Sinistar starts at $B000
692 MEMORY_END
693
694
695 static MEMORY_WRITE_START( sound_writemem )
696         { 0x0000, 0x007f, MWA_RAM },
697         { 0x0400, 0x0403, pia_2_w },
698         { 0x8400, 0x8403, pia_2_w },    // used by Colony 7, perhaps others?
699         { 0xb000, 0xffff, MWA_ROM },    // most games start at $F000, Sinistar starts at $B000
700 MEMORY_END
701
702 MACHINE_INIT( williams )
703 {
704         // reset the PIAs
705         pia_reset();
706
707         // reset the ticket dispenser (Lotto Fun)
708         ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
709
710         // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
711         timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
712
713         // also set a timer to go off on scanline 240
714         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
715 }
716
717
718 static void williams_va11_callback(int scanline)
719 {
720         // the IRQ signal comes into CB1, and is set to VA11
721         pia_1_cb1_w(0, scanline & 0x20);
722
723         // update the screen while we're here
724         force_partial_update(scanline - 1);
725
726         // set a timer for the next update
727         scanline += 8;
728         if (scanline >= 256) scanline = 0;
729         timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
730 }
731
732
733 static void williams_count240_off_callback(int param)
734 {
735         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
736         pia_1_ca1_w(0, 0);
737 }
738
739
740 static void williams_count240_callback(int param)
741 {
742         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
743         pia_1_ca1_w(0, 1);
744
745         // set a timer to turn it off once the scanline counter resets
746         timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
747
748         // set a timer for next frame
749         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
750 }
751
752
753 static void williams_main_irq(int state)
754 {
755         // IRQ to the main CPU
756         cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
757 }
758
759
760 static void williams_main_firq(int state)
761 {
762         // FIRQ to the main CPU
763         cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
764 }
765
766
767 static void williams_snd_irq(int state)
768 {
769         // IRQ to the sound CPU
770         cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
771 }
772
773
774 READ_HANDLER( williams_video_counter_r )
775 {
776         return cpu_getscanline() & 0xFC;
777 }
778
779
780 // Special PIA 0 for Stargate, to handle the controls
781 struct pia6821_interface stargate_pia_0_intf =
782 {
783         //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
784         //outputs: A/B,CA/B2       / 0, 0, 0, 0,
785         //irqs   : A/B             / 0, 0
786 };
787
788 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
789 struct pia6821_interface williams_pia_1_intf =
790 {
791         //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
792         //outputs: A/B,CA/B2       / 0, williams_snd_cmd_w, 0, 0,
793         //irqs   : A/B             / williams_main_irq, williams_main_irq
794 };
795
796 // Generic PIA 2, maps to DAC data in and sound IRQs
797 struct pia6821_interface williams_snd_pia_intf =
798 {
799         //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
800         //outputs: A/B,CA/B2       / DAC_0_data_w, 0, 0, 0,
801         //irqs   : A/B             / williams_snd_irq, williams_snd_irq
802 };
803
804 static DRIVER_INIT( stargate )
805 {
806         // CMOS configuration
807         CONFIGURE_CMOS(0xCC00, 0x400);
808
809         // PIA configuration
810         CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
811 }
812
813
814 int cpu_getscanline(void)
815 {
816         return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
817 }
818
819  *************************************
820  *
821  *      Returns time until given scanline
822  *
823  *************************************
824
825 double cpu_getscanlinetime(int scanline)
826 {
827         double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
828         double abstime = timer_get_time();
829         double result;
830
831         // if we're already past the computed time, count it for the next frame
832         if (abstime >= scantime)
833                 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
834
835         // compute how long from now until that time
836         result = scantime - abstime;
837
838         // if it's small, just count a whole frame
839         if (result < TIME_IN_NSEC(1))
840                 result = TIME_IN_HZ(Machine->drv->frames_per_second);
841         return result;
842 }
843
844  *************************************
845  *
846  *      Returns time for one scanline
847  *
848  *************************************
849
850 double cpu_getscanlineperiod(void)
851 {
852         return scanline_period;
853 }
854
855
856 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
857 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
858 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
859
860 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
861 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
862 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
863
864 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
865 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
866 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
867 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
868
869 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
870 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
871 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
872 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
873
874 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
875 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
876 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
877 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
878
879 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
880 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
881 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
882 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
883
884 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
885 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
886 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
887 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
888
889 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
890 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
891 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
892
893 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
894 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
895 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
896 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
897
898         C80C rom_pia_dataa
899         C80D rom_pia_ctrla
900         C80E rom_pia_datab
901               bit 0 \
902               bit 1 |
903               bit 2 |-6 bits to sound board
904               bit 3 |
905               bit 4 |
906               bit 5 /
907               bit 6 \
908               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
909         C80F rom_pia_ctrlb
910
911 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
912
913
914 PIA initialization:
915
916 00 -> $C80D = PIA2     -> DDR active
917 00 -> $C80C = PIA2 DDR -> All input?
918
919
920
921 */
922
923 #if 0
924
925 #define PIA_IRQ1                                (0x80)
926 #define PIA_IRQ2                                (0x40)
927
928 #define IRQ1_ENABLED(c)                 ( (((c) >> 0) & 0x01))
929 #define C1_LOW_TO_HIGH(c)               ( (((c) >> 1) & 0x01))
930 #define C1_HIGH_TO_LOW(c)               (!(((c) >> 1) & 0x01))
931 #define OUTPUT_SELECTED(c)              ( (((c) >> 2) & 0x01))
932 #define IRQ2_ENABLED(c)                 ( (((c) >> 3) & 0x01))
933 #define STROBE_E_RESET(c)               ( (((c) >> 3) & 0x01))
934 #define STROBE_C1_RESET(c)              (!(((c) >> 3) & 0x01))
935 #define C2_SET(c)                               ( (((c) >> 3) & 0x01))
936 #define C2_LOW_TO_HIGH(c)               ( (((c) >> 4) & 0x01))
937 #define C2_HIGH_TO_LOW(c)               (!(((c) >> 4) & 0x01))
938 #define C2_SET_MODE(c)                  ( (((c) >> 4) & 0x01))
939 #define C2_STROBE_MODE(c)               (!(((c) >> 4) & 0x01))
940 #define C2_OUTPUT(c)                    ( (((c) >> 5) & 0x01))
941 #define C2_INPUT(c)                             (!(((c) >> 5) & 0x01))
942
943 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
944 {
945         pia6821_state *p = get_token(device);
946
947         /* limit the data to 0 or 1 */
948         data = data ? TRUE : FALSE;
949
950         LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
951
952         /* the new state has caused a transition */
953         if ((p->in_ca1 != data) &&
954                 ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
955         {
956                 LOG(("PIA #%s: CA1 triggering\n", device->tag));
957
958                 /* mark the IRQ */
959                 p->irq_a1 = TRUE;
960
961                 /* update externals */
962                 update_interrupts(device);
963
964                 /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
965                 if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
966                         set_out_ca2(device, TRUE);
967         }
968
969         /* set the new value for CA1 */
970         p->in_ca1 = data;
971         p->in_ca1_pushed = TRUE;
972 }
973
974 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
975 {
976         pia6821_state *p = get_token(device);
977
978         /* limit the data to 0 or 1 */
979         data = data ? 1 : 0;
980
981         LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
982
983         /* the new state has caused a transition */
984         if ((p->in_cb1 != data) &&
985                 ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
986         {
987                 LOG(("PIA #%s: CB1 triggering\n", device->tag));
988
989                 /* mark the IRQ */
990                 p->irq_b1 = 1;
991
992                 /* update externals */
993                 update_interrupts(device);
994
995                 /* If CB2 is configured as a write-strobe output which is reset by a CB1
996            transition, this reset will only happen when a read from port B implicitly
997            clears the IRQ B1 flag.  So we handle the CB2 reset there.  Note that this
998            is different from what happens with port A. */
999         }
1000
1001         /* set the new value for CB1 */
1002         p->in_cb1 = data;
1003         p->in_cb1_pushed = TRUE;
1004 }
1005
1006 static void update_interrupts(const device_config *device)
1007 {
1008         pia6821_state *p = get_token(device);
1009         int new_state;
1010
1011         /* start with IRQ A */
1012         new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1013
1014         if (new_state != p->irq_a_state)
1015         {
1016                 p->irq_a_state = new_state;
1017                 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1018         }
1019
1020         /* then do IRQ B */
1021         new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1022
1023         if (new_state != p->irq_b_state)
1024         {
1025                 p->irq_b_state = new_state;
1026                 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1027         }
1028 }
1029
1030 static void control_b_w(const device_config *device, UINT8 data)
1031 {
1032         pia6821_state *p = get_token(device);
1033         int temp;
1034
1035         /* bit 7 and 6 are read only */
1036         data &= 0x3f;
1037
1038         LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1039
1040         /* update the control register */
1041         p->ctl_b = data;
1042
1043         if (C2_SET_MODE(p->ctl_b))
1044                 /* set/reset mode - bit value determines the new output */
1045                 temp = C2_SET(p->ctl_b);
1046         else
1047                 /* strobe mode - output is always high unless strobed */
1048                 temp = TRUE;
1049
1050         set_out_cb2(device, temp);
1051
1052         /* update externals */
1053         update_interrupts(device);
1054 }
1055
1056 static void control_a_w(const device_config *device, UINT8 data)
1057 {
1058         pia6821_state *p = get_token(device);
1059
1060         /* bit 7 and 6 are read only */
1061         data &= 0x3f;
1062
1063         LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1064
1065         /* update the control register */
1066         p->ctl_a = data;
1067
1068         /* CA2 is configured as output */
1069         if (C2_OUTPUT(p->ctl_a))
1070         {
1071                 int temp;
1072
1073                 if (C2_SET_MODE(p->ctl_a))
1074                         /* set/reset mode - bit value determines the new output */
1075                         temp = C2_SET(p->ctl_a);
1076                 else
1077                         /* strobe mode - output is always high unless strobed */
1078                         temp = TRUE;
1079
1080                 set_out_ca2(device, temp);
1081         }
1082
1083         /* update externals */
1084         update_interrupts(device);
1085 }
1086
1087
1088 CTRL REGISTER:
1089
1090 B7        B6        B5 B4 B3     B2   B1 B0
1091 --------------------------------------------------
1092 IRQA(B)1  IRQA(B)2  CA(B)2 Ctrl  DDR  CA(B)1 Ctrl
1093
1094 Bits 6 & 7 are RO. IRQs are cleared on read of PORTA when not in DDR mode
1095 DDR: 0: DDR selected, 1: Output register selected
1096 CA1(CB1) Ctrl: B0: 0/1 Dis/enable interrupt IRQA(B)
1097                B1: 0/1 IRQ set by Hi-to-Lo/Lo-to-Hi transition on CA(B)1
1098 CA2(CB2) Ctrl: If B5==0, B4 & B3 are similar to B1 & B0
1099
1100 Entering main loop...
1101 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=F4DC] --> Set DDR on PORTA, IRQs off
1102 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=F4DF] --> Set DDR to all input on PORTA
1103 V6809 WrMem: Writing PIA (PACTL) address C80D [->3C, PC=F4E4] --> Set Output on PORTA, Set CA2 = 1, disable IRQA1
1104 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=F4E7] --> Set DDR on PORTB, IRQs off
1105 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4EC] --> Set DDR to output on 6,7 input on 0-5 on PORTB
1106 V6809 WrMem: Writing PIA (PBCTL) address C80F [->3C, PC=F4F1] --> Set Output on PORTA, Set CB2 = 1, disable IRQB1
1107 V6809 WrMem: Writing PIA (PORTB) address C80E [->C0, PC=F4F6] --> Send 1s on bits 6 & 7 on PORTB
1108 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=F523] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1109 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=F526] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1110 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=F529] --> Send 0s on bits 6 & 7 on PORTB
1111 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=6076] --> Do nothing
1112 V6809 WrMem: Writing PIA (PACTL) address C80D [->00, PC=6076] --> Set DDR on PORTA, IRQs off
1113 V6809 WrMem: Writing PIA (PORTA) address C80C [->00, PC=607B] --> Set DDR to all input on PORTA
1114 V6809 WrMem: Writing PIA (PACTL) address C80D [->34, PC=607B] --> Set Output on PORTA, Set CA2 = 0, disable IRQA1
1115 V6809 WrMem: Writing PIA (PORTB) address C80E [->00, PC=6076] --> Send 0s on bits 6 & 7 on PORTB
1116 V6809 WrMem: Writing PIA (PBCTL) address C80F [->00, PC=6076] --> Set DDR on PORTB, IRQs off
1117 V6809 WrMem: Writing PIA (PORTB) address C80E [->FF, PC=607B] --> Set DDR to all output on PORTB
1118 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=607B] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1119 V6809 WrMem: Writing PIA (PORTB) address C80E [->3F, PC=6088] --> Send $3F on PORTB
1120 V6809 WrMem: Writing PIA (PORTB) address C80E [->0C, PC=60DB] --> Send $0C on PORTB
1121 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1122  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]  --> Clear IRQBs
1123  6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=075B]  --> Clear IRQAs
1124  6809 RdMem: Reading PIA (PORTA) address C80C [=00, PC=07B9]  --> Clear IRQAs
1125
1126 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644] --> Set Output on PORTB, Set CB2 = 0, enable IRQB1
1127 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3] --> Set Output on PORTB, Set CB2 = 0, disable IRQB1
1128  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]  --> Clear IRQBs
1129 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1130 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1131  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1132 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1133 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1134  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1135 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1136 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1137  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1138 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1139 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1140  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1141 V6809 WrMem: Writing PIA (PBCTL) address C80F [->35, PC=1644]
1142 V6809 WrMem: Writing PIA (PBCTL) address C80F [->34, PC=15C3]
1143  6809 RdMem: Reading PIA (PORTB) address C80E [=0C, PC=15C6]
1144
1145 #endif