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