-
Notifications
You must be signed in to change notification settings - Fork 16
/
ReadMe.txt
862 lines (716 loc) · 36.8 KB
/
ReadMe.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
Project: MKBasic (a.k.a. VM6502, a.k.a. VM65, I just can't decide
how to name it :-)).
Author: Copyright (C) Marek Karcz 2016. All rights reserved.
Free for personal and non-commercial use.
Code can be distributed and included in derivative work under
condition that the original copyright notice is preserved.
For use in commercial product, please contact me to obtain
permission and discuss possible fees, at: [email protected]
This software is provided with no warranty.
Purpose:
MOS 6502 emulator, Virtual CPU/Machine and potentially retro-style 8-bit
computer emulator.
MOS-6502-compatible virtual computer featuring BASIC interpreter, machine code
monitor, input/output device emulation etc.
Main UI of the program works in DOS/shell console.
Graphics display emulation requires SDL2.
The best way is to download source code of SDL2 and build it.
Makefile-s are included to build under Windows 32/64 (mingw compiler required)
and under Linux Ubuntu or Ubuntu based distro (GNU make).
SDL2 library must be on your execution path in order to run program.
E.g.:
set PATH=C:\src\SDL\lib\x64;%PATH%
To build under Windows 32/64:
* Install MINGW64 under C:\mingw-w64\x86_64-5.3.0 folder.
* Run mingw terminal.
* Build SDL2 library.
* Change current directory to that of this project.
* Set environment variable SDLDIR. (E.g.: set SDLDIR=C:\src\SDL)
* Run: makeming.bat
To build under Linux:
* Make sure C++11 compliant version of GCC compiler is installed.
NOTE: Under older distros, e.g.: Ubuntu Server 12.04, the newest c++
compiler installed may not be modern enough. Follow instructions
for your distro to install compatible c++ compiler.
E.g.:
https://gist.github.com/application2000/73fd6f4bf1be6600a2cf9f56315a2d91
* Build SDL2 library (make, make install) or download C/C++ headers and
install SDL2 from binary installation package.
* Change current directory to that of this project.
* Set environment variable SDLDIR. Your C/C++ headers for SDL2 should exist
in $SDLDIR/include folder.
* Run: make clean all
NOTE: To run the emulator under Linux, it may be necessary to set env.
variable LD_LIBRARY_PATH, e.g.:
export LD_LIBRARY_PATH=/usr/local/lib
Program passed following tests:
* 6502 functional test by Klaus Dormann
* AllSuiteA.asm from project hmc-6502
1. Credits/attributions:
Parts of this project is based on or contains 3-rd party work:
- Tiny Basic.
- Enhanced Basic by Lee Davison.
- Microchess by Peter Jennings (http://www.benlo.com/microchess/index.html).
- 6502 functional test by Klaus Dormann.
- All Suite test from project hmc-6502.
2. Format of the memory image definition file.
Emulator recognizes 4 formats of memory image:
- raw binary, no header, up to 64 kB of raw data,
- binary image with a configuration/snapshot header,
- Intel HEX format,
- plain text memory image definition file.
Please see detailed description of each format below:
Program can load raw binary image of data and MOS 6502 opcodes.
Binary image is always loaded from address $0000 and can be up to 64 kB long,
so the code must be properly located inside that image. Image can be shorter
than 64 kB, user will receive warning in such case, but it will be loaded.
Binary image may have header attached at the top.
Older version of header consists of magic keyword 'SNAPSHOT' followed by 15
bytes of data - this format had no space for expansion and will be removed
in future version. All new snapshots are saved in newest format.
Current version of header consists of magic keyword 'SNAPSHOT2' followed by
128 bytes of data. Not all of the 128 bytes are used, so there is a space
for expansion without the need of changing the file format.
The header data saves the status of CPU and emulation facilities like
character I/O address and enable flag, ROM boundaries and enable flag etc.
This header is added when user saves snapshot of the VM from debug console
menu with command: Y [file_name].
Below is the full detailed description of the header format:
* MAGIC_KEYWORD
* aabbccddefghijklmm[remaining unused bytes]
*
* Where:
* MAGIC_KEYWORD - text string indicating header, may vary between
* versions thus rendering headers from previous
* versions incompatible - currently: "SNAPSHOT2"
*
* Data:
*
* aa - low and hi bytes of execute address (PC)
* bb - low and hi bytes of char IO address
* cc - low and hi bytes of ROM begin address
* dd - low and hi bytes of ROM end address
* e - 0 if char IO is disabled, 1 if enabled
* f - 0 if ROM is disabled, 1 if enabled
* g - value in CPU Acc (accumulator) register
* h - value in CPU X (X index) register
* i - value in CPU Y (Y index) register
* j - value in CPU PS (processor status/flags)
* k - value in CPU SP (stack pointer) register
* l - 0 if generic graphics display device is disabled,
* 1 if graphics display is enabled
* mm - low and hi bytes of graphics display base address
* [remaining unused bytes are filled with 0-s]
Header is not mandatory, so the binary image created outside application can
also be used. User will receive warning at startup during image load if
header is missing, but image will be loaded. In this case, user may need
to configure necessary emulation facilities manually in debug console
before executing native 6502 code.
When binary image with a header is loaded, user can continue executing the
program from the place where it was saved by executing command from debug
console of the emulator:
X pc_value
Where:
pc_value - the address currently showing in CPU's PC register.
If the reset vector is set right, execute the 6502 code right from command
line:
vm65 -r image_name
Above will execute the code set in reset vector without having to start it
from debug console. If 6502 code requires character I/O and/or ROM facilities
then image should include header with proper setup.
Depending on your favorite 6502 assembler, you may need to use proper command
line arguments or configuration to achieve properly formatted binary file.
E.g.: if using CL65 from CC65 package, create configuration file that defines
memory segments that your 6502 code uses, then all of the segments (except the
last one) must have attribute 'fill' set to 'yes' so the unsused areas are
filled with 0-s.
Two CFG files, one for microchess and one for All Suite from hmc-6502 project
are supplied with this project and assembler source code adapted to be
compiled with CL65.
Other assemblers may need a different approach or may not be able to generate
binary images in format required for this emulator. In such case you may need
to design your own custom conversion tools to generate such images.
NOTE:
Simple conversion utility is supplied with this project (bin2hex), which is
described later in this file.
Emulator recognizes Intel HEX format file. It recognizes properly data
records and end-of-file record only at this time. Similar to binary image
with no header, when Intel HEX file is loaded, user may need to configure
necessary emulation facilities manually in debug console before executing
native 6502 code.
Program can also load memory image definition file (plain text), which is
a format developed especially for this project.
The format of the plain text memory image definition file is described below:
; comments
ADDR
address
data
ORG
address
data
IOADDR
address
ROMBEGIN
address
ROMEND
address
ENROM
ENIO
EXEC
address
ENGRAPH
GRAPHADDR
address
RESET
Where:
ADDR - label indicating that starting and run address will follow in
the next line
ORG - label indicating that the address counter will change to the
value provided in next line
IOADDR - label indicating that character I/O emulation trap address will
follow in the next line
ROMBEGIN - label indicating that the emulated read-only memory start
address will follow in the next line
ROMEND - label indicating that the emulated read-only memory end address
will follow in the next line
ENROM - enable read-only memory emulation
ENIO - enable character I/O emulation
EXEC - label indicating that the auto-execute address will follow
in the next line, 6502 program will auto-execute from that
address after memory definition file is done loading
ENGRAPH - enable generic graphics display device emulation with default
base address
GRAPHADDR - label indicating that base address for generic graphics display
device will follow in next line, also enables generic graphics
device emulation, but with the customized base address
RESET - initiate CPU reset sequence after loading memory definition file
address - decimal or hexadecimal (prefix $) address in memory
E.g:
ADDR
$0200
or
ADDR
512
changes the default start address (256) to 512.
ORG
49152
moves address counter to address 49152, following data will be
loaded from that address forward
data - the multi-line stream of decimal of hexadecimal ($xx) values
of size unsigned char (byte: 0-255) separated with spaces
or commas.
E.g.:
$00 $00 $00 $00
$00 $00 $00 $00
or
$00,$00,$00,$00
or
0 0 0 0
or
0,0,0,0
0 0 0 0
Each described above element of the memory image definition file is optional.
3. Character I/O emulation.
Emulator has ability to simulate a 80x25 text output display device and
rudimentary character I/O functions. The emulation is implemented by the means
of trapping memory locations defined to be designated I/O emulation addresses.
The default memory location is $E000 and also by default, the character I/O
is disabled. It can be enabled from the debug console with 'I' command:
I hexaddr
E.g.:
I E000
or
I FE00
or by putting optional statements in the memory image dedinition file:
ENIO
or
IOADDR
address
ENIO
Where:
address - decimal or hexadecimal (with prefix '$') address in memory
$0000 - $FFFF.
The same address is used for both, input and output operations.
Reading from IOADDR inside the 6502 code invokes a blocking character
input function from user's DOS/shell session.
After user enters the character, the memory location contains the character
code and also emulated CPU Acc register contains the same code.
Reading from IOADDR+1 inside 6502 code invokes a non-blocking character
input function from user's DOS/shell session.
The difference between blocking and non-blocking character input from 6502
code perspective:
- blocking
Just read from the address. Emulated device will wait for the character
input. The 6502 code does not need to be polling the address for
a keystroke.
- non-blocking
If there is no character/keystroke in the keyboard buffer, the function
will move on returning the value of 0. If implementing waiting character
input in 6502 code with this function, the 6502 code needs to be polling
the value returned from this address in a loop until it is different
than 0.
Note that there is no clearly distinguished prompt generated by emulator
when there is character input operation performed. It is designed like that
to avoid interfering with the character I/O performed by native 6502 code.
Therefore if user performs multi-step debugging in the debug console and
program suddenly stops, it is likely waiting for character input.
This is more clear when running the native 6502 code in non-debug execute
mode. In this case the I/O operations are represented on the screen instantly
and 6502 code may also produce prompts so user is aware when to enter data
to the program.
Writing to IOADDR inside the 6502 code will result in character code
being put in the IOADDR memory location and also written to the character
output buffer of the emulated display device.
When VM is running in one of the debug modes, like step-by-step mode
(S - step, N - go number of steps) or one of the debug code execution modes
(C- continue or G - go/cont. from new address), that character is not
immediately transferred to the user's DOS/shell session.
It is only written to the emulated display's text memory.
When VM is running in non-debug code execution mode (X - execute from new
address), the character is also output to the native DOS/shell console
(user's screen, a.k.a. standard output).
The character output history is therefore always kept in the memory of the
emulated text display device and can be recalled to the screen in debug
console with command 'T'.
There are 2 reasons for this:
* Performance.
In previous version only the emulated text display device approach was used.
That meant that each time there was a new character in the emulated display
buffer, the entire emulated text output device screen had to be refreshed on
the DOS/shell console. That was slow and caused screen flicker when characters
were output at high rate of speed.
* Record of character I/O operation.
During step-by-step debugging or multiple-step animated registers mode, any
characters output is immediately replaced by the registers and stack status
on the screen and is not visible on the screen. However user, while debugging
must be able to recall the history of the characters output to the emulated
text display device if user's program uses character I/O, for normal operation
or diagnostic purposes. This is when shadow copy of character I/O comes handy.
In real life, text output devices like text terminals, serial terminals,
especially the software emulated ones also offer the facilities to buffer the
output to memory for later review (history).
4. ROM (read-only memory) emulation.
This facility provides very basic means for memory mapping of the read-only
area. This may be required by some 6502 programs that check for non-writable
memory to establish the bounds of memory that can be used for data and code.
One good example is Tiny Basic.
By default the ROM emulation is disabled and the memory range of ROM is
defined as $D000 - $DFFF.
ROM emulation can be enabled (and the memory range defined) using debug
console's command 'K':
K [rombegin] [romend] - to enable
or
K - to disable
The ROM emulation can also be defined and enabled in the memory image
definition file with following statements:
ROMBEGIN
address
ROMEND
address
ENROM
For more sophisticated memory mapping schemes including multiple ROM ranges
and/or MMU-s that allow to read from ROM, write to RAM in the same range etc.
I suggest to write your ROM device emulating code and use it to expand the
functionality of this emulator using memory Mapped Devices framework,
explained later in this document and in the programmer's reference manual
document included with this project.
5. Additional comments and remarks.
IOADDR is permitted to be located in the emulated ROM memory range.
The writing to IOADDR is trapped first before checking ROM range and writing
to it is permitted when character I/O emulation and ROM are enabled at the
same time. It is a good idea in fact to put the IOADDR inside ROM range,
otherwise memory scanning routines like the one in Tiny Basic may trigger
unexpected character input because of the reading from IOADDR during the scan.
If you experience unexpected character input prompt while emulating
6502 code, this may be the case. Reconfigure your IOADDR to be inside ROM in
such case and try again.
Emulator is "cycle accurate" but not time or speed accurate.
This means that each call to MKCpu::ExecOpcode() method is considered a single
CPU cycle, so depending on the executed opcode, multiple calls (# varies per
opcode and other conditions) are needed to complete the opcode execution and
proceed to the next one. Method returns pointer to the virtual CPU registers.
One of the members of this structure is named CyclesLeft. When this variable
reaches 0, the opcode execution is considered complete.
The VMachine class calls the ExecOpcode() method as fast as possible, so it is
not real-time accurate, as already mentioned. To implement real-time accurate
emulation, the MKCpu::ExecOpcode() method calls would have to be synchronized
to some fairly accurate time scale (some kind of timer thread or a different
solution) to emulate the timing on the bus signals level. This emulator does
not implement such level of accuracy.
6. Debugger Console Command Reference.
S - step
Executes single opcode at current address.
C - continue
Continues code execution from current address until BRK.
M - dump memory
Usage: M [startaddr] [endaddr]
Where: startaddr,endaddr - memory addr. in hexadecimal format [0000..FFFF].
Dumps contents of memory, hexadecimal and ASCII formats."
G - go/continue from new address until BRK
Usage: G [address]
Where: address - memory addr. in hexadecimal format [0000.FFFF].
Executes code at provided address, interrupted by BRK opcode.
X - execute code from new address until RTS
Usage: X [address]
Where: address - memory addr. in hexadecimal format [0000.FFFF].
Executes code at provided address, until RTS (last one).
Q - quit
Exits from the emulator/debugger.
A - set address for next step
Usage: A [address]
Where: address - memory addr. in hexadecimal format [0000.FFFF].
Sets current address to a new value.
N - go number of steps
Usage: N [steps]
Where: steps - number of steps in decimal format
Execute number of opcodes provided in steps argument starting
from current address.
P - IRQ
Send maskable interrupt request to CPU (set the IRQ line LOW).
W - write to memory
Usage: W [address] [hexval] [hexval] ... 100
Where: address - memory addr. in hexadecimal format [0000.FFFF],
hexval - byte value in hexadecimal format [00.FF].
Writes provided values to memory starting at specified address.
I - toggle char I/O emulation
Usage: I [address]
Where: address - memory addr. in hexadecimal format [0000.FFFF],
Toggles basic character I/O emulation. When enabled, all writes
to the specified memory address also writes a character code to
to a virtual console. All reads from specified memory address
are interpreted as console character input.
V - toggle graphics display (video) emulation
Usage: V [address]
Where: address - memory addr. in hexadecimal format [0000.FFFF],
Toggles basic raster (pixel) based RGB graphics display emulation.
When enabled, window with graphics screen will open and several
registers are available to control the device starting at provided
base address. Read programmers reference for detailed documentation
regarding the available registers and their functions.
R - show registers
Displays CPU registers, flags and stack.
Y - snapshot
Usage: Y [file_name]
Where: file_name - the name of the output file.
Save snapshot of current CPU and memory in a binary file.
T - show I/O console
Displays/prints the contents of the virtual console screen.
Note that in run mode (commands X, G or C), virtual screen is
displayed automatically in real-time if I/O emulation is enabled.
E - toggle I/O local echo
Toggles local echo on/off when I/O emulation is enabled.
B - blank (clear) screen
Clears the screen, useful when after exiting I/O emulation or
registers animation (long stack) your screen is messed up.
F - toggle registers animation mode
When in multi-step debug mode (command: N), displaying registers
can be suppressed or, when animation mode is enabled - they will
be continuously displayed after each executed step.
J - set registers status animation delay
Usage: J [delay]
Where: delay - time of delay in milliseconds,
Sets the time added at the end of each execution step in multi
step mode (command: N). The default value is 250 ms.
K - toggle ROM emulation
Usage: K [rombegin] [romend] - to enable,
K - to disable,
(OR just use 'K' in both cases and be prompted for arguments.)
Where:
rombegin - hexadecimal address [0200..FFFF],
romend - hexadecimal address [rombegin+1..FFFF].
Enable/disable ROM emulation and define address range to which the ROM
(read-only memory) will be mapped. Default range: $D000-$DFFF.
L - load memory image
Usage: L [image_type] [image_name]
Where:
image_type - A - (auto), B (binary), H (Intel HEX) OR D (definition),
image_name - name of the image file.
This function allows to load new memory image from either binary
image file, Intel HEX format file or the ASCII definition file.
With option 'A' selected, automatic input format detection will be
attempted.
The binary image is always loaded from address 0x0000 and can be up to
64kB long. The definition file format is a plain text file that can
contain following keywords and data:
ADDR This keyword defines the run address of the executable code.
It is optional, but if exists, it must be the 1-st keyword
in the definition file.
Address in decimal or hexadecimal ($xxxx) format must follow
in the next line.
ORG Changes the current address counter. The line that follows
sets the new address in decimal or hexadecimal format.
Data that follows will be put in memory starting from that
address. This keyword is optional and can be used multiple
times in the definition file.
IOADDR Defines the address of the character I/O emulation. The
next line sets the address of I/O emulation in decimal or
hexadecimal format. If the I/O emulation is enabled
(see ENIO keyword), then any character written to this
address will be sent to the virtual console. The reading
from that address will invoke character input from the
emulated console. That input procedure is of blocking
type. To invoke non-blocking character procedure, reading
should be performed from IOADDR+1.
ROMBEGIN Defines the address in memory where the beginning of the
Read Only memory is mapped. The next line that follows this
keyword sets the address in decimal or hexadecimal format.
ROMEND Defines the address in memory where the end of the Read
Only Memory is mapped. The next line that follows this
keyword sets the address in decimal or hexadecimal format.
ENIO Putting this keyword in memory definition file enables
rudimentary character I/O emulation and virtual console
emulation.
ENROM Putting this keyword in memory definition file enables
emulation of Read Only Memory, in range of addresses
defined by ROMBEGIN and ROMEND keywords.
EXEC Define starting address of code which will be automatically
executed after the memory image is loaded.
The next line that follows this keyword sets the address
in decimal or hexadecimal format.
RESET Enables auto-reset of the CPU. After loading the memory
definition file, the CPU reset sequence will be initiated.
ENGRAPH Enables raster graphics device emulation.
GRAPHADDR Defines the base address of raster graphics device. The next
line that follows sets the address in decimal or hexadecimal
format.
NOTE: The binary image file can contain a header which contains
definitions corresponding to the above parameters at fixed
positions. This header is created when user saves the snapshot of
current emulator memory image and status. Example use scenario:
* User loads the image definition file.
* User adjusts various parameters of the emulator
(enables/disables devices, sets addresses, changes memory
contents).
* User saves the snapshot with 'Y' command.
* Next time user loads the snapshot image, all the parameters
and memory contents stick. This way game status can be saved
or a BASIC interpreter with BASIC program in it.
See command 'Y' for details.
O - display op-codes history
Show the history of last executed op-codes/instructions, full with
disassembled mnemonic, argument and CPU registers and status.
NOTE: op-codes execute history must be enabled, see command 'U'.
D - diassemble code in memory
Usage: D [startaddr] [endaddr]
Where: startaddr,endaddr - hexadecimal address [0000..FFFF].
Attempt to disassemble code in specified address range and display
the results (print) on the screen in symbolic form.
0 - reset
Run the processor initialization sequence, just like the real CPU
when its RTS signal is set to LOW and HIGH again. CPU will disable
interrupts, copy address from vector $FFFC to processors PC and will
start executing code. Programmer must put initialization routine
under address pointed by $FFFC vector, which will set the arithmetic
mode, initialize stack, I/O devices and enable IRQ if needed before
jumping to main loop. The reset routine disables trapping last RTS
opcode if stack is empty, so the VM will never return from opcodes
execution loop unless user interrupts with CTRL-C or CTRL-Break.
? - display commands menu
Display the menu of all available in Debug Console commands.
U - enable/disable exec. history
Toggle enable/disable of op-codes execute history.
Disabling this feature improves performance.
Z - enable/disable debug traces
Toggle enable/disable of debug traces.
2 - display debug traces
Display recent debug traces.
1 - enable/disable performance stats
Toggle enable/disable emulation speed measurement.
NOTE:
1. If no arguments provided, each command will prompt user to enter
missing data.
2. It is possible to exit from running program to debugger console
by pressing CTRL-C or CTRL-Pause/Break, which will generate
a "Operator Interrupt". However in the character input mode
use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux).
You may need to press ENTER after that in input mode (DOS).
7. Command line usage.
D:\src\wrk\mkbasic>vm65 -h
Virtual Machine/CPU Emulator (MOS 6502) and Debugger.
Copyright (C) by Marek Karcz 2016. All rights reserved.
Usage:
vm65 [-h] | [ramdeffile] [-b | -x] [-r]
Where:
ramdeffile - RAM definition file name
-b - specify input format as binary
-x - specify input format as Intel HEX
-r - after loading, perform CPU RESET
-h - print this help screen
When ran with no arguments, program will load default memory
definition files: default.rom, default.ram and will enter the debug
console menu.
When ramdeffile argument is provided with no input format specified,
program will attempt to automatically detect input format and load the
memory definition from the file, set the flags and parameters depending
on the contents of the memory definition file and enter the corresponding
mode of operation as defined in that file.
If input format is specified (-b|-x), program will load memory from the
provided image file and enter the debug console menu.
8. Utilities.
Utility bin2hex is supplied with the project to aid in conversion from raw
binary memory image to one of the plain text formats recognized by emulator
NOTE: In current version, emulator can load raw binary format directly, so
usefulness of this utility is somewhat deprecated.
D:\src\wrk\mkbasic>bin2hex -h
Program: bin2hex
Convert binary file to Intel HEX format.
OR
Convert binary file to memory image definition for MKBASIC (VM65) emulator.
Copyright: Marek Karcz 2016. All rights reserved.
Free for personal and educational use.
Usage:
bin2hex -f input -o output [-w addr] [-x exec] [[-s] [-z] | -i]
Where:
input - binary file name
output - output file name
addr - starting address to load data (default: 2048)
exec - address to auto-execute code from (default: 2048)
-s - suppress auto-execute statement in output
-z - suppress data blocks with 0-s only
-i - convert to Intel HEX format
NOTE: When this switch is used, addr, exec, -s, -z are ignored,
addr = 0, exec is not set and data blocks with 0-s only
are always suppressed.
9. Memory Mapped Device abstraction layer.
In microprocessor based systems in majority of cases communication with
peripheral devices is done via registers which in turn are located under
specific memory addresses.
Programming API responsible for modeling this functionality is implemented
in Memory and MemMapDev classes. The Memory class implements access to
specific memory locations and maintains the memory image.
The MemMapDev class implements specific device address spaces and handling
methods that are triggered when addresses that belong to the device are
accessed by the microprocessor.
Programmers can expand the functionality of this emulator by adding necessary
code emulating specific devices in MemMapDev and Memory classes implementation
and header files. In current version, two basic devices are implemented:
character I/O and raster (pixel based) graphics display. Both can be activated
or inactivated at will and provide simple register based interface that
requires no extra memory space use for data.
E.g.:
Character I/O device uses just 2 memory locations, one for non-blocking I/O
and one for blocking I/O. Writing to location causes character output, while
reading from location waits for character input (blocking mode) or reads the
character from keyboard buffer if available (non-blocking mode) or returns 0.
The graphics display can be accessed by writing to multiple memory locations.
If we assume that GRDEVBASE is the base address of the Graphics Device, there
are following registers:
Offset Register Description
----------------------------------------------------------------------------
0 GRAPHDEVREG_X_LO Least significant part of pixel's X (column)
coordinate or begin of line coord. (0-255)
1 GRAPHDEVREG_X_HI Most significant part of pixel's X (column)
coordinate or begin of line coord. (0-1)
2 GRAPHDEVREG_Y Pixel's Y (row) coordinate (0-199)
3 GRAPHDEVREG_PXCOL_R Pixel's RGB color component - Red (0-255)
4 GRAPHDEVREG_PXCOL_G Pixel's RGB color component - Green (0-255)
5 GRAPHDEVREG_PXCOL_B Pixel's RGB color component - Blue (0-255)
6 GRAPHDEVREG_BGCOL_R Backgr. RGB color component - Red (0-255)
7 GRAPHDEVREG_BGCOL_G Backgr. RGB color component - Green (0-255)
8 GRAPHDEVREG_BGCOL_B Backgr. RGB color component - Blue (0-255)
9 GRAPHDEVREG_CMD Command code
10 GRAPHDEVREG_X2_LO Least significant part of end of line's X
coordinate
11 GRAPHDEVREG_X2_HI Most significant part of end of line's X
coordinate
12 GRAPHDEVREG_Y2 End of line's Y (row) coordinate (0-199)
13 GRAPHDEVREG_CHRTBL Set the 2 kB bank where char. table resides
14 GRAPHDEVREG_TXTCURX Set text cursor position (column)
15 GRAPHDEVREG_TXTCURY Set text cursor position (row)
16 GRAPHDEVREG_PUTC Output char. to current pos. and move cursor
17 GRAPHDEVREG_CRSMODE Set cursor mode : 0 - not visible, 1 - block
18 GRAPHDEVREG_TXTMODE Set text mode : 0 - normal, 1 - reverse
NOTE: Functionality maintaining text cursor is not yet implemented.
Writing values to above memory locations when Graphics Device is enabled
allows to set the corresponding parameters of the device, while writing to
command register executes corresponding command (performs action) per codes
listed below:
Command code Command description
------------------------------------------------------------------------------
GRAPHDEVCMD_CLRSCR = 0 Clear screen
GRAPHDEVCMD_SETPXL = 1 Set the pixel location to pixel color
GRAPHDEVCMD_CLRPXL = 2 Clear the pixel location (set to bg color)
GRAPHDEVCMD_SETBGC = 3 Set the background color
GRAPHDEVCMD_SETFGC = 4 Set the foreground (pixel) color
GRAPHDEVCMD_DRAWLN = 5 Draw line
GRAPHDEVCMD_ERASLN = 6 Erase line
Reading from registers has no effect (returns 0).
Above method of interfacing GD requires no dedicated graphics memory space
in VM's RAM. It is also simple to implement.
The downside - slower performance (multiple memory writes to select/unselect
a pixel or set color).
I plan to add graphics frame buffer in the VM's RAM address space in future
release.
Simple demo program written in EhBasic that shows how to drive the graphics
screen is included: grdevdemo.bas.
10. Performance considerations.
Program measures the emulation performance and displays it in the Debug
Console. It uses a 1 MHz CPU as a reference to return % of speed compared to
assumed 1,000,000 CPU cycles or clock ticks per second - which is considered
a 100 % speed.
Performance is measured during the whole execution cycle and calculated at the
end of the run. Captured speed is summed with previous result and divided by 2
to produce average emulation speed during single session.
This emulator has been optimized for performance. I had issues with
emulation speed in previous version, mostly because it was a prototype with
many debugging aids enabled by default and not yet optimized for speed.
I took a good look at all the critical parts of code and fixed the problems.
I am sure there is still space for improvement, but now the emulation speed
leaves good margin for expansion with new emulated peripherals and still
should compare well to the model 1 MHz CPU.
Emulating of pure 6502 machine code with all peripherals (memory mapped
devices, I/O etc.) emulation disabled and time critical debugging facility,
the op-codes execute history also disabled, returns performance in range of
646 % (PC1) and 468 % (PC2) (* see annotation below).
Enabling the op-code execute history drops the performance.
With all peripherals disabled and op-code history enabled we are down to
411 % on PC1 and 312 % on PC2.
Enabling and adding the emulated memory mapped devices to the pool may cause
the emulation speed to drop as well. However even with currently implemented
peripherals (char I/O, graphics raster device) enabled and actively used and
op-codes execute history enabled the performance is still well above 300 %
on both PC1 and on PC2 (* see annotations for PC configurations/specs).
The same case but with op-code execute history disabled - performance exceeds
400 % on both PC configurations.
Currently the main emulation loop is not synchronized to an accurate
clock tick or raster synchronization signal but just runs as fast as it can.
Therefore emulation speed may vary per PC and per current load on the system.
If this code is to be used as a template to implement emulator of a real-world
machine, like C-64 or Apple I, it may need some work to improve performance,
but I think is leaves a pretty good margin for expansion as it is.
On a fast PC (* see annotation) the emulation speed above 600 % with
basically nothing but CPU emulated and op-codes execute history disabled
(which should be disabled by default as it is needed for debugging purposes
only) is IMO decent if we don't want to emulate MOS 6502 machine with clock
much faster than 1 MHz.
Annotations to 'Performance considerations':
*)
PC1 stats:
Type: Desktop
CPU: 2.49 GHz (64-bit Quad-core Q8300)
RAM: 4,060 MB
OS: Windows 10 Pro (no SP) [6.2.9200]
PC2 stats:
Type: Laptop
CPU: 2.3 GHz (64-bit Quad-core i5-6300HQ)
RAM: 15.9 GB
OS: Win 10 Home.
11. Problems, issues, bugs.
* Regaining focus of the graphics window when it is not being written to by the
6502 code is somewhat flakey. Since the window has no title bar, user can
only switch to it by ALT-TAB (windows) or clicking on the corresponding icon
on the task bar. However it doesn't always work. Switching to the DOS console
of emulator while in emulation mode should bring the graphics window back
to front.
12. Warranty and License Agreement.
This software is provided with No Warranty.
I (The Author) will not be held responsible for any damage to computer
systems, data or user's health resulting from using this software.
Please use responsibly.
This software is provided in hope that it will be be useful and free of
charge for non-commercial and educational use.
Distribution of this software in non-commercial and educational derivative
work is permitted under condition that original copyright notices and
comments are preserved. Some 3-rd party work included with this project
may require separate application for permission from their respective
authors/copyright owners.