]> Shamusworld >> Repos - stargem2/blob - src/stargem2.cpp
Fixed the dreaded demo failing bug! HUZZAH! Turned out to be an IRQ line
[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 //
14
15 #include "SDL.h"
16 #include <fstream>
17 #include <string>
18 #include <iomanip>
19 #include <iostream>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <time.h>
23 #include "types.h"
24 #include "log.h"
25 #include "v6808.h"
26 #include "v6809.h"
27 #include "video.h"
28 #include "sound.h"
29 #include "timing.h"
30 #include "settings.h"
31 #include "dis6809.h"
32 #include "dis6808.h"
33
34 #define __DEBUG__
35
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 // Global variables
45
46 uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
47 V6809REGS mainCPU;
48 V6808REGS soundCPU;
49 uint8 color[16];
50 uint32 palette[256];
51 bool paletteDirty = false;
52
53 // Local variables
54
55 static bool running = true;                                             // Machine running state flag...
56 static uint32 startTicks;
57 static uint8 * keys;                                                    // SDL raw keyboard matrix
58
59 // Local timer callback functions
60
61 static void FrameCallback(void);
62 static void ScanlineCallback(void);
63
64
65 //
66 // 6809 memory functions
67 //
68
69 #ifdef LOG_PIA1_IO
70 char piaRegsName[4][10] = { "PORTA", "PACTL", "PORTB", "PBCTL" };
71 #endif
72 uint8 RdMem6809(uint16 addr)
73 {
74         uint8 b;
75
76         if (addr >= 0x9000 && addr <= 0xCFFF)           // No ROM between $9000 - $CFFF...
77                 b = gram[addr];
78         else
79         {
80                 if (!gram[0xC900] && addr <= 0x8FFF)    // Check RAM $C900 bank switch
81                         b = gram[addr];
82                 else
83                         b = grom[addr];
84         }
85
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...
89
90         // More kludge...
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
93
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
96
97 //temp...
98 /*extern uint16 pcr;
99 //if (addr >= 0xC000 && addr <= 0xCBFF)
100 if (addr == 0x9C59)
101         WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
102 #ifdef LOG_PIA1_IO
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());//*/
105 #endif
106         return b;
107 }
108
109 void WrMem6809(uint16 addr, uint8 b)
110 {
111 //temp...
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]);//*/
119
120         gram[addr] = b;
121
122         if (addr > 0x0006 && addr < 0x97F7)                     // 304 pixels  152-128=24-16=8
123         {
124                 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
125                 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
126
127                 if (sy > 5 && sy < 246)
128                 {
129                         uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
130 //Hmm. This approach won't work with palette color cycling...
131 #if 0
132                         scrBuffer[saddr + 0] = b >> 4;
133                         scrBuffer[saddr + 1] = b & 0x0F;
134 #else
135                         scrBuffer[saddr + 0] = palette[color[b >> 4]];
136                         scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
137 #endif
138                 }
139         }
140         else if (addr >= 0xC000 && addr <= 0xC00F)
141 //Let's see if we can fix the color cycling here... [DONE]
142 #if 0
143                 color[addr - 0xC000] = b;                                               // color[] from VIDEO.CPP (not any more!)
144 #else
145         {
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;
151
152 #if 0
153                 for(uint32 addr=0x0007; addr<0x97F7; addr++)
154                 {
155                         uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
156
157                         if (sy > 5 && sy < 246)
158                         {
159                                 uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
160                                 uint8 sb = gram[addr];
161
162                                 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
163                                 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
164                         }
165                 }
166 #else
167                 paletteDirty = true;
168 #endif
169         }
170 #endif
171         else if (addr == 0xC80E)
172         {
173                 sram[0x0402] = b;                                                               // Connect PIAs in 6809 & 6808
174                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ;             // Start sound IRQ
175         }
176
177 #ifdef LOG_PIA1_IO
178 //if (addr >= 0xC80C && addr <= 0xC80F)
179 if (addr == 0xC80D)
180         WriteLog("V6809 WrMem: Writing PIA (%s) address %04X [->%02X, PC=%04X]\n", piaRegsName[addr&0x03], addr, b, GetCurrentV6809PC());//*/
181 #endif
182 }
183
184 //
185 // 6808 memory functions
186 //
187
188 uint8 RdMem6808(uint16 addr)
189 {
190         return (addr < 0xF000 ? sram[addr] : srom[addr]);
191 }
192
193 void WrMem6808(uint16 addr, uint8 b)
194 {
195         sram[addr] = b;
196
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;
201 }
202
203 //
204 // Load a file into RAM/ROM image space
205 //
206 bool LoadImg(const char * filename, uint8 * ram, int size)
207 {
208         FILE * fp = fopen(filename, "rb");
209
210         if (fp == NULL)
211                 return false;
212
213         size_t ignoredResult = fread(ram, 1, size, fp);
214         fclose(fp);
215
216         return true;
217 }
218
219 //
220 // Save CMOS ram
221 //
222 void SaveCMOS(void)
223 {
224         FILE * fp = fopen(CMOS, "wb");
225
226         if (fp != NULL)
227         {
228                 size_t ignoredResult = fwrite(gram + 0xCC00, 1, 1024, fp);
229                 fclose(fp);
230         }
231         else
232                 WriteLog("CMOS RAM not saved!\n");
233 }
234
235 //
236 // Load state save file
237 //
238 bool LoadMachineState(void)
239 {
240         FILE * fp = fopen(SAVESTATE, "rb");
241
242         if (fp == NULL)
243                 return false;
244
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);
250         fclose(fp);
251
252         for(int i=0x0006; i<0x97F8; i++)                                        // Set up backbuffer... ;-)
253                 WrMem6809(i, gram[i]);
254
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;
259
260         return true;
261 }
262
263 //
264 // Save state save file
265 //
266 void SaveMachineState(void)
267 {
268         FILE * fp = fopen(SAVESTATE, "wb");
269
270         if (fp != NULL)
271         {
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);
276                 fclose(fp);
277         }
278         else
279                 WriteLog("Machine state not saved!\n");
280 }
281
282 //
283 // Main loop
284 //
285 int main(int /*argc*/, char * /*argv*/[])
286 {
287         InitLog("stargem2.log");
288         WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
289
290         LoadSettings();
291
292         // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
293         for(uint32 i=0; i<256; i++)
294                 palette[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;
299 #else
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;
303 #endif
304
305         // Zero out memory
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);
312
313         // Set up V6809 & V6808 execution contexts
314
315         memset(&mainCPU, 0, sizeof(V6809REGS));
316         mainCPU.RdMem = RdMem6809;
317         mainCPU.WrMem = WrMem6809;
318         mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
319
320         memset(&soundCPU, 0, sizeof(V6808REGS));
321         soundCPU.RdMem = RdMem6808;
322         soundCPU.WrMem = WrMem6808;
323         soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
324
325         char ROMs[12][8] = {
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"
328                 };
329
330         for(int i=0; i<12; i++)
331         {
332                 uint32 baseAddress = i * 0x1000;
333
334                 if (i > 8)
335                         baseAddress += 0x4000;
336
337 #if 0
338 WriteLog("Loading ROM image '%s' at $%04X...\n", ROMs[i], baseAddress);
339 #endif
340 //              if (!LoadImg(ROMs[i], grom + (i * 0x1000), 0x1000))
341                 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
342                 {
343                         WriteLog("Could not open file '%s'!\n", ROMs[i]);
344                         return -1;
345                 }
346         }
347
348         if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
349         {
350                 WriteLog("Could not open file '%s'!\n", SOUNDROM);
351                 return -1;
352         }
353
354         WriteLog("Stargate ROM images loaded...\n");
355         WriteLog("About to initialize video...\n");
356
357         if (!InitVideo())
358         {
359                 cout << "Aborting!" << endl;
360                 return -1;
361         }
362
363         // Have to do this *after* video init but *before* sound init...!
364         WriteLog("About to load machine state...");
365
366         if (!LoadMachineState())
367                 WriteLog("Machine state file not found!\n");
368         else
369                 WriteLog("done!\n");
370
371         if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
372                 WriteLog("CMOS RAM not found!\n");
373
374         WriteLog("About to intialize audio...\n");
375         SoundInit();
376 //      uint8 * keys = SDL_GetKeyState(NULL);
377         keys = SDL_GetKeyState(NULL);
378
379 //      running = true;                                                         // Set running status...
380         srom[0xF800] = 0x37;                                            // Fix checksum so ST works...
381
382 #if 0
383
384 //kludge...
385 //This didn't work--it still acted like the old way (interrupt @ VC = 0)
386 //gram[0xCB00] = 64*3;
387
388         WriteLog("Entering main loop...\n");
389         while (running)
390         {
391                 SDL_PumpEvents();                                               // Force key events into the buffer.
392                 gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
393
394                 if (keys[SDLK_ESCAPE])
395                         running = false;                                        // ESC to exit...
396
397                 if (keys[SDLK_SEMICOLON])
398                         gram[0xC804] |= 0x01;                           // Fire (;)
399                 if (keys[SDLK_l])
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)
405                 if (keys[SDLK_2])
406                         gram[0xC804] |= 0x10;                           // Two Player Start (2)
407                 if (keys[SDLK_1])
408                         gram[0xC804] |= 0x20;                           // One Player Start (1)
409                 if (keys[SDLK_RETURN])
410                         gram[0xC804] |= 0x40;                           // Reverse (Enter)
411                 if (keys[SDLK_f])
412                         gram[0xC804] |= 0x80;                           // Down (F)
413
414                 if (keys[SDLK_r])
415                         gram[0xC806] |= 0x01;                           // Up (R)
416                 if (keys[SDLK_a])
417                         gram[0xC806] |= 0x02;                           // Inviso (A)
418
419                 if (keys[SDLK_F1])
420                         gram[0xC80C] |= 0x01;                           // Auto up (F1)
421                 if (keys[SDLK_F2])
422                         gram[0xC80C] |= 0x02;                           // Advance (F2)
423                 if (keys[SDLK_5])
424                         gram[0xC80C] |= 0x04;                           // Right Coin (5)
425                 if (keys[SDLK_F3])
426                         gram[0xC80C] |= 0x08;                           // High Score Reset (F3)
427                 if (keys[SDLK_3])
428                         gram[0xC80C] |= 0x10;                           // Left Coin (3)
429                 if (keys[SDLK_4])
430                         gram[0xC80C] |= 0x20;                           // Center Coin (4)
431                 if (keys[SDLK_F4])
432                         gram[0xC80C] |= 0x40;                           // Slam Switch (F4)
433
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;
438
439 /*
440 $CB00 is scanline counter, bits 2-7 (1 frame/240 =69.44... usec)
441
442 Some places of interest to look at:
443
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
450 */
451                 uint32 startTicks = SDL_GetTicks();
452 //              long video_clk = 0;
453 //              gram[0xCB00] = 0;
454
455 /*
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!
459
460         // the IRQ signal comes into CB1, and is set to VA11
461         pia_1_cb1_w(0, scanline & 0x20);
462 ...
463         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
464         pia_1_ca1_w(0, 0);
465 ...
466         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
467         pia_1_ca1_w(0, 1);
468 */
469
470 //WriteLog("--> Start of frame...\n");
471                 for(int i=0; i<3; i++)
472                 {
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;//*/
477
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;//*/
484
485                         gram[0xCB00] += 64;                                     // Update video counter...
486                 }
487
488 //Hmm.
489 /*if (gram[0xC80E] & 0x01)
490         mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
491 //48 lines... (+ 16)
492 //gram[0xCB00] = 0;
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;
501
502 /*if (gram[0xC80F] & 0x01)
503                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
504 gram[0xCB00] = 0; //*/
505
506                 gram[0xCB00] += 48;                                             // Update video counter...
507
508                 Execute6809(&mainCPU, 1000);
509                 mainCPU.clock -= 1000;                                  // Remove 1K ticks from clock (in case it overflowed)
510 //Eh?
511 //Ok, this is the interrupt it's looking for, but still...
512 //if (gram[0xC80F] & 0x01)
513 //              mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
514
515                 gram[0xCB00] += 16;                                             // Update video counter...
516
517 //              RenderScreenBuffer();
518                 RenderScreenBuffer2();  // 1 frame = 16667 cycles
519 //              WriteLog("Main: Rendered back buffer. [6809 PC=%04X]\n", pcr);
520
521                 Execute6809(&mainCPU, 667);                             // Do QnD VBLANK
522
523                 // 1/60 sec = ? ms (16.6 ms)
524                 while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
525
526 //kludge, temp...
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]);//*/
531         }
532
533 /*uint16 pc = 0x15BA;
534 for(int i=0; i<200; i++)
535 //while (pc < 0x9000)
536         pc += Decode6809(pc);//*/
537
538 #else
539
540         running = true;                                                         // Set running status...
541
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();
549
550         WriteLog("Entering main loop...\n");
551
552         while (running)
553         {
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);
559                 HandleNextEvent();
560         }
561
562 #ifdef __DEBUG__
563 WriteLog("\n");
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);
566 WriteLog("\n");
567
568 /*uint16 pc = mainCPU.pc;//0x15BA;
569 for(int i=0; i<200; i++)
570 //while (pc < 0x9000)
571 {
572         pc += Decode6809(pc);
573         WriteLog("\n");
574 }//*/
575
576 /*uint32 pc = 0;
577 while (pc < 0xFFFF)
578 {
579         pc += Decode6809(pc);
580         WriteLog("\n");
581 }//*/
582 #endif
583
584 #endif
585
586         SoundDone();
587         VideoDone();
588         SaveCMOS();
589         SaveMachineState();
590         LogDone();
591
592         return 0;
593 }
594
595 static void FrameCallback(void)
596 {
597         SDL_PumpEvents();                                                       // Force key events into the buffer.
598         gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
599
600         if (keys[SDLK_ESCAPE])
601                 running = false;                                                // ESC to exit...
602
603 //Convert this stuff to use the settings module... !!! FIX !!!
604         if (keys[SDLK_SEMICOLON])
605                 gram[0xC804] |= 0x01;                                   // Fire (;)
606         if (keys[SDLK_l])
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)
612         if (keys[SDLK_2])
613                 gram[0xC804] |= 0x10;                                   // Two Player Start (2)
614         if (keys[SDLK_1])
615                 gram[0xC804] |= 0x20;                                   // One Player Start (1)
616         if (keys[SDLK_RETURN])
617                 gram[0xC804] |= 0x40;                                   // Reverse (Enter)
618         if (keys[SDLK_f])
619                 gram[0xC804] |= 0x80;                                   // Down (F)
620
621         if (keys[SDLK_r])
622                 gram[0xC806] |= 0x01;                                   // Up (R)
623         if (keys[SDLK_a])
624                 gram[0xC806] |= 0x02;                                   // Inviso (A)
625
626         if (keys[SDLK_F1])
627                 gram[0xC80C] |= 0x01;                                   // Auto up (F1)
628         if (keys[SDLK_F2])
629                 gram[0xC80C] |= 0x02;                                   // Advance (F2)
630         if (keys[SDLK_5])
631                 gram[0xC80C] |= 0x04;                                   // Right Coin (5)
632         if (keys[SDLK_F3])
633                 gram[0xC80C] |= 0x08;                                   // High Score Reset (F3)
634         if (keys[SDLK_3])
635                 gram[0xC80C] |= 0x10;                                   // Left Coin (3)
636         if (keys[SDLK_4])
637                 gram[0xC80C] |= 0x20;                                   // Center Coin (4)
638         if (keys[SDLK_F4])
639                 gram[0xC80C] |= 0x40;                                   // Slam Switch (F4)
640
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;
645
646 #if 0
647 //Grr...
648 for(int i=0; i<16; i++)
649         WrMem6809(0xC000 + i, gram[0x9C26 + i]);//*/
650 #endif
651
652         if (paletteDirty)
653         {
654                 for(uint32 addr=0x0007; addr<0x97F7; addr++)
655                 {
656                         uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
657
658                         if (sy > 5 && sy < 246)
659                         {
660                                 uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
661                                 uint8 sb = gram[addr];
662
663                                 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
664                                 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
665                         }
666                 }
667
668                 paletteDirty = false;
669         }
670
671         RenderScreenBuffer();                                           // 1 frame = 1/60 sec ~ 16667 cycles
672         SetCallbackTime(FrameCallback, 16666.66666667);
673
674 //Hmm. Yield some time?
675 //This works, but doesn't seem to yield much CPU--maybe 10%
676 //SDL_Delay(2);
677 //The following works much better--yields as much as 50%
678         // Wait for next frame...
679         while (SDL_GetTicks() - startTicks < 16)
680                 SDL_Delay(1);
681
682         startTicks = SDL_GetTicks();
683 }
684
685 static void ScanlineCallback(void)
686 {
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 !!!
690 #if 0
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 )
698 {
699         return cpu_getscanline() & 0xFC;
700 }
701 #endif
702 //      mainCPU.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
703
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;//*/
708
709         if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
710                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
711
712 /*      if ((gram[0xCB00] >= 0xF0) && (gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
713                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
714
715 // Hmm. No. But this *should* do it... Why doesn't it???
716 /*
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...
721 */
722         if ((gram[0xCB00] >= 240) && (gram[0xC80D] & 0x09))     // Do COUNT240 IRQ (if enabled!)
723                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
724
725 //Is $C80E COUNT240? Hmm... Doesn't seem to be. Bleh.
726 /*      if (gram[0xCB00] >= 240)
727                 gram[0xC80E] = 0xFF;
728         else
729                 gram[0xC80E] = 0x00;//*/
730 //      gram[0xC80E] = (gram[0xCB00] >= 240 ? 0xFF : 0x00);
731
732         // This should set everything between $CB00-CBFF...
733 //      gram[0xCB00] += 8;                                                      // Update video counter...
734         gram[0xCB00] += 4;                                                      // Update video counter...
735
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
738 }
739
740
741 /*
742 ; With correct timing, but no color cycling
743
744 --> Start of frame...
745 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
746 At $07AD. $6E: 00
747 At $0B66. $6E: 00
748 At $0CF4. $6E: 00
749 At $0CCB. $6E: 00
750 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
751 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
752 At $0718. $6E: 01
753 At $07AD. $6E: 01
754 At $0BB8. $6E: 01
755 At $0927. $6E: 01
756 At $0CF4. $6E: 01
757 At $0B66. $6E: 01
758 At $16C8. $6E: 01
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]
762
763
764 ; With incorrect timing, but has color cycling
765
766 --> Start of frame...
767 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
768 At $1609. $6E: 00                       ; Color cycling...
769 At $07AD. $6E: 00
770 At $0B66. $6E: 00
771 At $0CF4. $6E: 00
772 At $0CCB. $6E: 00
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]
777 At $0718. $6E: 01
778 At $07AD. $6E: 01
779 At $0BB8. $6E: 01
780 At $0927. $6E: 01
781 At $0CF4. $6E: 01
782 At $0B66. $6E: 01
783 At $16C8. $6E: 01
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]
787
788
789
790         Stargate
791         --------
792
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)
795         9800-BFFF RAM
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
799         C000-CFFF I/O
800         D000-FFFF ROM
801
802         C000-C00F color_registers  (16 bytes of BBGGGRRR)
803
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
811
812         C80C rom_pia_dataa
813         C80D rom_pia_ctrla
814         C80E rom_pia_datab
815               bit 0 \
816               bit 1 |
817               bit 2 |-6 bits to sound board
818               bit 3 |
819               bit 4 |
820               bit 5 /
821               bit 6 \
822               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
823         C80F rom_pia_ctrlb
824
825         C900 rom_enable_scr_ctrl  Switch between video ram and rom at 0000-97FF
826
827         Stargate
828         --------
829         C804 widget_pia_dataa (widget = I/O board)
830           bit 0  Fire
831           bit 1  Thrust
832           bit 2  Smart Bomb
833           bit 3  HyperSpace
834           bit 4  2 Players
835           bit 5  1 Player
836           bit 6  Reverse
837           bit 7  Down
838
839         C806 widget_pia_datab
840           bit 0  Up
841           bit 1  Inviso
842           bit 2
843           bit 3
844           bit 4
845           bit 5
846           bit 6
847           bit 7  0 = Upright  1 = Table
848
849         C80C rom_pia_dataa
850           bit 0  Auto Up
851           bit 1  Advance
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)
856           bit 6  Slam Door Tilt
857           bit 7  Hand Shake from sound board
858 */
859
860
861 /*
862
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 },
871 MEMORY_END
872
873
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 },
885 MEMORY_END
886
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
892 MEMORY_END
893
894
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
900 MEMORY_END
901
902 MACHINE_INIT( williams )
903 {
904         // reset the PIAs
905         pia_reset();
906
907         // reset the ticket dispenser (Lotto Fun)
908         ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
909
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);
912
913         // also set a timer to go off on scanline 240
914         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
915 }
916
917
918 static void williams_va11_callback(int scanline)
919 {
920         // the IRQ signal comes into CB1, and is set to VA11
921         pia_1_cb1_w(0, scanline & 0x20);
922
923         // update the screen while we're here
924         force_partial_update(scanline - 1);
925
926         // set a timer for the next update
927         scanline += 8;
928         if (scanline >= 256) scanline = 0;
929         timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
930 }
931
932
933 static void williams_count240_off_callback(int param)
934 {
935         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
936         pia_1_ca1_w(0, 0);
937 }
938
939
940 static void williams_count240_callback(int param)
941 {
942         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
943         pia_1_ca1_w(0, 1);
944
945         // set a timer to turn it off once the scanline counter resets
946         timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
947
948         // set a timer for next frame
949         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
950 }
951
952
953 static void williams_main_irq(int state)
954 {
955         // IRQ to the main CPU
956         cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
957 }
958
959
960 static void williams_main_firq(int state)
961 {
962         // FIRQ to the main CPU
963         cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
964 }
965
966
967 static void williams_snd_irq(int state)
968 {
969         // IRQ to the sound CPU
970         cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
971 }
972
973
974 READ_HANDLER( williams_video_counter_r )
975 {
976         return cpu_getscanline() & 0xFC;
977 }
978
979
980 // Special PIA 0 for Stargate, to handle the controls
981 struct pia6821_interface stargate_pia_0_intf =
982 {
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,
985         //irqs   : A/B             / 0, 0
986 };
987
988 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
989 struct pia6821_interface williams_pia_1_intf =
990 {
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
994 };
995
996 // Generic PIA 2, maps to DAC data in and sound IRQs
997 struct pia6821_interface williams_snd_pia_intf =
998 {
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
1002 };
1003
1004 static DRIVER_INIT( stargate )
1005 {
1006         // CMOS configuration
1007         CONFIGURE_CMOS(0xCC00, 0x400);
1008
1009         // PIA configuration
1010         CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
1011 }
1012
1013
1014 int cpu_getscanline(void)
1015 {
1016         return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
1017 }
1018
1019  *************************************
1020  *
1021  *      Returns time until given scanline
1022  *
1023  *************************************
1024
1025 double cpu_getscanlinetime(int scanline)
1026 {
1027         double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
1028         double abstime = timer_get_time();
1029         double result;
1030
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);
1034
1035         // compute how long from now until that time
1036         result = scantime - abstime;
1037
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);
1041         return result;
1042 }
1043
1044  *************************************
1045  *
1046  *      Returns time for one scanline
1047  *
1048  *************************************
1049
1050 double cpu_getscanlineperiod(void)
1051 {
1052         return scanline_period;
1053 }
1054
1055
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]
1059
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]
1063
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]
1068
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]
1073
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]
1078
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]
1083
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]
1088
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]
1092
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]
1097
1098         C80C rom_pia_dataa
1099         C80D rom_pia_ctrla
1100         C80E rom_pia_datab
1101               bit 0 \
1102               bit 1 |
1103               bit 2 |-6 bits to sound board
1104               bit 3 |
1105               bit 4 |
1106               bit 5 /
1107               bit 6 \
1108               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
1109         C80F rom_pia_ctrlb
1110
1111 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
1112
1113
1114 PIA initialization:
1115
1116 00 -> $C80D = PIA2     -> DDR active
1117 00 -> $C80C = PIA2 DDR -> All input?
1118
1119
1120
1121 */
1122
1123 #if 0
1124
1125 #define PIA_IRQ1                                (0x80)
1126 #define PIA_IRQ2                                (0x40)
1127
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))
1142
1143 WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
1144 {
1145         pia6821_state *p = get_token(device);
1146
1147         /* limit the data to 0 or 1 */
1148         data = data ? TRUE : FALSE;
1149
1150         LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
1151
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))))
1155         {
1156                 LOG(("PIA #%s: CA1 triggering\n", device->tag));
1157
1158                 /* mark the IRQ */
1159                 p->irq_a1 = TRUE;
1160
1161                 /* update externals */
1162                 update_interrupts(device);
1163
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);
1167         }
1168
1169         /* set the new value for CA1 */
1170         p->in_ca1 = data;
1171         p->in_ca1_pushed = TRUE;
1172 }
1173
1174 WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
1175 {
1176         pia6821_state *p = get_token(device);
1177
1178         /* limit the data to 0 or 1 */
1179         data = data ? 1 : 0;
1180
1181         LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
1182
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))))
1186         {
1187                 LOG(("PIA #%s: CB1 triggering\n", device->tag));
1188
1189                 /* mark the IRQ */
1190                 p->irq_b1 = 1;
1191
1192                 /* update externals */
1193                 update_interrupts(device);
1194
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. */
1199         }
1200
1201         /* set the new value for CB1 */
1202         p->in_cb1 = data;
1203         p->in_cb1_pushed = TRUE;
1204 }
1205
1206 static void update_interrupts(const device_config *device)
1207 {
1208         pia6821_state *p = get_token(device);
1209         int new_state;
1210
1211         /* start with IRQ A */
1212         new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
1213
1214         if (new_state != p->irq_a_state)
1215         {
1216                 p->irq_a_state = new_state;
1217                 devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
1218         }
1219
1220         /* then do IRQ B */
1221         new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
1222
1223         if (new_state != p->irq_b_state)
1224         {
1225                 p->irq_b_state = new_state;
1226                 devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
1227         }
1228 }
1229
1230 static void control_b_w(const device_config *device, UINT8 data)
1231 {
1232         pia6821_state *p = get_token(device);
1233         int temp;
1234
1235         /* bit 7 and 6 are read only */
1236         data &= 0x3f;
1237
1238         LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
1239
1240         /* update the control register */
1241         p->ctl_b = data;
1242
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);
1246         else
1247                 /* strobe mode - output is always high unless strobed */
1248                 temp = TRUE;
1249
1250         set_out_cb2(device, temp);
1251
1252         /* update externals */
1253         update_interrupts(device);
1254 }
1255
1256 static void control_a_w(const device_config *device, UINT8 data)
1257 {
1258         pia6821_state *p = get_token(device);
1259
1260         /* bit 7 and 6 are read only */
1261         data &= 0x3f;
1262
1263         LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
1264
1265         /* update the control register */
1266         p->ctl_a = data;
1267
1268         /* CA2 is configured as output */
1269         if (C2_OUTPUT(p->ctl_a))
1270         {
1271                 int temp;
1272
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);
1276                 else
1277                         /* strobe mode - output is always high unless strobed */
1278                         temp = TRUE;
1279
1280                 set_out_ca2(device, temp);
1281         }
1282
1283         /* update externals */
1284         update_interrupts(device);
1285 }
1286
1287
1288 CTRL REGISTER:
1289
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
1293
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
1299
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
1325
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]
1344
1345 #endif