Browse code

Add source code of the XiPC emulator

Pawel Jablonski authored on 17/03/2023 16:46:08
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,558 @@
1
+/*
2
+ * Author: Pawel Jablonski
3
+ * E-mail: pj@xirx.net
4
+ * WWW: xirx.net
5
+ * GIT: git.xirx.net
6
+ *
7
+ * License: You can use this code however you like
8
+ * but leave information about the original author.
9
+ * Code is free for non-commercial and commercial use.
10
+ */
11
+
12
+#include "io.h"
13
+
14
+/**
15
+ * Constructor for the IO class. It connects all communication classes for IO operations.
16
+ *
17
+ * @param parent Parent object
18
+ */
19
+IO::IO(QObject *parent) : QObject(parent)
20
+{
21
+	this->reset();
22
+
23
+	QObject::connect(&this->led, SIGNAL(updateRunSignal(bool)), this, SIGNAL(updateLEDRunSignal(bool)));
24
+	QObject::connect(&this->led, SIGNAL(updateErrorSignal(bool)), this, SIGNAL(updateLEDErrorSignal(bool)));
25
+
26
+	QObject::connect(&this->lcd, SIGNAL(updateBufferSignal(LCD::Buffer)), this, SIGNAL(updateLCDBufferSignal(LCD::Buffer)));
27
+
28
+	QObject::connect(&this->rs232, SIGNAL(updateTxSignal(unsigned char)), this, SIGNAL(updateRS232TxSignal(unsigned char)));
29
+	QObject::connect(&this->rs232, SIGNAL(updateRxSignal(unsigned char)), this, SIGNAL(updateRS232RxSignal(unsigned char)));
30
+
31
+	QObject::connect(&this->rtc, SIGNAL(updateDateTimeSignal(QDateTime)), this, SIGNAL(updateRTCDateTimeSignal(QDateTime)));
32
+
33
+	QObject::connect(&this->speaker, SIGNAL(updateStatusSignal(int)), this, SIGNAL(updateSpeakerStatusSignal(int)));
34
+
35
+	QObject::connect(&this->keyboard, SIGNAL(dataReadySignal(bool)), this, SLOT(keyboardDataReadySlot(bool)));
36
+	QObject::connect(&this->rs232, SIGNAL(dataReadySignal(bool)), this, SLOT(rs232DataReadySlot(bool)));
37
+}
38
+
39
+//! Destructor for the IO class
40
+IO::~IO()
41
+{
42
+	QObject::disconnect(this);
43
+}
44
+
45
+//! Reset the IO buffers and trigger reset for all connected communication classes
46
+void IO::reset()
47
+{
48
+	this->regSelected = 0;
49
+	this->reg = {};
50
+	this->in = 0;
51
+
52
+	this->keyboard.reset();
53
+	this->led.reset();
54
+	this->lcd.reset();
55
+	this->rs232.reset();
56
+	this->rtc.reset();
57
+	this->speaker.reset();
58
+	this->fs.reset();
59
+}
60
+
61
+/**
62
+ * Process a key press event
63
+ *
64
+ * @param key Pressed key code
65
+ * @param modifiers Optional modifiers to pressed key
66
+ */
67
+void IO::keyboardKeyPress(int key, Qt::KeyboardModifiers modifiers)
68
+{
69
+	this->keyboard.keyPress(key, modifiers);
70
+}
71
+
72
+/**
73
+ * Set enable status of the run LED
74
+ *
75
+ * @param enable Enable status
76
+ */
77
+void IO::ledSetRun(bool enable)
78
+{
79
+	this->led.setRun(enable);
80
+}
81
+
82
+/**
83
+ * Set enable status of the error LED
84
+ *
85
+ * @param enable Enable status
86
+ */
87
+void IO::ledSetError(bool enable)
88
+{
89
+	this->led.setError(enable);
90
+}
91
+
92
+/**
93
+ * Process a received text event
94
+ *
95
+ * @param text Received text
96
+ */
97
+void IO::rs232Receive(const QString &text)
98
+{
99
+	this->rs232.rx(text);
100
+}
101
+
102
+/**
103
+ * Set date and time
104
+ *
105
+ * @param dateTime Date and time to set in own RTC format
106
+ */
107
+void IO::rtcSetDateTime(const QDateTime &dateTime)
108
+{
109
+	this->rtc.setDateTime(dateTime);
110
+}
111
+
112
+/**
113
+ * Set volume
114
+ *
115
+ * @param volume Volume level in range 0-100
116
+ */
117
+void IO::speakerSetVolume(unsigned int volume)
118
+{
119
+	this->speaker.setVolume(volume);
120
+}
121
+
122
+/**
123
+ * Set path to the emulated file system on the local disk
124
+ *
125
+ * @param path Path to emulated file system
126
+ */
127
+void IO::fsSetPath(const QString &path)
128
+{
129
+	this->fs.setPath(path);
130
+}
131
+
132
+/**
133
+ * Process "keyboard data is ready to get" event
134
+ *
135
+ * @param ready Status of ready data to get
136
+ */
137
+void IO::keyboardDataReadySlot(bool ready)
138
+{
139
+	if(ready)
140
+	{
141
+		this->in |= IN_KEYBOARD_READY_BIT;
142
+	}
143
+	else
144
+	{
145
+		this->in &= ~IN_KEYBOARD_READY_BIT;
146
+	}
147
+
148
+	emit inSignal(this->in);
149
+}
150
+
151
+/**
152
+ * Process "RS232 receive data is ready to get" event
153
+ *
154
+ * @param ready Status of ready data to get
155
+ */
156
+void IO::rs232DataReadySlot(bool ready)
157
+{
158
+	if(ready)
159
+	{
160
+		this->in |= IN_RS232_READY_BIT;
161
+	}
162
+	else
163
+	{
164
+		this->in &= ~IN_RS232_READY_BIT;
165
+	}
166
+
167
+	emit inSignal(this->in);
168
+}
169
+
170
+/**
171
+ * Process "update the output register value" event.
172
+ * Fill up the output register data half with corresponding data from selected virtual register.
173
+ *
174
+ * @param out Output register value with set up data of the flags half
175
+ */
176
+void IO::outSlot(unsigned char out)
177
+{
178
+	if((out & OUT_DATA_READY_BIT) != 0)
179
+	{
180
+		unsigned char value = out;
181
+
182
+		bool half = ((value & OUT_HALF_BIT) != 0);
183
+		bool mode = ((value & OUT_MODE_BIT) != 0);
184
+		bool rw = ((value & OUT_RW_BIT) != 0);
185
+
186
+		if(!mode)
187
+		{
188
+			// Select register to work
189
+			regSelected = (value & REG_SELECT_MASK);
190
+		}
191
+		else
192
+		{
193
+			if(!rw)
194
+			{
195
+				unsigned char dataOut = 0x00;
196
+
197
+				// Read register value
198
+				switch(regSelected)
199
+				{
200
+					case REG_KEYBOARD :
201
+						{
202
+							if(!half)
203
+							{
204
+								this->reg.keyboard = this->keyboard.getKey();
205
+							}
206
+
207
+							dataOut = this->reg.keyboard;
208
+						}
209
+						break;
210
+
211
+					case REG_RS232_RX :
212
+						{
213
+							if(!half)
214
+							{
215
+								this->reg.rs232Rx = this->rs232.receive();
216
+							}
217
+
218
+							dataOut = this->reg.rs232Rx;
219
+						}
220
+						break;
221
+
222
+					case REG_RTC_FIELD :
223
+						switch(this->reg.address.low)
224
+						{
225
+							case RTC_YEAR :
226
+								dataOut = this->reg.rtcField.year;
227
+								break;
228
+
229
+							case RTC_MONTH :
230
+								dataOut = this->reg.rtcField.month;
231
+								break;
232
+
233
+							case RTC_DAY :
234
+								dataOut = this->reg.rtcField.day;
235
+								break;
236
+
237
+							case RTC_HOUR :
238
+								dataOut = this->reg.rtcField.hour;
239
+								break;
240
+
241
+							case RTC_MINUTE :
242
+								dataOut = this->reg.rtcField.minute;
243
+								break;
244
+
245
+							case RTC_SECOND :
246
+								dataOut = this->reg.rtcField.second;
247
+								break;
248
+
249
+							default :
250
+								dataOut = 0;
251
+								break;
252
+						}
253
+						break;
254
+
255
+					case REG_SPEAKER_FIELD :
256
+						{
257
+							if(!half)
258
+							{
259
+								this->reg.speakerBufferFree = this->speaker.getBufferFree();
260
+							}
261
+
262
+							dataOut = this->reg.speakerBufferFree;
263
+						}
264
+						break;
265
+
266
+					case REG_FS_DATA :
267
+						{
268
+							dataOut = this->reg.fsBlock.data[this->reg.address.low];
269
+
270
+							if(half)
271
+							{
272
+								this->reg.address.low++;
273
+							}
274
+						}
275
+						break;
276
+
277
+					case REG_FS_COMMAND :
278
+						switch(this->reg.address.low)
279
+						{
280
+							case FS_OPEN_APP :
281
+								{
282
+									this->reg.fsName.data[FS::NAME_MAX_LENGTH] = 0;
283
+
284
+									if(this->fs.open("app", this->reg.fsName, this->reg.fsSize))
285
+									{
286
+										dataOut = 1;
287
+									}
288
+									else
289
+									{
290
+										dataOut = 0;
291
+									}
292
+								}
293
+								break;
294
+
295
+							case FS_OPEN_FILE :
296
+								{
297
+									this->reg.fsName.data[FS::NAME_MAX_LENGTH] = 0;
298
+
299
+									if(this->fs.open("file", this->reg.fsName, this->reg.fsSize))
300
+									{
301
+										dataOut = 1;
302
+									}
303
+									else
304
+									{
305
+										dataOut = 0;
306
+									}
307
+								}
308
+								break;
309
+
310
+							case FS_LIST_APP :
311
+								{
312
+									if(this->fs.list("app", this->reg.fsSize))
313
+									{
314
+										dataOut = 1;
315
+									}
316
+									else
317
+									{
318
+										dataOut = 0;
319
+									}
320
+								}
321
+								break;
322
+
323
+							case FS_LIST_FILE :
324
+								{
325
+									if(this->fs.list("file", this->reg.fsSize))
326
+									{
327
+										dataOut = 1;
328
+									}
329
+									else
330
+									{
331
+										dataOut = 0;
332
+									}
333
+								}
334
+								break;
335
+
336
+							case FS_SIZE :
337
+								switch(this->reg.address.high)
338
+								{
339
+									case 0 :
340
+										dataOut = this->reg.fsSize.low;
341
+										break;
342
+
343
+									case 1 :
344
+										dataOut = this->reg.fsSize.high;
345
+										break;
346
+
347
+									default:
348
+										dataOut = 0;
349
+										break;
350
+								}
351
+								break;
352
+
353
+							default :
354
+								dataOut = 0;
355
+								break;
356
+						}
357
+						break;
358
+
359
+					default :
360
+						dataOut = 0;
361
+						break;
362
+				}
363
+
364
+				if(half)
365
+				{
366
+					dataOut >>= 4;
367
+				}
368
+
369
+				// Register value to out
370
+				this->in = ((this->in & IN_CONTROL_MASK) | (dataOut & IN_DATA_MASK));
371
+			}
372
+			else
373
+			{
374
+				unsigned char dataInRaw = (value & IN_DATA_MASK);
375
+				unsigned char dataIn = dataInRaw;
376
+				unsigned char mask = HALF_UP_DATA_MASK;
377
+
378
+				if(half)
379
+				{
380
+					dataIn <<= HALF_DATA_OFFSET;
381
+					mask >>= HALF_DATA_OFFSET;
382
+				}
383
+
384
+				// Write register value
385
+				switch(this->regSelected)
386
+				{
387
+					case REG_LED_COMMAND :
388
+						switch(dataInRaw)
389
+						{
390
+							case LED_RUN_OFF :
391
+								this->led.setRun(false);
392
+								break;
393
+							case LED_RUN_ON :
394
+								this->led.setRun(true);
395
+								break;
396
+							case LED_ERROR_OFF :
397
+								this->led.setError(false);
398
+								break;
399
+							case LED_ERROR_ON :
400
+								this->led.setError(true);
401
+								break;
402
+						}
403
+						break;
404
+
405
+					case REG_ADDRESS_LOW :
406
+						this->reg.address.low = ((this->reg.address.low & mask) | dataIn);
407
+						break;
408
+
409
+					case REG_ADDRESS_HIGH :
410
+						this->reg.address.high = ((this->reg.address.high & mask) | dataIn);
411
+						break;
412
+
413
+					case REG_LCD_COLOR :
414
+						if(!half)
415
+						{
416
+							this->reg.lcdForegroundColor = dataInRaw;
417
+						}
418
+						else
419
+						{
420
+							this->reg.lcdBackgroundColor = dataInRaw;
421
+						}
422
+						break;
423
+
424
+					case REG_LCD_CHAR :
425
+						this->reg.lcdChar = ((this->reg.lcdChar & mask) | dataIn);
426
+						if(half)
427
+						{
428
+							this->lcd.setChar(this->reg.lcdChar, this->reg.address.low, this->reg.address.high, this->reg.lcdForegroundColor, this->reg.lcdBackgroundColor);
429
+						}
430
+						break;
431
+
432
+					case REG_LCD_COMMAND :
433
+						switch(dataIn)
434
+						{
435
+							case LCD_CLEAR :
436
+								this->lcd.clear();
437
+								this->lcd.refresh();
438
+								this->lcd.update();
439
+								break;
440
+							case LCD_REFRESH :
441
+								this->lcd.refresh();
442
+								this->lcd.update();
443
+								break;
444
+							case LCD_SCROLL :
445
+								this->lcd.scroll();
446
+								break;
447
+						}
448
+						break;
449
+
450
+					case REG_RS232_TX :
451
+						this->reg.rs232Tx = ((this->reg.rs232Tx & mask) | dataIn);
452
+						if(half)
453
+						{
454
+							this->rs232.send(this->reg.rs232Tx);
455
+						}
456
+						break;
457
+
458
+					case REG_RTC_FIELD :
459
+						switch(this->reg.address.low)
460
+						{
461
+							case RTC_YEAR :
462
+								this->reg.rtcField.year = ((this->reg.rtcField.year & mask) | dataIn);
463
+								break;
464
+
465
+							case RTC_MONTH :
466
+								this->reg.rtcField.month = ((this->reg.rtcField.month & mask) | dataIn);
467
+								break;
468
+
469
+							case RTC_DAY :
470
+								this->reg.rtcField.day = ((this->reg.rtcField.day & mask) | dataIn);
471
+								break;
472
+
473
+							case RTC_HOUR :
474
+								this->reg.rtcField.hour = ((this->reg.rtcField.hour & mask) | dataIn);
475
+								break;
476
+
477
+							case RTC_MINUTE :
478
+								this->reg.rtcField.minute = ((this->reg.rtcField.minute & mask) | dataIn);
479
+								break;
480
+
481
+							case RTC_SECOND :
482
+								this->reg.rtcField.second = ((this->reg.rtcField.second & mask) | dataIn);
483
+								break;
484
+						}
485
+						break;
486
+
487
+					case REG_RTC_COMMAND :
488
+						switch(dataInRaw)
489
+						{
490
+							case RTC_READ :
491
+								this->reg.rtcField = this->rtc.getDateTime();
492
+								break;
493
+
494
+							case RTC_WRITE :
495
+								this->rtc.setDateTime(this->reg.rtcField);
496
+								break;
497
+						}
498
+						break;
499
+
500
+					case REG_SPEAKER_FIELD :
501
+						switch(this->reg.address.low)
502
+						{
503
+							case SPEAKER_NOTE :
504
+								this->reg.speakerField.note = ((this->reg.speakerField.note & mask) | dataIn);
505
+								break;
506
+
507
+							case SPEAKER_TIME :
508
+								this->reg.speakerField.time = ((this->reg.speakerField.time & mask) | dataIn);
509
+								break;
510
+
511
+							case SPEAKER_FILL :
512
+								this->reg.speakerField.fill = dataInRaw;
513
+								break;
514
+
515
+							case SPEAKER_VOLUME :
516
+								this->reg.speakerField.volume = dataInRaw;
517
+								break;
518
+						}
519
+						break;
520
+
521
+					case REG_SPEAKER_COMMAND :
522
+						switch(dataInRaw)
523
+						{
524
+							case SPEAKER_CLEAR :
525
+								this->speaker.clear();
526
+								break;
527
+
528
+							case SPEAKER_ADD_NOTE :
529
+								this->speaker.addNote(this->reg.speakerField);
530
+								break;
531
+						}
532
+						break;
533
+
534
+					case REG_FS_NAME :
535
+						if(this->reg.address.low < FS::NAME_MAX_LENGTH)
536
+						{
537
+							this->reg.fsName.data[this->reg.address.low] = ((this->reg.fsName.data[this->reg.address.low] & mask) | dataIn);
538
+						}
539
+						break;
540
+
541
+					case REG_FS_COMMAND :
542
+						switch(dataInRaw)
543
+						{
544
+							case FS_READ_BLOCK :
545
+								this->fs.readBlock(this->reg.fsBlock, this->reg.address.low);
546
+								break;
547
+						}
548
+						break;
549
+				}
550
+			}
551
+		}
552
+
553
+		// Operation is completed
554
+		this->in ^= IN_OPERATION_COMPLETE_BIT;
555
+
556
+		emit inSignal(this->in);
557
+	}
558
+}