1 // Emulator.cpp
2 
3 #include "stdafx.h"
4 #include "main.h"
5 #include "mainwindow.h"
6 #include "Emulator.h"
7 #include "emubase/Emubase.h"
8 #include "qsoundout.h"
9 #include <QTime>
10 #include <QFile>
11 
12 
13 //////////////////////////////////////////////////////////////////////
14 
15 
16 CMotherboard* g_pBoard = nullptr;
17 QSoundOut * g_sound = nullptr;
18 
19 bool g_okEmulatorInitialized = false;
20 bool g_okEmulatorRunning = false;
21 
22 int m_wEmulatorCPUBpsCount = 0;
23 int m_wEmulatorPPUBpsCount = 0;
24 quint16 m_EmulatorCPUBps[MAX_BREAKPOINTCOUNT + 1];
25 quint16 m_EmulatorPPUBps[MAX_BREAKPOINTCOUNT + 1];
26 quint16 m_wEmulatorTempCPUBreakpoint = 0177777;
27 quint16 m_wEmulatorTempPPUBreakpoint = 0177777;
28 
29 bool m_okEmulatorSound = false;
30 
31 long m_nFrameCount = 0;
32 QTime m_emulatorTime;
33 int m_nTickCount = 0;
34 quint32 m_dwEmulatorUptime = 0;  // UKNC uptime, seconds, from turn on or reset, increments every 25 frames
35 long m_nUptimeFrameCount = 0;
36 
37 quint8* g_pEmulatorRam[3];  // RAM values - for change tracking
38 quint8* g_pEmulatorChangedRam[3];  // RAM change flags
39 quint16 g_wEmulatorCpuPC = 0177777;      // Current PC value
40 quint16 g_wEmulatorPrevCpuPC = 0177777;  // Previous PC value
41 quint16 g_wEmulatorPpuPC = 0177777;      // Current PC value
42 quint16 g_wEmulatorPrevPpuPC = 0177777;  // Previous PC value
43 
44 const int KEYEVENT_QUEUE_SIZE = 32;
45 quint16 m_EmulatorKeyQueue[KEYEVENT_QUEUE_SIZE];
46 int m_EmulatorKeyQueueTop = 0;
47 int m_EmulatorKeyQueueBottom = 0;
48 int m_EmulatorKeyQueueCount = 0;
49 
50 
51 void CALLBACK Emulator_FeedDAC(unsigned short l, unsigned short r);
52 
53 
54 //////////////////////////////////////////////////////////////////////
55 
Emulator_Init()56 bool Emulator_Init()
57 {
58     ASSERT(g_pBoard == nullptr);
59 
60     ::memset(g_pEmulatorRam, 0, sizeof(g_pEmulatorRam));
61     ::memset(g_pEmulatorChangedRam, 0, sizeof(g_pEmulatorChangedRam));
62     CProcessor::Init();
63 
64     m_wEmulatorCPUBpsCount = m_wEmulatorPPUBpsCount = 0;
65     for (int i = 0; i <= MAX_BREAKPOINTCOUNT; i++)
66     {
67         m_EmulatorCPUBps[i] = 0177777;
68         m_EmulatorPPUBps[i] = 0177777;
69     }
70 
71     g_pBoard = new CMotherboard();
72 
73     quint8 buffer[32768];
74 
75     // Load ROM file
76     memset(buffer, 0, 32768);
77     QFile romFile(":/uknc_rom.bin");
78     if (!romFile.open(QIODevice::ReadOnly))
79     {
80         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to load ROM file."));
81         return false;
82     }
83     qint64 bytesRead = romFile.read((char*)buffer, 32256);
84     romFile.close();
85     if (bytesRead != 32256)
86     {
87         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to load ROM file."));
88         return false;
89     }
90 
91     g_pBoard->LoadROM(buffer);
92 
93     //g_pBoard->SetNetStation((uint16_t)Settings_GetNetStation());
94 
95     g_pBoard->Reset();
96 
97     g_sound = new QSoundOut();
98     if (m_okEmulatorSound)
99     {
100         g_pBoard->SetSoundGenCallback(Emulator_FeedDAC);
101     }
102 
103     m_nUptimeFrameCount = 0;
104     m_dwEmulatorUptime = 0;
105 
106     // Allocate memory for old RAM values
107     for (int i = 0; i < 3; i++)
108     {
109         g_pEmulatorRam[i] = (quint8*) ::calloc(65536, 1);
110         g_pEmulatorChangedRam[i] = (quint8*) ::calloc(65536, 1);
111     }
112 
113     g_okEmulatorInitialized = true;
114     return true;
115 }
116 
Emulator_Done()117 void Emulator_Done()
118 {
119     ASSERT(g_pBoard != nullptr);
120 
121     CProcessor::Done();
122 
123     g_pBoard->SetSoundGenCallback(nullptr);
124     if (g_sound)
125     {
126         delete g_sound;
127         g_sound = nullptr;
128     }
129 
130     delete g_pBoard;
131     g_pBoard = nullptr;
132 
133     // Free memory used for old RAM values
134     for (int i = 0; i < 3; i++)
135     {
136         ::free(g_pEmulatorRam[i]);
137         ::free(g_pEmulatorChangedRam[i]);
138     }
139 
140     g_okEmulatorInitialized = false;
141 }
142 
Emulator_Start()143 void Emulator_Start()
144 {
145     g_okEmulatorRunning = true;
146 
147     // Set title bar text
148     Global_getMainWindow()->updateWindowText();
149 
150     m_nFrameCount = 0;
151     m_emulatorTime.restart();
152     m_nTickCount = 0;
153 
154     // For proper breakpoint processing
155     if (m_wEmulatorCPUBpsCount != 0 || m_wEmulatorPPUBpsCount)
156     {
157         g_pBoard->GetCPU()->ClearInternalTick();
158         g_pBoard->GetPPU()->ClearInternalTick();
159     }
160 }
Emulator_Stop()161 void Emulator_Stop()
162 {
163     g_okEmulatorRunning = false;
164 
165     Emulator_SetTempCPUBreakpoint(0177777);
166     Emulator_SetTempPPUBreakpoint(0177777);
167 
168     // Set title bar text
169     Global_getMainWindow()->updateWindowText();
170 
171     // Reset FPS indicator
172     Global_showFps(-1.0);
173 
174     Global_UpdateAllViews();
175 }
176 
Emulator_Reset()177 void Emulator_Reset()
178 {
179     ASSERT(g_pBoard != nullptr);
180 
181     g_pBoard->Reset();
182 
183     m_nUptimeFrameCount = 0;
184     m_dwEmulatorUptime = 0;
185     Global_showUptime(0);
186 
187     Global_UpdateAllViews();
188 }
189 
Emulator_AddCPUBreakpoint(quint16 address)190 bool Emulator_AddCPUBreakpoint(quint16 address)
191 {
192     if (m_wEmulatorCPUBpsCount == MAX_BREAKPOINTCOUNT - 1 || address == 0177777)
193         return false;
194     for (int i = 0; i < m_wEmulatorCPUBpsCount; i++)  // Check if the BP exists
195     {
196         if (m_EmulatorCPUBps[i] == address)
197             return false;  // Already in the list
198     }
199     for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)  // Put in the first empty cell
200     {
201         if (m_EmulatorCPUBps[i] == 0177777)
202         {
203             m_EmulatorCPUBps[i] = address;
204             break;
205         }
206     }
207     m_wEmulatorCPUBpsCount++;
208     return true;
209 }
Emulator_AddPPUBreakpoint(quint16 address)210 bool Emulator_AddPPUBreakpoint(quint16 address)
211 {
212     if (m_wEmulatorPPUBpsCount == MAX_BREAKPOINTCOUNT - 1 || address == 0177777)
213         return false;
214     for (int i = 0; i < m_wEmulatorPPUBpsCount; i++)  // Check if the BP exists
215     {
216         if (m_EmulatorPPUBps[i] == address)
217             return false;  // Already in the list
218     }
219     for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)  // Put in the first empty cell
220     {
221         if (m_EmulatorPPUBps[i] == 0177777)
222         {
223             m_EmulatorPPUBps[i] = address;
224             break;
225         }
226     }
227     m_wEmulatorPPUBpsCount++;
228     return true;
229 }
Emulator_RemoveCPUBreakpoint(quint16 address)230 bool Emulator_RemoveCPUBreakpoint(quint16 address)
231 {
232     if (m_wEmulatorCPUBpsCount == 0 || address == 0177777)
233         return false;
234     for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)
235     {
236         if (m_EmulatorCPUBps[i] == address)
237         {
238             m_EmulatorCPUBps[i] = 0177777;
239             m_wEmulatorCPUBpsCount--;
240             if (m_wEmulatorCPUBpsCount > i)  // fill the hole
241             {
242                 m_EmulatorCPUBps[i] = m_EmulatorCPUBps[m_wEmulatorCPUBpsCount];
243                 m_EmulatorCPUBps[m_wEmulatorCPUBpsCount] = 0177777;
244             }
245             return true;
246         }
247     }
248     return false;
249 }
Emulator_RemovePPUBreakpoint(quint16 address)250 bool Emulator_RemovePPUBreakpoint(quint16 address)
251 {
252     if (m_wEmulatorPPUBpsCount == 0 || address == 0177777)
253         return false;
254     for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)
255     {
256         if (m_EmulatorPPUBps[i] == address)
257         {
258             m_EmulatorPPUBps[i] = 0177777;
259             m_wEmulatorPPUBpsCount--;
260             if (m_wEmulatorPPUBpsCount > i)  // fill the hole
261             {
262                 m_EmulatorPPUBps[i] = m_EmulatorPPUBps[m_wEmulatorPPUBpsCount];
263                 m_EmulatorPPUBps[m_wEmulatorPPUBpsCount] = 0177777;
264             }
265             return true;
266         }
267     }
268     return false;
269 }
Emulator_SetTempCPUBreakpoint(quint16 address)270 void Emulator_SetTempCPUBreakpoint(quint16 address)
271 {
272     if (m_wEmulatorTempCPUBreakpoint != 0177777)
273         Emulator_RemoveCPUBreakpoint(m_wEmulatorTempCPUBreakpoint);
274     if (address == 0177777)
275     {
276         m_wEmulatorTempCPUBreakpoint = 0177777;
277         return;
278     }
279     for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)
280     {
281         if (m_EmulatorCPUBps[i] == address)
282             return;  // We have regular breakpoint with the same address
283     }
284     m_wEmulatorTempCPUBreakpoint = address;
285     m_EmulatorCPUBps[m_wEmulatorCPUBpsCount] = address;
286     m_wEmulatorCPUBpsCount++;
287 }
Emulator_SetTempPPUBreakpoint(quint16 address)288 void Emulator_SetTempPPUBreakpoint(quint16 address)
289 {
290     if (m_wEmulatorTempPPUBreakpoint != 0177777)
291         Emulator_RemovePPUBreakpoint(m_wEmulatorTempPPUBreakpoint);
292     if (address == 0177777)
293     {
294         m_wEmulatorTempPPUBreakpoint = 0177777;
295         return;
296     }
297     for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)
298     {
299         if (m_EmulatorPPUBps[i] == address)
300             return;  // We have regular breakpoint with the same address
301     }
302     m_wEmulatorTempPPUBreakpoint = address;
303     m_EmulatorPPUBps[m_wEmulatorPPUBpsCount] = address;
304     m_wEmulatorPPUBpsCount++;
305 }
Emulator_GetCPUBreakpointList()306 const quint16* Emulator_GetCPUBreakpointList() { return m_EmulatorCPUBps; }
Emulator_GetPPUBreakpointList()307 const quint16* Emulator_GetPPUBreakpointList() { return m_EmulatorPPUBps; }
Emulator_IsBreakpoint()308 bool Emulator_IsBreakpoint()
309 {
310     quint16 address = g_pBoard->GetCPU()->GetPC();
311     if (m_wEmulatorCPUBpsCount > 0)
312     {
313         for (int i = 0; i < m_wEmulatorCPUBpsCount; i++)
314         {
315             if (address == m_EmulatorCPUBps[i])
316                 return true;
317         }
318     }
319     address = g_pBoard->GetPPU()->GetPC();
320     if (m_wEmulatorPPUBpsCount > 0)
321     {
322         for (int i = 0; i < m_wEmulatorPPUBpsCount; i++)
323         {
324             if (address == m_EmulatorPPUBps[i])
325                 return true;
326         }
327     }
328     return false;
329 }
Emulator_IsBreakpoint(bool okCpuPpu,quint16 address)330 bool Emulator_IsBreakpoint(bool okCpuPpu, quint16 address)
331 {
332     int bpsCount = okCpuPpu ? m_wEmulatorCPUBpsCount : m_wEmulatorPPUBpsCount;
333     quint16* pbps = okCpuPpu ? m_EmulatorCPUBps : m_EmulatorPPUBps;
334     if (bpsCount == 0)
335         return false;
336     for (int i = 0; i < bpsCount; i++)
337     {
338         if (address == pbps[i])
339             return true;
340     }
341     return false;
342 }
Emulator_RemoveAllBreakpoints(bool okCpuPpu)343 void Emulator_RemoveAllBreakpoints(bool okCpuPpu)
344 {
345     quint16* pbps = okCpuPpu ? m_EmulatorCPUBps : m_EmulatorPPUBps;
346     for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++)
347         pbps[i] = 0177777;
348     if (okCpuPpu)
349         m_wEmulatorCPUBpsCount = 0;
350     else
351         m_wEmulatorPPUBpsCount = 0;
352 }
353 
Emulator_SystemFrame()354 bool Emulator_SystemFrame()
355 {
356     Emulator_ProcessKeyEvent();
357 
358     g_pBoard->SetCPUBreakpoints(m_wEmulatorCPUBpsCount > 0 ? m_EmulatorCPUBps : nullptr);
359     g_pBoard->SetPPUBreakpoints(m_wEmulatorPPUBpsCount > 0 ? m_EmulatorPPUBps : nullptr);
360 
361     if (!g_pBoard->SystemFrame())  // Breakpoint hit
362     {
363         Emulator_SetTempCPUBreakpoint(0177777);
364         Emulator_SetTempPPUBreakpoint(0177777);
365         return false;
366     }
367 
368     // Calculate frames per second
369     m_nFrameCount++;
370     int nCurrentTicks = m_emulatorTime.elapsed();
371     long nTicksElapsed = nCurrentTicks - m_nTickCount;
372     if (nTicksElapsed >= 1200)
373     {
374         double dFramesPerSecond = m_nFrameCount * 1000.0 / nTicksElapsed;
375         Global_showFps(dFramesPerSecond);
376 
377         m_nFrameCount = 0;
378         m_nTickCount = nCurrentTicks;
379     }
380 
381     // Calculate emulator uptime (25 frames per second)
382     m_nUptimeFrameCount++;
383     if (m_nUptimeFrameCount >= 25)
384     {
385         m_dwEmulatorUptime++;
386         m_nUptimeFrameCount = 0;
387 
388         Global_showUptime(m_dwEmulatorUptime);
389     }
390 
391     return true;
392 }
393 
Emulator_GetUptime()394 float Emulator_GetUptime()
395 {
396     return (float)m_dwEmulatorUptime + float(m_nUptimeFrameCount) / 25.0f;
397 }
398 
399 // Update cached values after Run or Step
Emulator_OnUpdate()400 void Emulator_OnUpdate()
401 {
402     // Update stored PC value
403     g_wEmulatorPrevCpuPC = g_wEmulatorCpuPC;
404     g_wEmulatorCpuPC = g_pBoard->GetCPU()->GetPC();
405     g_wEmulatorPrevPpuPC = g_wEmulatorPpuPC;
406     g_wEmulatorPpuPC = g_pBoard->GetPPU()->GetPC();
407 
408     // Update memory change flags
409     for (int plane = 0; plane < 3; plane++)
410     {
411         quint8* pOld = g_pEmulatorRam[plane];
412         quint8* pChanged = g_pEmulatorChangedRam[plane];
413         quint16 addr = 0;
414         do
415         {
416             quint8 newvalue = g_pBoard->GetRAMByte(plane, addr);
417             quint8 oldvalue = *pOld;
418             *pChanged = (newvalue != oldvalue) ? 255 : 0;
419             *pOld = newvalue;
420             addr++;
421             pOld++;  pChanged++;
422         }
423         while (addr < 65535);
424     }
425 }
426 
427 // Get RAM change flag for RAM word
428 //   addrtype - address mode - see ADDRTYPE_XXX constants
Emulator_GetChangeRamStatus(int addrtype,quint16 address)429 quint16 Emulator_GetChangeRamStatus(int addrtype, quint16 address)
430 {
431     switch (addrtype)
432     {
433     case ADDRTYPE_RAM0:
434     case ADDRTYPE_RAM1:
435     case ADDRTYPE_RAM2:
436         return *((quint16*)(g_pEmulatorChangedRam[addrtype] + address));
437     case ADDRTYPE_RAM12:
438         if (address < 0170000)
439             return MAKEWORD(
440                     *(g_pEmulatorChangedRam[1] + address / 2),
441                     *(g_pEmulatorChangedRam[2] + address / 2));
442         else
443             return 0;
444     default:
445         return 0;
446     }
447 }
448 
Emulator_LoadROMCartridge(int slot,LPCTSTR sFilePath)449 bool Emulator_LoadROMCartridge(int slot, LPCTSTR sFilePath)
450 {
451     // Open file
452     FILE* fpFile = ::fopen(sFilePath, "rb");
453     if (fpFile == nullptr)
454     {
455         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to load ROM cartridge image."));
456         return false;
457     }
458 
459     // Allocate memory
460     quint8* pImage = (quint8*) ::calloc(24 * 1024, 1);
461     if (pImage == nullptr)
462     {
463         ::fclose(fpFile);
464         return false;
465     }
466     size_t dwBytesRead = ::fread(pImage, 1, 24 * 1024, fpFile);
467     if (dwBytesRead != 24 * 1024)
468     {
469         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to load ROM cartridge image."));
470         return false;
471     }
472 
473     g_pBoard->LoadROMCartridge(slot, pImage);
474 
475     // Free memory, close file
476     ::free(pImage);
477     ::fclose(fpFile);
478 
479     //TODO: Save the file name for a future SaveImage() call
480 
481     return true;
482 }
483 
Emulator_PrepareScreenRGB32(void * pImageBits,const quint32 * colors)484 void Emulator_PrepareScreenRGB32(void* pImageBits, const quint32* colors)
485 {
486     if (pImageBits == nullptr) return;
487     if (!g_okEmulatorInitialized) return;
488 
489     // Tag parsing loop
490     quint8 cursorYRGB = 0;
491     bool okCursorType = false;
492     quint8 cursorPos = 128;
493     bool cursorOn = false;
494     quint8 cursorAddress = 0;  // Address of graphical cursor
495     quint16 address = 0000270;  // Tag sequence start address
496     bool okTagSize = false;  // Tag size: true - 4-word, false - 2-word (first tag is always 2-word)
497     bool okTagType = false;  // Type of 4-word tag: true - set palette, false - set params
498     int scale = 1;           // Horizontal scale: 1, 2, 4, or 8
499     quint32 palette = 0;       // Palette
500     qint32 palettecurrent[8];  memset(palettecurrent, 0, sizeof(palettecurrent)); // Current palette; update each time we change the "palette" variable
501     quint8 pbpgpr = 0;         // 3-bit Y-value modifier
502     for (int yy = 0; yy < 307; yy++)
503     {
504         if (okTagSize)  // 4-word tag
505         {
506             quint16 tag1 = g_pBoard->GetRAMWord(0, address);
507             address += 2;
508             quint16 tag2 = g_pBoard->GetRAMWord(0, address);
509             address += 2;
510 
511             if (okTagType)  // 4-word palette tag
512             {
513                 palette = ((quint32)tag1) | ((quint32)tag2 << 16);
514             }
515             else  // 4-word params tag
516             {
517                 scale = (tag2 >> 4) & 3;  // Bits 4-5 - new scale value
518                 pbpgpr = (quint8)((7 - (tag2 & 7)) << 4);  // Y-value modifier
519                 cursorYRGB = (quint8)(tag1 & 15);  // Cursor color
520                 okCursorType = ((tag1 & 16) != 0);  // true - graphical cursor, false - symbolic cursor
521                 //ASSERT(okCursorType==0);  //DEBUG
522                 cursorPos = (quint8)(((tag1 >> 8) >> scale) & 0x7f);  // Cursor position in the line
523                 cursorAddress = (quint8)((tag1 >> 5) & 7);
524                 scale = 1 << scale;
525             }
526             for (uint8_t c = 0; c < 8; c++)  // Update palettecurrent
527             {
528                 quint8 valueYRGB = (uint8_t) (palette >> (c << 2)) & 15;
529                 palettecurrent[c] = colors[pbpgpr | valueYRGB];
530                 //if (pbpgpr != 0) DebugLogFormat("pbpgpr %02x\r\n", pbpgpr | valueYRGB);
531             }
532         }
533 
534         quint16 addressBits = g_pBoard->GetRAMWord(0, address);  // The word before the last word - is address of bits from all three memory planes
535         address += 2;
536 
537         // Calculate size, type and address of the next tag
538         quint16 tagB = g_pBoard->GetRAMWord(0, address);  // Last word of the tag - is address and type of the next tag
539         okTagSize = (tagB & 2) != 0;  // Bit 1 shows size of the next tag
540         if (okTagSize)
541         {
542             address = tagB & ~7;
543             okTagType = (tagB & 4) != 0;  // Bit 2 shows type of the next tag
544         }
545         else
546             address = tagB & ~3;
547         if ((tagB & 1) != 0)
548             cursorOn = !cursorOn;
549 
550         // Draw bits into the bitmap, from line 20 to line 307
551         if (yy < 19 /*|| yy > 306*/)
552             continue;
553 
554         // Loop thru bits from addressBits, planes 0,1,2
555         // For each pixel:
556         //   Get bit from planes 0,1,2 and make value
557         //   Map value to palette; result is 4-bit value YRGB
558         //   Translate value to 24-bit RGB
559         //   Put value to m_bits; repeat using scale value
560 
561         int xr = 640;
562         int y = yy - 19;
563         quint32* pBits = (static_cast<quint32*>(pImageBits)) + y * 640;
564         int pos = 0;
565         for (;;)
566         {
567             // Get bit from planes 0,1,2
568             quint8 src0 = g_pBoard->GetRAMByte(0, addressBits);
569             quint8 src1 = g_pBoard->GetRAMByte(1, addressBits);
570             quint8 src2 = g_pBoard->GetRAMByte(2, addressBits);
571             // Loop through the bits of the byte
572             int bit = 0;
573             for (;;)
574             {
575                 quint32 valueRGB;
576                 if (cursorOn && (pos == cursorPos) && (!okCursorType || (okCursorType && bit == cursorAddress)))
577                     valueRGB = colors[cursorYRGB];  // 4-bit to 32-bit color
578                 else
579                 {
580                     // Make 3-bit value from the bits
581                     quint8 value012 = (src0 & 1) | ((src1 & 1) << 1) | ((src2 & 1) << 2);
582                     valueRGB = palettecurrent[value012];  // 3-bit to 32-bit color
583                 }
584 
585                 // Put value to m_bits; repeat using scale value
586                 //WAS: for (int s = 0; s < scale; s++) *pBits++ = valueRGB;
587                 switch (scale)
588                 {
589                 case 8:
590                     *pBits++ = valueRGB;
591                     *pBits++ = valueRGB;
592                     *pBits++ = valueRGB;
593                     *pBits++ = valueRGB;
594                 case 4:
595                     *pBits++ = valueRGB;
596                     *pBits++ = valueRGB;
597                 case 2:
598                     *pBits++ = valueRGB;
599                 case 1:
600                     *pBits++ = valueRGB;
601                 default:
602                     break;
603                 }
604 
605                 xr -= scale;
606 
607                 if (bit == 7)
608                     break;
609                 bit++;
610 
611                 // Shift to the next bit
612                 src0 >>= 1;
613                 src1 >>= 1;
614                 src2 >>= 1;
615             }
616             if (xr <= 0)
617                 break;  // End of line
618             addressBits++;  // Go to the next byte
619             pos++;
620         }
621     }
622 }
623 
Emulator_PrepareScreenToText(void * pImageBits,const quint32 * colors)624 void Emulator_PrepareScreenToText(void* pImageBits, const quint32* colors)
625 {
626     if (pImageBits == nullptr) return;
627     if (!g_okEmulatorInitialized) return;
628 
629     // Tag parsing loop
630     quint16 address = 0000270;  // Tag sequence start address
631     bool okTagSize = false;  // Tag size: TRUE - 4-word, false - 2-word (first tag is always 2-word)
632     bool okTagType = false;  // Type of 4-word tag: TRUE - set palette, false - set params
633     int scale = 1;           // Horizontal scale: 1, 2, 4, or 8
634     for (int yy = 0; yy < 307; yy++)
635     {
636         if (okTagSize)  // 4-word tag
637         {
638             //WORD tag1 = g_pBoard->GetRAMWord(0, address);
639             address += 2;
640             quint16 tag2 = g_pBoard->GetRAMWord(0, address);
641             address += 2;
642 
643             if (okTagType)  // 4-word palette tag
644             {
645                 //palette = MAKELONG(tag1, tag2);
646             }
647             else  // 4-word params tag
648             {
649                 scale = (tag2 >> 4) & 3;  // Bits 4-5 - new scale value
650                 scale = 1 << scale;
651             }
652         }
653 
654         quint16 addressBits = g_pBoard->GetRAMWord(0, address);  // The word before the last word - is address of bits from all three memory planes
655         address += 2;
656 
657         // Calculate size, type and address of the next tag
658         quint16 tagB = g_pBoard->GetRAMWord(0, address);  // Last word of the tag - is address and type of the next tag
659         okTagSize = (tagB & 2) != 0;  // Bit 1 shows size of the next tag
660         if (okTagSize)
661         {
662             address = tagB & ~7;
663             okTagType = (tagB & 4) != 0;  // Bit 2 shows type of the next tag
664         }
665         else
666             address = tagB & ~3;
667 
668         // Draw bits into the bitmap, from line 20 to line 307
669         if (yy < 19 /*|| yy > 306*/)
670             continue;
671 
672         // Loop thru bits from addressBits, planes 0,1,2
673         int xr = 640;
674         int y = yy - 19;
675         quint32* pBits = (static_cast<quint32*>(pImageBits)) + y * 640;
676         int pos = 0;
677         for (;;)
678         {
679             // Get bit from planes 0,1,2
680             quint8 src0 = g_pBoard->GetRAMByte(0, addressBits);
681             quint8 src1 = g_pBoard->GetRAMByte(1, addressBits);
682             quint8 src2 = g_pBoard->GetRAMByte(2, addressBits);
683             // Loop through the bits of the byte
684             int bit = 0;
685             for (;;)
686             {
687                 // Make 3-bit value from the bits
688                 quint8 value012 = (src0 & 1) | ((src1 & 1) << 1) | ((src2 & 1) << 2);
689                 quint32 valueRGB = colors[value012];  // 3-bit to 32-bit color
690 
691                 // Put value to m_bits; (do not repeat using scale value)
692                 *pBits++ = valueRGB;
693                 xr -= scale;
694 
695                 if (bit == 7)
696                     break;
697                 bit++;
698 
699                 // Shift to the next bit
700                 src0 >>= 1;
701                 src1 >>= 1;
702                 src2 >>= 1;
703             }
704             if (xr <= 0)
705                 break;  // End of line
706             addressBits++;  // Go to the next byte
707             pos++;
708         }
709     }
710 }
711 
Emulator_KeyEvent(quint8 keyscan,bool pressed)712 void Emulator_KeyEvent(quint8 keyscan, bool pressed)
713 {
714     if (m_EmulatorKeyQueueCount == KEYEVENT_QUEUE_SIZE) return;  // Full queue
715 
716     quint16 keyevent = MAKEWORD(keyscan, pressed ? 128 : 0);
717 
718     m_EmulatorKeyQueue[m_EmulatorKeyQueueTop] = keyevent;
719     m_EmulatorKeyQueueTop++;
720     if (m_EmulatorKeyQueueTop >= KEYEVENT_QUEUE_SIZE)
721         m_EmulatorKeyQueueTop = 0;
722     m_EmulatorKeyQueueCount++;
723 }
724 
Emulator_GetKeyEventFromQueue()725 quint16 Emulator_GetKeyEventFromQueue()
726 {
727     if (m_EmulatorKeyQueueCount == 0) return 0;  // Empty queue
728 
729     quint16 keyevent = m_EmulatorKeyQueue[m_EmulatorKeyQueueBottom];
730     m_EmulatorKeyQueueBottom++;
731     if (m_EmulatorKeyQueueBottom >= KEYEVENT_QUEUE_SIZE)
732         m_EmulatorKeyQueueBottom = 0;
733     m_EmulatorKeyQueueCount--;
734 
735     return keyevent;
736 }
737 
Emulator_ProcessKeyEvent()738 void Emulator_ProcessKeyEvent()
739 {
740     // Process next event in the keyboard queue
741     quint16 keyevent = Emulator_GetKeyEventFromQueue();
742     if (keyevent != 0)
743     {
744         bool pressed = ((keyevent & 0x8000) != 0);
745         quint8 ukncscan = (quint8)(keyevent & 0xff);
746         g_pBoard->KeyboardEvent(ukncscan, pressed);
747     }
748 }
749 
Emulator_FeedDAC(unsigned short l,unsigned short r)750 void CALLBACK Emulator_FeedDAC(unsigned short l, unsigned short r)
751 {
752     if (g_sound)
753     {
754         if (m_okEmulatorSound)
755             g_sound->FeedDAC(l, r);
756     }
757 }
758 
Emulator_SetSound(bool enable)759 void Emulator_SetSound(bool enable)
760 {
761     m_okEmulatorSound = enable;
762     if (g_pBoard != nullptr)
763     {
764         if (enable)
765             g_pBoard->SetSoundGenCallback(Emulator_FeedDAC);
766         else
767             g_pBoard->SetSoundGenCallback(nullptr);
768     }
769 }
770 
771 
772 //////////////////////////////////////////////////////////////////////
773 //
774 // Emulator image format - see CMotherboard::SaveToImage()
775 // Image header format (32 bytes):
776 //   4 bytes        UKNC_IMAGE_HEADER1
777 //   4 bytes        UKNC_IMAGE_HEADER2
778 //   4 bytes        UKNC_IMAGE_VERSION
779 //   4 bytes        UKNC_IMAGE_SIZE
780 //   4 bytes        UKNC uptime
781 //   12 bytes       Not used
782 //TODO: 256 bytes * 2 - Cartridge 1..2 path
783 //TODO: 256 bytes * 4 - Floppy 1..4 path
784 //TODO: 256 bytes * 2 - Hard 1..2 path
785 
Emulator_SaveImage(const QString & sFilePath)786 bool Emulator_SaveImage(const QString& sFilePath)
787 {
788     QFile file(sFilePath);
789     if (! file.open(QIODevice::Truncate | QIODevice::WriteOnly))
790     {
791         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to save image file."));
792         return false;
793     }
794 
795     // Allocate memory
796     quint8* pImage = (quint8*) ::calloc(UKNCIMAGE_SIZE, 1);
797     if (pImage == nullptr)
798     {
799         file.close();
800         return false;
801     }
802     // Prepare header
803     quint32* pHeader = (quint32*) pImage;
804     *pHeader++ = UKNCIMAGE_HEADER1;
805     *pHeader++ = UKNCIMAGE_HEADER2;
806     *pHeader++ = UKNCIMAGE_VERSION;
807     *pHeader++ = UKNCIMAGE_SIZE;
808     // Store emulator state to the image
809     g_pBoard->SaveToImage(pImage);
810     *(quint32*)(pImage + 16) = m_dwEmulatorUptime;
811 
812     // Save image to the file
813     qint64 bytesWritten = file.write((const char *)pImage, UKNCIMAGE_SIZE);
814     if (bytesWritten != UKNCIMAGE_SIZE)
815     {
816         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to save image file data."));
817         return false;
818     }
819 
820     // Free memory, close file
821     ::free(pImage);
822     file.close();
823 
824     return true;
825 }
826 
Emulator_LoadImage(const QString & sFilePath)827 bool Emulator_LoadImage(const QString &sFilePath)
828 {
829     Emulator_Stop();
830 
831     QFile file(sFilePath);
832     if (! file.open(QIODevice::ReadOnly))
833     {
834         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to load image file."));
835         return false;
836     }
837 
838     // Read header
839     quint32 bufHeader[UKNCIMAGE_HEADER_SIZE / sizeof(quint32)];
840     qint64 bytesRead = file.read((char*)bufHeader, UKNCIMAGE_HEADER_SIZE);
841     if (bytesRead != UKNCIMAGE_HEADER_SIZE)
842     {
843         file.close();
844         return false;
845     }
846 
847     //TODO: Check version and size
848 
849     // Allocate memory
850     quint8* pImage = (quint8*) ::malloc(UKNCIMAGE_SIZE);
851     if (pImage == nullptr)
852     {
853         file.close();
854         return false;
855     }
856 
857     // Read image
858     file.seek(0);
859     bytesRead = file.read((char*)pImage, UKNCIMAGE_SIZE);
860     if (bytesRead != UKNCIMAGE_SIZE)
861     {
862         ::free(pImage);
863         file.close();
864         AlertWarning(QT_TRANSLATE_NOOP("Emulator", "Failed to load image file data."));
865         return false;
866     }
867     else
868     {
869         // Restore emulator state from the image
870         g_pBoard->LoadFromImage(pImage);
871 
872         m_dwEmulatorUptime = *(quint32*)(pImage + 16);
873     }
874 
875     // Free memory, close file
876     ::free(pImage);
877     file.close();
878 
879     return true;
880 }
881 
882 
883 //////////////////////////////////////////////////////////////////////
884