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