]> Shamusworld >> Repos - stargem2/blob - src/stargem2.cpp
edad8fdf8c2697a5b55e5d79ed8a0c3404bfbcf7
[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 using namespace std;
37
38 #define SOUNDROM        "ROMs/sg.snd"
39 #define CMOS            "cmos.ram"
40 #define SAVESTATE       "sg2.state"
41
42 // Global variables
43
44 uint8 gram[0x10000], grom[0x10000], sram[0x10000], srom[0x10000]; // RAM & ROM spaces
45 V6809REGS mainCPU;
46 V6808REGS soundCPU;
47 uint8 color[16];
48 uint32 palette[256];
49
50 // Local variables
51
52 static bool running = true;                                             // Machine running state flag...
53 static uint32 startTicks;
54 static uint8 * keys;                                                    // SDL raw keyboard matrix
55
56 // Local timer callback functions
57
58 static void FrameCallback(void);
59 static void ScanlineCallback(void);
60
61
62 //
63 // 6809 memory functions
64 //
65
66 uint8 RdMem6809(uint16 addr)
67 {
68         uint8 b;
69
70         if (addr >= 0x9000 && addr <= 0xCFFF)           // No ROM between $9000 - $CFFF...
71                 b = gram[addr];
72         else
73         {
74                 if (!gram[0xC900] && addr <= 0x8FFF)    // Check RAM $C900 bank switch
75                         b = gram[addr];
76                 else
77                         b = grom[addr];
78         }
79
80 //Is $C80E COUNT240? Hmm... No.
81
82 //temp...
83 /*extern uint16 pcr;
84 //if (addr >= 0xC000 && addr <= 0xCBFF)
85 if (addr == 0x9C59)
86         WriteLog("RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, pcr);//*/
87 /*if (addr >= 0xC80D && addr <= 0xC80F)
88         WriteLog("V6809 RdMem: Reading address %04X [=%02X, PC=%04X]\n", addr, b, mainCPU.pc);//*/
89
90         return b;
91 }
92
93 void WrMem6809(uint16 addr, uint8 b)
94 {
95 //temp...
96 //extern V6809REGS regs;
97 //if (addr >= 0xC800 && addr <= 0xCBFE)
98 //if (addr == 0xC80F || addr == 0xC80D)
99 //      WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
100 //if (addr == 0xC80E)
101 /*if (addr >= 0xC800 && addr <= 0xC80F)
102         WriteLog("V6809 WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, mainCPU.pc, gram[0xCB00]);//*/
103
104         gram[addr] = b;
105
106         if (addr > 0x0006 && addr < 0x97F7)                     // 304 pixels  152-128=24-16=8
107         {
108                 // NOTE: Screen was 304 x 256, but we truncate the vertical dimension here...
109                 uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
110
111                 if (sy > 5 && sy < 246)
112                 {
113                         uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
114 //Hmm. This approach won't work with palette color cycling...
115 #if 0
116                         scrBuffer[saddr + 0] = b >> 4;
117                         scrBuffer[saddr + 1] = b & 0x0F;
118 #else
119                         scrBuffer[saddr + 0] = palette[color[b >> 4]];
120                         scrBuffer[saddr + 1] = palette[color[b & 0x0F]];
121 #endif
122                 }
123         }
124         else if (addr >= 0xC000 && addr <= 0xC00F)
125 //Let's see if we can fix the color cycling here... [DONE]
126 #if 0
127                 color[addr - 0xC000] = b;                                               // color[] from VIDEO.CPP (not any more!)
128 #else
129         {
130 // A better strategy here would probably be to set a flag when the color register changes,
131 // then change it before doing the render.
132 // ALSO: This approach doesn't take the color to the edges of the screen
133                 color[addr - 0xC000] = b;
134
135                 for(uint32 addr=0x0007; addr<0x97F7; addr++)
136                 {
137                         uint16 sx = (addr >> 7) & 0x01FE, sy = addr & 0x00FF;
138
139                         if (sy > 5 && sy < 246)
140                         {
141                                 uint32 saddr = 8 + sx + ((sy - 6) * 320);       // Calc screen address
142                                 uint8 sb = gram[addr];
143
144                                 scrBuffer[saddr + 0] = palette[color[sb >> 4]];
145                                 scrBuffer[saddr + 1] = palette[color[sb & 0x0F]];
146                         }
147                 }
148         }
149 #endif
150         else if (addr == 0xC80E)
151         {
152                 sram[0x0402] = b;                                                               // Connect PIAs in 6809 & 6808
153                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_IRQ;             // Start sound IRQ
154         }
155 }
156
157 //
158 // 6808 memory functions
159 //
160
161 uint8 RdMem6808(uint16 addr)
162 {
163         return (addr < 0xF000 ? sram[addr] : srom[addr]);
164 }
165
166 void WrMem6808(uint16 addr, uint8 b)
167 {
168         sram[addr] = b;
169
170         // A total guess, but let's try it...
171 //It probably depends on how the PIA is configured, so this is most likely wrong.
172 //      if (addr == 0x0401)
173 //              soundCPU.cpuFlags &= ~V6808_ASSERT_LINE_IRQ;
174 }
175
176 //
177 // Load a file into RAM/ROM image space
178 //
179 bool LoadImg(char * filename, uint8 * ram, int size)
180 {
181         FILE * fp = fopen(filename, "rb");
182
183         if (fp == NULL)
184                 return false;
185
186         fread(ram, 1, size, fp);
187         fclose(fp);
188
189         return true;
190 }
191
192 //
193 // Save CMOS ram
194 //
195 void SaveCMOS(void)
196 {
197         FILE * fp = fopen(CMOS, "wb");
198
199         if (fp != NULL)
200         {
201                 fwrite(gram + 0xCC00, 1, 1024, fp);
202                 fclose(fp);
203         }
204         else
205                 WriteLog("CMOS RAM not saved!\n");
206 }
207
208 //
209 // Load state save file
210 //
211 bool LoadMachineState(void)
212 {
213         FILE * fp = fopen(SAVESTATE, "rb");
214
215         if (fp == NULL)
216                 return false;
217
218         // This is kinda crappy--we don't do any sanity checking here!!!
219         fread(gram, 1, 0x10000, fp);
220         fread(sram, 1, 0x10000, fp);
221         fread(&mainCPU, 1, sizeof(V6809REGS), fp);
222         fread(&soundCPU, 1, sizeof(V6808REGS), fp);
223         fclose(fp);
224
225         for(int i=0x0006; i<0x97F8; i++)                                        // Set up backbuffer... ;-)
226                 WrMem6809(i, gram[i]);
227
228         mainCPU.RdMem = RdMem6809;                                                      // Make sure our function pointers are
229         mainCPU.WrMem = WrMem6809;                                                      // pointing to the right places!
230         soundCPU.RdMem = RdMem6808;
231         soundCPU.WrMem = WrMem6808;
232
233         return true;
234 }
235
236 //
237 // Save state save file
238 //
239 void SaveMachineState(void)
240 {
241         FILE * fp = fopen(SAVESTATE, "wb");
242
243         if (fp != NULL)
244         {
245                 fwrite(gram, 1, 0x10000, fp);
246                 fwrite(sram, 1, 0x10000, fp);
247                 fwrite(&mainCPU, 1, sizeof(V6809REGS), fp);
248                 fwrite(&soundCPU, 1, sizeof(V6808REGS), fp);
249                 fclose(fp);
250         }
251         else
252                 WriteLog("Machine state not saved!\n");
253 }
254
255 //
256 // Main loop
257 //
258 int main(int /*argc*/, char * /*argv*/[])
259 {
260         InitLog("stargem2.log");
261         WriteLog("StarGem2 - A portable Stargate emulator by James L. Hammons\n");
262
263         LoadSettings();
264
265         // Initialize Williams' palette (RGB coded as: 3 bits red, 3 bits green, 2 bits blue)
266         for(uint32 i=0; i<256; i++)
267                 palette[i] =
268 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
269                 (((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151) << 24)
270                         | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 16)
271                         | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 8) | 0xFF;
272 #else
273                 ((i & 0x01) * 33 + ((i & 0x02) >> 1) * 71 + ((i & 0x04) >> 2) * 151)
274                         | ((((i & 0x08) >> 3) * 33 + ((i & 0x10) >> 4) * 71 + ((i & 0x20) >> 5) * 151) << 8)
275                         | ((((i & 0x40) >> 6) * 71 + ((i & 0x80) >> 7) * 151) << 16) | 0xFF000000;
276 #endif
277
278         // Zero out memory
279         for(long i=0; i<0x10000; i++)
280                 gram[i] = grom[i] = sram[i] = srom[i] = 0;
281
282         // Set up V6809 & V6808 execution contexts
283         
284         memset(&mainCPU, sizeof(V6809REGS), 0);
285         mainCPU.RdMem = RdMem6809;
286         mainCPU.WrMem = WrMem6809;
287         mainCPU.cpuFlags |= V6809_ASSERT_LINE_RESET;
288
289         memset(&soundCPU, sizeof(V6808REGS), 0);
290         soundCPU.RdMem = RdMem6808;
291         soundCPU.WrMem = WrMem6808;
292         soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
293
294         char ROMs[12][8] = {
295                 "ROMs/01", "ROMs/02", "ROMs/03", "ROMs/04", "ROMs/05", "ROMs/06",
296                 "ROMs/07", "ROMs/08", "ROMs/09", "ROMs/10", "ROMs/11", "ROMs/12"
297                 };
298
299         for(int i=0; i<12; i++)
300         {
301                 uint32 baseAddress = i * 0x1000;
302
303                 if (i > 8)
304                         baseAddress += 0x4000;
305
306 #if 0
307 WriteLog("Loading ROM image '%s' at $%04X...\n", ROMs[i], baseAddress);
308 #endif
309 //              if (!LoadImg(ROMs[i], grom + (i * 0x1000), 0x1000))
310                 if (!LoadImg(ROMs[i], grom + baseAddress, 0x1000))
311                 {
312                         WriteLog("Could not open file '%s'!\n", ROMs[i]);
313                         return -1;
314                 }
315         }
316
317         if (!LoadImg(SOUNDROM, srom + 0xF800, 0x800))
318         {
319                 WriteLog("Could not open file '%s'!\n", SOUNDROM);
320                 return -1;
321         }
322
323         WriteLog("Stargate ROM images loaded...\n");
324         WriteLog("About to initialize video...\n");
325
326         if (!InitVideo())
327         {
328                 cout << "Aborting!" << endl;
329                 return -1;
330         }
331
332         // Have to do this *after* video init but *before* sound init...!
333         WriteLog("About to load machine state...");
334
335         if (!LoadMachineState())
336                 WriteLog("Machine state file not found!\n");
337         else
338                 WriteLog("done!\n");
339
340         if (!LoadImg(CMOS, gram + 0xCC00, 0x400))
341                 WriteLog("CMOS RAM not found!\n");
342
343         WriteLog("About to intialize audio...\n");
344         SoundInit();
345 //      uint8 * keys = SDL_GetKeyState(NULL);
346         keys = SDL_GetKeyState(NULL);
347
348 //      running = true;                                                         // Set running status...
349         srom[0xF800] = 0x37;                                            // Fix checksum so ST works...
350
351 #if 0
352
353 //kludge...
354 //This didn't work--it still acted like the old way (interrupt @ VC = 0)
355 //gram[0xCB00] = 64*3;
356
357         WriteLog("Entering main loop...\n");
358         while (running)
359         {
360                 SDL_PumpEvents();                                               // Force key events into the buffer.
361                 gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
362
363                 if (keys[SDLK_ESCAPE])
364                         running = false;                                        // ESC to exit...
365
366                 if (keys[SDLK_SEMICOLON])
367                         gram[0xC804] |= 0x01;                           // Fire (;)
368                 if (keys[SDLK_l])
369                         gram[0xC804] |= 0x02;                           // Thrust (L)
370                 if (keys[SDLK_SPACE])
371                         gram[0xC804] |= 0x04;                           // Smart Bomb (space)
372                 if (keys[SDLK_BACKSPACE])
373                         gram[0xC804] |= 0x08;                           // Hyperspace (BkSp)
374                 if (keys[SDLK_2])
375                         gram[0xC804] |= 0x10;                           // Two Player Start (2)
376                 if (keys[SDLK_1])
377                         gram[0xC804] |= 0x20;                           // One Player Start (1)
378                 if (keys[SDLK_RETURN])
379                         gram[0xC804] |= 0x40;                           // Reverse (Enter)
380                 if (keys[SDLK_f])
381                         gram[0xC804] |= 0x80;                           // Down (F)
382
383                 if (keys[SDLK_r])
384                         gram[0xC806] |= 0x01;                           // Up (R)
385                 if (keys[SDLK_a])
386                         gram[0xC806] |= 0x02;                           // Inviso (A)
387
388                 if (keys[SDLK_F1])
389                         gram[0xC80C] |= 0x01;                           // Auto up (F1)
390                 if (keys[SDLK_F2])
391                         gram[0xC80C] |= 0x02;                           // Advance (F2)
392                 if (keys[SDLK_5])
393                         gram[0xC80C] |= 0x04;                           // Right Coin (5)
394                 if (keys[SDLK_F3])
395                         gram[0xC80C] |= 0x08;                           // High Score Reset (F3)
396                 if (keys[SDLK_3])
397                         gram[0xC80C] |= 0x10;                           // Left Coin (3)
398                 if (keys[SDLK_4])
399                         gram[0xC80C] |= 0x20;                           // Center Coin (4)
400                 if (keys[SDLK_F4])
401                         gram[0xC80C] |= 0x40;                           // Slam Switch (F4)
402
403                 if (keys[SDLK_F5])                                              // Sound CPU self-test (F5)
404                         soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
405                 if (keys[SDLK_F6])                                              // Reset the 6808 (F6)
406                         soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
407
408 /*
409 $CB00 is scanline counter, bits 2-7 (1 frame/240 =69.44... usec)
410
411 Some places of interest to look at:
412
413 RdMem: Reading address C80E [=0C, PC=15C3]      <- Inside interrupt (read, then discarded)...
414 RdMem: Reading address CB00 [=43, PC=15C6]      <- interrupt
415 RdMem: Reading address C80C [=00, PC=0758]      <- input (?)
416 RdMem: Reading address C80C [=00, PC=07B9]      <- input (?)
417 RdMem: Reading address C806 [=00, PC=078C]      <- input
418 RdMem: Reading address C804 [=00, PC=2679]      <- input
419 */
420                 uint32 startTicks = SDL_GetTicks();
421 //              long video_clk = 0;
422 //              gram[0xCB00] = 0;
423
424 /*
425 //This is where the interrupt mask is restored in CC... Hmm...
426 //This enables interrupts *after* the previous interrupt has occurred... Hmm.
427 //Could $C80F (rom_pia_ctrlb) be the IRQ inhibit? Yes, it is!
428
429         // the IRQ signal comes into CB1, and is set to VA11
430         pia_1_cb1_w(0, scanline & 0x20);
431 ...
432         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
433         pia_1_ca1_w(0, 0);
434 ...
435         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
436         pia_1_ca1_w(0, 1);
437 */
438
439 //WriteLog("--> Start of frame...\n");
440                 for(int i=0; i<3; i++)
441                 {
442 //Not sure, but this *might* fix IRQ problem...
443 //Checking the PIA IRQ mask for an IRQ seems to work OK. Now if only the timing elsewhere was right...
444                         if (gram[0xC80F] & 0x01)
445                                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
446
447                         Execute6809(&mainCPU, 4000);
448                         mainCPU.clock -= 4000;                          // Remove 4K ticks from clock (in case it overflowed)
449 //Not sure, but this *might* fix IRQ problem...
450 //Checking the PIA IRQ mask for an IRQ seems to work OK. Now if only the timing elsewhere was right...
451 /*                      if (gram[0xC80F] & 0x01)
452                                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
453
454                         gram[0xCB00] += 64;                                     // Update video counter...
455                 }
456
457 //Hmm.
458 /*if (gram[0xC80E] & 0x01)
459         mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
460 //48 lines... (+ 16)
461 //gram[0xCB00] = 0;
462                 Execute6809(&mainCPU, 3000);
463                 mainCPU.clock -= 3000;                                  // Remove 3K ticks from clock (in case it overflowed)
464 //Not sure, but this *might* fix IRQ problem...
465 //if (gram[0xC80F] & 0x01)
466 //This isn't the right port on the PIA, but it does seem to make it through the demo now...
467 //Lesse if this works... Seems to!
468                 if (gram[0xC80D] & 0x01)                                // Do COUNT240 IRQ (if enabled!)
469                         mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
470
471 /*if (gram[0xC80F] & 0x01)
472                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
473 gram[0xCB00] = 0; //*/
474
475                 gram[0xCB00] += 48;                                             // Update video counter...
476
477                 Execute6809(&mainCPU, 1000);
478                 mainCPU.clock -= 1000;                                  // Remove 1K ticks from clock (in case it overflowed)
479 //Eh?
480 //Ok, this is the interrupt it's looking for, but still...
481 //if (gram[0xC80F] & 0x01)
482 //              mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;
483
484                 gram[0xCB00] += 16;                                             // Update video counter...
485
486 //              RenderScreenBuffer();
487                 RenderScreenBuffer2();  // 1 frame = 16667 cycles
488 //              WriteLog("Main: Rendered back buffer. [6809 PC=%04X]\n", pcr);
489
490                 Execute6809(&mainCPU, 667);                             // Do QnD VBLANK
491
492                 // 1/60 sec = ? ms (16.6 ms)
493                 while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
494
495 //kludge, temp...
496 //Very interesting! It's the palette rotation that's slowing it down!
497 //Fixed now, but this allows the color rotation while the wrong timing is in effect...
498 /*for(int i=0; i<16; i++)
499         WrMem(0xC000 + i, gram[0x9C26 + i]);//*/
500         }
501
502 /*uint16 pc = 0x15BA;
503 for(int i=0; i<200; i++)
504 //while (pc < 0x9000)
505         pc += Decode6809(pc);//*/
506
507 #else
508
509         running = true;                                                         // Set running status...
510
511         InitializeEventList();                                          // Clear the event list before we use it...
512         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
513 //      SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
514         SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
515 //      SetCallbackTime(ScanlineCallback, 520.83333334*32.00); // Set scanline callback at 1/32 of frame
516         startTicks = SDL_GetTicks();
517
518         WriteLog("Entering main loop...\n");
519
520         while (running)
521         {
522                 double timeToNextEvent = GetTimeToNextEvent();
523                 Execute6809(&mainCPU, USEC_TO_M6809_CYCLES(timeToNextEvent));
524 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
525 //(Fix so that this is not a requirement!)
526                 mainCPU.clock -= USEC_TO_M6809_CYCLES(timeToNextEvent);
527                 HandleNextEvent();
528         }
529
530 #ifdef __DEBUG__
531 WriteLog("\n");
532 WriteLog("$C900 = $%02X (0=RAM)\n", gram[0xC900]);
533 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);
534 WriteLog("\n");
535
536 /*uint16 pc = mainCPU.pc;//0x15BA;
537 for(int i=0; i<200; i++)
538 //while (pc < 0x9000)
539 {
540         pc += Decode6809(pc);
541         WriteLog("\n");
542 }//*/
543
544 /*uint32 pc = 0;
545 while (pc < 0xFFFF)
546 {
547         pc += Decode6809(pc);
548         WriteLog("\n");
549 }//*/
550 #endif
551
552 #endif
553
554         SoundDone();
555         VideoDone();
556         SaveCMOS();
557         SaveMachineState();
558         LogDone();
559
560         return 0;
561 }
562
563 static void FrameCallback(void)
564 {
565         SDL_PumpEvents();                                                       // Force key events into the buffer.
566         gram[0xC804] = gram[0xC806] = gram[0xC80C] = 0; // Reset PIA ports...
567
568         if (keys[SDLK_ESCAPE])
569                 running = false;                                                // ESC to exit...
570
571 //Convert this stuff to use the settings module... !!! FIX !!!
572         if (keys[SDLK_SEMICOLON])
573                 gram[0xC804] |= 0x01;                                   // Fire (;)
574         if (keys[SDLK_l])
575                 gram[0xC804] |= 0x02;                                   // Thrust (L)
576         if (keys[SDLK_SPACE])
577                 gram[0xC804] |= 0x04;                                   // Smart Bomb (space)
578         if (keys[SDLK_BACKSPACE])
579                 gram[0xC804] |= 0x08;                                   // Hyperspace (BkSp)
580         if (keys[SDLK_2])
581                 gram[0xC804] |= 0x10;                                   // Two Player Start (2)
582         if (keys[SDLK_1])
583                 gram[0xC804] |= 0x20;                                   // One Player Start (1)
584         if (keys[SDLK_RETURN])
585                 gram[0xC804] |= 0x40;                                   // Reverse (Enter)
586         if (keys[SDLK_f])
587                 gram[0xC804] |= 0x80;                                   // Down (F)
588
589         if (keys[SDLK_r])
590                 gram[0xC806] |= 0x01;                                   // Up (R)
591         if (keys[SDLK_a])
592                 gram[0xC806] |= 0x02;                                   // Inviso (A)
593
594         if (keys[SDLK_F1])
595                 gram[0xC80C] |= 0x01;                                   // Auto up (F1)
596         if (keys[SDLK_F2])
597                 gram[0xC80C] |= 0x02;                                   // Advance (F2)
598         if (keys[SDLK_5])
599                 gram[0xC80C] |= 0x04;                                   // Right Coin (5)
600         if (keys[SDLK_F3])
601                 gram[0xC80C] |= 0x08;                                   // High Score Reset (F3)
602         if (keys[SDLK_3])
603                 gram[0xC80C] |= 0x10;                                   // Left Coin (3)
604         if (keys[SDLK_4])
605                 gram[0xC80C] |= 0x20;                                   // Center Coin (4)
606         if (keys[SDLK_F4])
607                 gram[0xC80C] |= 0x40;                                   // Slam Switch (F4)
608
609         if (keys[SDLK_F5])                                                      // Sound CPU self-test (F5)
610                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_NMI;
611         if (keys[SDLK_F6])                                                      // Reset the 6808 (F6)
612                 soundCPU.cpuFlags |= V6808_ASSERT_LINE_RESET;
613
614         RenderScreenBuffer2();                                          // 1 frame = 1/60 sec ~ 16667 cycles
615         SetCallbackTime(FrameCallback, 16666.66666667);
616
617         while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
618         startTicks = SDL_GetTicks();
619 }
620
621 static void ScanlineCallback(void)
622 {
623 // CA1 of PIA 1 maps to $C80C-F... <-- Count240 is in PIA1...
624 // What about COUNT240???
625
626 //wil wok? Yes, but still screws up on the demo...
627 /*      if (gram[0xCB00] & 0x20)
628                 if (gram[0xC80F] & 0x01)
629                         mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
630         if ((gram[0xCB00] & 0x20) && (gram[0xC80F] & 0x01))
631                 mainCPU.cpuFlags |= V6809_ASSERT_LINE_IRQ;//*/
632
633 //Is $C80E COUNT240? Hmm... Doesn't seem to be. Bleh.
634 /*      if (gram[0xCB00] >= 240)
635                 gram[0xC80E] = 0xFF;
636         else
637                 gram[0xC80E] = 0x00;//*/
638 //      gram[0xC80E] = (gram[0xCB00] >= 240 ? 0xFF : 0x00);
639
640         gram[0xCB00] += 8;                                                      // Update video counter...
641
642         SetCallbackTime(ScanlineCallback, 520.83333334); // Set scanline callback at 1/32 of frame
643 }
644
645
646 /*
647 ; With correct timing, but no color cycling
648
649 --> Start of frame...
650 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
651 At $07AD. $6E: 00
652 At $0B66. $6E: 00
653 At $0CF4. $6E: 00
654 At $0CCB. $6E: 00
655 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
656 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
657 At $0718. $6E: 01
658 At $07AD. $6E: 01
659 At $0BB8. $6E: 01
660 At $0927. $6E: 01
661 At $0CF4. $6E: 01
662 At $0B66. $6E: 01
663 At $16C8. $6E: 01
664 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
665 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
666 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
667
668
669 ; With incorrect timing, but has color cycling
670
671 --> Start of frame...
672 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=00]
673 At $1609. $6E: 00                       ; Color cycling...
674 At $07AD. $6E: 00
675 At $0B66. $6E: 00
676 At $0CF4. $6E: 00
677 At $0CCB. $6E: 00
678 WrMem: Writing address C80F with 35 [PC=1644, $CB00=00]
679 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=40]
680 WrMem: Writing address C80F with 35 [PC=1644, $CB00=40]
681 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=80]
682 At $0718. $6E: 01
683 At $07AD. $6E: 01
684 At $0BB8. $6E: 01
685 At $0927. $6E: 01
686 At $0CF4. $6E: 01
687 At $0B66. $6E: 01
688 At $16C8. $6E: 01
689 WrMem: Writing address C80F with 35 [PC=1644, $CB00=80]
690 WrMem: Writing address C80F with 34 [PC=15C3, $CB00=C0]
691 WrMem: Writing address C80F with 35 [PC=1644, $CB00=C0]
692
693
694
695         Stargate
696         --------
697
698         0000-8FFF ROM   (for Blaster, 0000-3FFF is a bank of 12 ROMs)
699         0000-97FF Video  RAM Bank switched with ROM (96FF for Blaster)
700         9800-BFFF RAM
701                 0xBB00 Blaster only, Color 0 for each line (256 entry)
702                 0xBC00 Blaster only, Color 0 flags, latch color only if bit 0 = 1 (256 entry)
703                     Do something else with the bit 1, I do not know what
704         C000-CFFF I/O
705         D000-FFFF ROM
706
707         C000-C00F color_registers  (16 bytes of BBGGGRRR)
708
709         C804 widget_pia_dataa (widget = I/O board)
710         C805 widget_pia_ctrla
711         C806 widget_pia_datab
712         C807 widget_pia_ctrlb (CB2 select between player 1 and player 2
713                                controls if Table or Joust)
714               bits 5-3 = 110 = player 2
715               bits 5-3 = 111 = player 1
716
717         C80C rom_pia_dataa
718         C80D rom_pia_ctrla
719         C80E rom_pia_datab
720               bit 0 \
721               bit 1 |
722               bit 2 |-6 bits to sound board
723               bit 3 |
724               bit 4 |
725               bit 5 /
726               bit 6 \
727               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
728         C80F rom_pia_ctrlb
729
730         C900 rom_enable_scr_ctrl  Switch between video ram and rom at 0000-97FF
731
732         Stargate
733         --------
734         C804 widget_pia_dataa (widget = I/O board)
735           bit 0  Fire
736           bit 1  Thrust
737           bit 2  Smart Bomb
738           bit 3  HyperSpace
739           bit 4  2 Players
740           bit 5  1 Player
741           bit 6  Reverse
742           bit 7  Down
743
744         C806 widget_pia_datab
745           bit 0  Up
746           bit 1  Inviso
747           bit 2
748           bit 3
749           bit 4
750           bit 5
751           bit 6
752           bit 7  0 = Upright  1 = Table
753
754         C80C rom_pia_dataa
755           bit 0  Auto Up
756           bit 1  Advance
757           bit 2  Right Coin        (High Score Reset in schematics)
758           bit 3  High Score Reset  (Left Coin in schematics)
759           bit 4  Left Coin         (Center Coin in schematics)
760           bit 5  Center Coin       (Right Coin in schematics)
761           bit 6  Slam Door Tilt
762           bit 7  Hand Shake from sound board
763 */
764
765
766 /*
767
768 static MEMORY_READ_START( williams_readmem )
769         { 0x0000, 0x97ff, MRA_BANK1 },
770         { 0x9800, 0xbfff, MRA_RAM },
771         { 0xc804, 0xc807, pia_0_r },
772         { 0xc80c, 0xc80f, pia_1_r },
773         { 0xcb00, 0xcb00, williams_video_counter_r },
774         { 0xcc00, 0xcfff, MRA_RAM },
775         { 0xd000, 0xffff, MRA_ROM },
776 MEMORY_END
777
778
779 static MEMORY_WRITE_START( williams_writemem )
780         { 0x0000, 0x97ff, williams_videoram_w, &williams_bank_base, &videoram_size },
781         { 0x9800, 0xbfff, MWA_RAM },
782         { 0xc000, 0xc00f, paletteram_BBGGGRRR_w, &paletteram },
783         { 0xc804, 0xc807, pia_0_w },
784         { 0xc80c, 0xc80f, pia_1_w },
785         { 0xc900, 0xc900, williams_vram_select_w },
786         { 0xca00, 0xca07, williams_blitter_w, &williams_blitterram },
787         { 0xcbff, 0xcbff, watchdog_reset_w },
788         { 0xcc00, 0xcfff, MWA_RAM },
789         { 0xd000, 0xffff, MWA_ROM },
790 MEMORY_END
791
792 static MEMORY_READ_START( sound_readmem )
793         { 0x0000, 0x007f, MRA_RAM },
794         { 0x0400, 0x0403, pia_2_r },
795         { 0x8400, 0x8403, pia_2_r },    // used by Colony 7, perhaps others?
796         { 0xb000, 0xffff, MRA_ROM },    // most games start at $F000, Sinistar starts at $B000
797 MEMORY_END
798
799
800 static MEMORY_WRITE_START( sound_writemem )
801         { 0x0000, 0x007f, MWA_RAM },
802         { 0x0400, 0x0403, pia_2_w },
803         { 0x8400, 0x8403, pia_2_w },    // used by Colony 7, perhaps others?
804         { 0xb000, 0xffff, MWA_ROM },    // most games start at $F000, Sinistar starts at $B000
805 MEMORY_END
806
807 MACHINE_INIT( williams )
808 {
809         // reset the PIAs
810         pia_reset();
811
812         // reset the ticket dispenser (Lotto Fun)
813         ticket_dispenser_init(70, TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_HIGH);
814
815         // set a timer to go off every 16 scanlines, to toggle the VA11 line and update the screen
816         timer_set(cpu_getscanlinetime(0), 0, williams_va11_callback);
817
818         // also set a timer to go off on scanline 240
819         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
820 }
821
822
823 static void williams_va11_callback(int scanline)
824 {
825         // the IRQ signal comes into CB1, and is set to VA11
826         pia_1_cb1_w(0, scanline & 0x20);
827
828         // update the screen while we're here
829         force_partial_update(scanline - 1);
830
831         // set a timer for the next update
832         scanline += 8;
833         if (scanline >= 256) scanline = 0;
834         timer_set(cpu_getscanlinetime(scanline), scanline, williams_va11_callback);
835 }
836
837
838 static void williams_count240_off_callback(int param)
839 {
840         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
841         pia_1_ca1_w(0, 0);
842 }
843
844
845 static void williams_count240_callback(int param)
846 {
847         // the COUNT240 signal comes into CA1, and is set to the logical AND of VA10-VA13
848         pia_1_ca1_w(0, 1);
849
850         // set a timer to turn it off once the scanline counter resets
851         timer_set(cpu_getscanlinetime(0), 0, williams_count240_off_callback);
852
853         // set a timer for next frame
854         timer_set(cpu_getscanlinetime(240), 0, williams_count240_callback);
855 }
856
857
858 static void williams_main_irq(int state)
859 {
860         // IRQ to the main CPU
861         cpu_set_irq_line(0, M6809_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
862 }
863
864
865 static void williams_main_firq(int state)
866 {
867         // FIRQ to the main CPU
868         cpu_set_irq_line(0, M6809_FIRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
869 }
870
871
872 static void williams_snd_irq(int state)
873 {
874         // IRQ to the sound CPU
875         cpu_set_irq_line(1, M6800_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
876 }
877
878
879 READ_HANDLER( williams_video_counter_r )
880 {
881         return cpu_getscanline() & 0xFC;
882 }
883
884
885 // Special PIA 0 for Stargate, to handle the controls
886 struct pia6821_interface stargate_pia_0_intf =
887 {
888         //inputs : A/B,CA/B1,CA/B2 / stargate_input_port_0_r, input_port_1_r, 0, 0, 0, 0,
889         //outputs: A/B,CA/B2       / 0, 0, 0, 0,
890         //irqs   : A/B             / 0, 0
891 };
892
893 // Generic PIA 1, maps to input port 2, sound command out, and IRQs
894 struct pia6821_interface williams_pia_1_intf =
895 {
896         //inputs : A/B,CA/B1,CA/B2 / input_port_2_r, 0, 0, 0, 0, 0,
897         //outputs: A/B,CA/B2       / 0, williams_snd_cmd_w, 0, 0,
898         //irqs   : A/B             / williams_main_irq, williams_main_irq
899 };
900
901 // Generic PIA 2, maps to DAC data in and sound IRQs
902 struct pia6821_interface williams_snd_pia_intf =
903 {
904         //inputs : A/B,CA/B1,CA/B2 / 0, 0, 0, 0, 0, 0,
905         //outputs: A/B,CA/B2       / DAC_0_data_w, 0, 0, 0,
906         //irqs   : A/B             / williams_snd_irq, williams_snd_irq
907 };
908
909 static DRIVER_INIT( stargate )
910 {
911         // CMOS configuration
912         CONFIGURE_CMOS(0xCC00, 0x400);
913
914         // PIA configuration
915         CONFIGURE_PIAS(stargate_pia_0_intf, williams_pia_1_intf, williams_snd_pia_intf);
916 }
917
918
919 int cpu_getscanline(void)
920 {
921         return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
922 }
923
924  *************************************
925  *
926  *      Returns time until given scanline
927  *
928  *************************************
929
930 double cpu_getscanlinetime(int scanline)
931 {
932         double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
933         double abstime = timer_get_time();
934         double result;
935
936         // if we're already past the computed time, count it for the next frame
937         if (abstime >= scantime)
938                 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
939
940         // compute how long from now until that time
941         result = scantime - abstime;
942
943         // if it's small, just count a whole frame
944         if (result < TIME_IN_NSEC(1))
945                 result = TIME_IN_HZ(Machine->drv->frames_per_second);
946         return result;
947 }
948
949  *************************************
950  *
951  *      Returns time for one scanline
952  *
953  *************************************
954
955 double cpu_getscanlineperiod(void)
956 {
957         return scanline_period;
958 }
959
960
961 V6809 WrMem: Writing address C80D with 00 [PC=0000, $CB00=00]
962 V6809 WrMem: Writing address C80C with 00 [PC=0000, $CB00=00]
963 V6809 WrMem: Writing address C80D with 3C [PC=0000, $CB00=00]
964
965 V6809 WrMem: Writing address C80F with 00 [PC=0000, $CB00=00]
966 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
967 V6809 WrMem: Writing address C80F with 3C [PC=0000, $CB00=00]
968
969 V6809 WrMem: Writing address C80E with C0 [PC=0000, $CB00=00]
970 V6809 WrMem: Writing address C80D with 34 [PC=FE61, $CB00=48]
971 V6809 WrMem: Writing address C80F with 34 [PC=FE61, $CB00=48]
972 V6809 WrMem: Writing address C80E with 00 [PC=FE61, $CB00=48]
973
974 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
975 V6809 WrMem: Writing address C80D with 00 [PC=FD92, $CB00=C8]
976 V6809 WrMem: Writing address C80C with 00 [PC=FD92, $CB00=C8]
977 V6809 WrMem: Writing address C80D with 34 [PC=FD92, $CB00=C8]
978
979 V6809 WrMem: Writing address C80E with 00 [PC=FD92, $CB00=C8]
980 V6809 WrMem: Writing address C80F with 00 [PC=FD92, $CB00=C8]
981 V6809 WrMem: Writing address C80E with FF [PC=FD92, $CB00=C8]
982 V6809 WrMem: Writing address C80F with 35 [PC=FD92, $CB00=C8]
983
984 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
985 V6809 WrMem: Writing address C805 with 00 [PC=607B, $CB00=D0]
986 V6809 WrMem: Writing address C804 with 00 [PC=607B, $CB00=D0]
987 V6809 WrMem: Writing address C805 with 34 [PC=607B, $CB00=D0]
988
989 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
990 V6809 WrMem: Writing address C807 with 00 [PC=607B, $CB00=D0]
991 V6809 WrMem: Writing address C806 with 00 [PC=607B, $CB00=D0]
992 V6809 WrMem: Writing address C807 with 3E [PC=607B, $CB00=D0]
993
994 V6809 WrMem: Writing address C80E with 3F [PC=13CB, $CB00=A8]
995 V6809 WrMem: Writing address C807 with 3C [PC=60B4, $CB00=90]
996 V6809 WrMem: Writing address C80E with 0C [PC=014D, $CB00=80]
997
998 V6809 WrMem: Writing address C80F with 34 [PC=014D, $CB00=80]
999 V6809 WrMem: Writing address C80F with 35 [PC=014D, $CB00=80]
1000 V6809 WrMem: Writing address C80F with 34 [PC=0013, $CB00=A8]
1001 V6809 WrMem: Writing address C80F with 35 [PC=0013, $CB00=A8]
1002
1003         C80C rom_pia_dataa
1004         C80D rom_pia_ctrla
1005         C80E rom_pia_datab
1006               bit 0 \
1007               bit 1 |
1008               bit 2 |-6 bits to sound board
1009               bit 3 |
1010               bit 4 |
1011               bit 5 /
1012               bit 6 \
1013               bit 7 /Plus CA2 and CB2 = 4 bits to drive the LED 7 segment
1014         C80F rom_pia_ctrlb
1015
1016 CTRLA = IRQA1 (1 bit) IRQA2 (1 bit) CA2 (3 bits) DDR (1 bit) CA1 (2 bits)
1017
1018
1019 PIA initialization:
1020
1021 00 -> $C80D = PIA2     -> DDR active
1022 00 -> $C80C = PIA2 DDR -> All input?
1023
1024
1025
1026 */
1027