mirror of https://github.com/makarcz/vm6502.git
966 lines
34 KiB
Plaintext
966 lines
34 KiB
Plaintext
|
||
-------------------------------------------- TO DO:
|
||
|
||
* Implement all 6502 opcodes.
|
||
STATUS: DONE. Testing passed.
|
||
|
||
* Implement memory write in external machine code monitor/control program.
|
||
STATUS: DONE.
|
||
|
||
* Implement extended opcodes to make VM easier for BASIC language implementation.
|
||
STATUS: Not started.
|
||
|
||
* Write BASIC compiler for my VM.
|
||
STATUS: Not started.
|
||
|
||
* Step by step debugging (do not rely on BRK).
|
||
STATUS: Done.
|
||
|
||
* In debugger, under Stack pointer display, display the contents of the stack from
|
||
the top (*SP) to bottom ($1FF).
|
||
Something like:
|
||
STOPPED at 46b
|
||
Registers:
|
||
Acc: $38 (%00111000)
|
||
X: $0
|
||
Y: $1
|
||
Addr: $46b
|
||
Acc16: $0
|
||
Ptr16: $0
|
||
Stack: $fd
|
||
[$34 $04]
|
||
Flags: NV-BDIZC
|
||
00101000
|
||
----------------------------------------
|
||
|
||
STATUS: DONE.
|
||
|
||
* In debugger add displaying of the decimal values.
|
||
STATUS: Not started.
|
||
|
||
* Add ability to set breakpoints.
|
||
This will also need a new execute or continue command variation that will stop
|
||
at the next breakpoint.
|
||
STATUS: Not started.
|
||
|
||
* Add basic character I/O emulation.
|
||
STATUS: DONE.
|
||
|
||
* Add displaying last executed op-code and argument.
|
||
STATUS: DONE.
|
||
|
||
* Implement disassembler in the debug console.
|
||
STATUS: DONE.
|
||
|
||
* Add "animation" mode, where in multi-step debugging mode the registers will
|
||
be displayed continuously at the fixed area of the screen.
|
||
STATUS: DONE.
|
||
|
||
* Implement time-scale and cycles emulation.
|
||
Note that the opcodes map contains only base # of cycles per opcode.
|
||
For accurate emulation, add 1 cycle if page boundary is crossed for select
|
||
opcodes and 1 cycle on branches taken.
|
||
Reference: http://www.oxyron.de/html/opcodes02.html
|
||
STATUS: IN PROGRESS.
|
||
Since cycle-accurate emulation was introduced, a step in debugger is
|
||
no longer a step but a cycle. It should be updated in documentation or
|
||
corrected in code so the step calls the MKCpu::Step until the # of
|
||
cycles left is 0. - DONE, Step() method is called until # of cycles
|
||
is 0.
|
||
VMachine::Run – correct so the virtual display is shown only at the
|
||
end of each instruction, not after each cycle. - DONE.
|
||
|
||
* Add ability to load binary 6502 code and hex formats.
|
||
Should be able to load from command line or debug console.
|
||
STATUS: DONE.
|
||
|
||
* Add fixed size header to binary image with emulator
|
||
* configuration data. Presence of the header will be detected
|
||
* by magic key at the beginning. Header should also include
|
||
* snapshot info, so the program can continue from the place
|
||
* where it was frozen/saved.
|
||
* Add hot-key to create snapshot/save memory image in binary file
|
||
* with header data including next execute address in memory.
|
||
STATUS: DONE.
|
||
The hot-key was not added. User must interrupt program
|
||
(CTRL-C, CTRL-Pause/Break) and then save snapshot from debugger console
|
||
menu (Y filename).
|
||
|
||
* Add ability to configure ROM range and I/O address as well as turn on/off
|
||
features in the memory image definition file.
|
||
Add keywords:
|
||
IOADDR for setting up I/O emulation address.
|
||
ENIO for I/O enabling.
|
||
ROMBEGIN, ROMEND for setting up ROM address range.
|
||
ENROM to enable ROM.
|
||
EXEC to automatically execute code at address.
|
||
STATUS: DONE.
|
||
|
||
* Refactor - MKCpu::ExecOpcode()
|
||
Replace huge switch/case statement with array of functions.
|
||
STATUS: DONE.
|
||
|
||
* Display emulation - method Display::ShowScr() works well only when console/DOS
|
||
window width matches exactly the emulated display width (currently hardcoded
|
||
80 characters). Make it work when console is wider. Automatically decrease
|
||
emulated display size when console is smaller.
|
||
STATUS: COMPLETED.
|
||
|
||
* Add Reset option in debug console, which will send the processor through its
|
||
initialization/reset procedure.
|
||
STATUS: DONE.
|
||
|
||
* Add public API methods to MKCpu class that will trigger IRQ, NMI and RESET.
|
||
|
||
According to MOS 6502 specs, when the ORQ signal comes, the execution of
|
||
current opcode is allowed to finished and then 7-cycle interrupt sequence
|
||
is executed.
|
||
|
||
STATUS: IN PROGRESS.
|
||
Reset() added to VMachine and MKCpu.
|
||
Interrupt() (IRQ) added to VMachine and MKCpu. Need to make it cycle
|
||
accurate.
|
||
Need to implement NMI.
|
||
|
||
* Add automatic recognition of memory image file format.
|
||
STATUS: DONE.
|
||
|
||
* Add graphical display device emulator.
|
||
STATUS: In progress.
|
||
|
||
Abstraction layer for memory mapped devices. - OK
|
||
Activate/deactivate graphics display from debugger console. - OK
|
||
Activate/deactivate graphics display in memory definition file
|
||
and binary header. - DONE.
|
||
NOTE: I did not reserve space for future
|
||
expansion in header section, therefore I have to recognize the older
|
||
version of header by old magic keyword: HDRMAGICKEY_OLD
|
||
New header is recognized by HDRMAGICKEY and this one has space for
|
||
future expansion (128 bytes of data vs 15 in older format) so there
|
||
will be no need to change header format if data are added up to 128
|
||
total bytes (currently only 18 bytes used).
|
||
Add line draw function/command. - DONE
|
||
Add line erase function/command. - DONE
|
||
Add rectangle draw/erase function/command. - NOT DONE
|
||
Add closed shape fill function/command. - NOT DONE
|
||
Add sprites. - NOT DONE.
|
||
Add text mode. - IN PROGRESS.
|
||
Add VM65 memory mapped raster buffer - NOT DONE.
|
||
Review all load/save methods in VMachine.cpp and correct where needed. - DONE.
|
||
[Either disable devices emulation facilities for the time of accessing memory
|
||
during load/save or skip the memory ranges assigned to active devices.
|
||
I can also introduce and use Peek8BitImg/Poke8BitImg methods that ignore memory
|
||
mapped devices.]
|
||
[Methods to review:
|
||
VMachine::SaveSnapshot,
|
||
VMachine::LoadRAMBin,
|
||
VMachine::LoadRAMHex,
|
||
VMachine::LoadMEM]
|
||
|
||
Implementing with SDL2.
|
||
I have a rudimentary graphical display class now which can clear
|
||
the screen, set bg/fg colors, set/unset pixel etc.
|
||
Now what is needed is some fake VIC chip emulation class
|
||
and some elegant way of introducing the device's registers into the
|
||
memory address space.
|
||
I designed an abstract layer between the memory and memory
|
||
mapped device. Any read/write operation on memory checks
|
||
the list of memory mapped devices. If the address falls in to the
|
||
range of any of these devices, the r/w operation is
|
||
redirected to appropriate class entry/handler method.
|
||
The devices implementations: MemMapDev.h/MemMapDev.cpp.
|
||
Text mode will require:
|
||
- the characters table base address (one of the 16 4 KB banks)
|
||
- characters table
|
||
- cursor and cursor position control/movement/mode
|
||
|
||
* Add mass storage device emulator.
|
||
STATUS: Started.
|
||
|
||
Created MassStorage.h, MassStorage.cpp for implementation of a disk-like
|
||
device. The device will emulate abstracts typical to disk surface, like
|
||
track, sector, block. It will be rather low level but will keep the images
|
||
of the emulated disk media on the hard drive.
|
||
This device will be then mapped to memory via MemMapDev class.
|
||
The memory registers will represent the functions of a low level disk
|
||
controller. E.g.: there will be a register to initiate a required data
|
||
access function (read, write, format) and registers for arguments.
|
||
There may be registers for byte-by-byte data transfer with handshaking
|
||
like protocol or a memory address register for DMA transfer mode.
|
||
Internally I will try to emulate Commodore 1541 disk format, compatible with
|
||
popular D64 image.
|
||
|
||
---------------------------------------------- TEXT TEMPLATES:
|
||
|
||
/*
|
||
*--------------------------------------------------------------------
|
||
* Method:
|
||
* Purpose:
|
||
* Arguments:
|
||
* Returns:
|
||
*--------------------------------------------------------------------
|
||
*/
|
||
|
||
/*
|
||
*--------------------------------------------------------------------
|
||
* Project: VM65 - Virtual Machine/CPU emulator programming
|
||
* framework.
|
||
*
|
||
* File:
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Date:
|
||
*
|
||
* Copyright: (C) by Marek Karcz 2016. All rights reserved.
|
||
*
|
||
* Contact: makarcz@yahoo.com
|
||
*
|
||
* License Agreement and Warranty:
|
||
|
||
This software is provided with No Warranty.
|
||
I (Marek Karcz) will not be held responsible for any damage to
|
||
computer systems, data or user's health resulting from use.
|
||
Please proceed responsibly and apply common sense.
|
||
This software is provided in hope that it will be useful.
|
||
It is 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.
|
||
|
||
*--------------------------------------------------------------------
|
||
*/
|
||
|
||
---------------------------------------------- LOG:
|
||
|
||
1/13/2016
|
||
Implemented ORA and ASL.
|
||
Need to test and verify proper functioning of ORA opcodes.
|
||
|
||
1/14/2016
|
||
Tested ORA opcodes.
|
||
Corrected bug in addressing mode: izy = ($00),Y
|
||
|
||
1/15/2016
|
||
Corrected problems with relative jump computation.
|
||
Implemented some new opcodes (PHP, JSR, CLC).
|
||
Optimized some code.
|
||
I rely on Piotr Kowalski's 6502 emulator/debugger to figure out some opcodes implementation.
|
||
This way I found I implemented incorrectly relative jump and also figured out how PC should
|
||
be pushed to stack during JSR.
|
||
|
||
1/18/2016
|
||
Implemented opcodes:
|
||
BIT, ROL, PLP, BMI, SEC, RTI, EOR, LSR.
|
||
|
||
1/21/2016
|
||
Implemented opcodes:
|
||
PHA, BVC, CLI.
|
||
Tested JSR, corrected bugs in JSR, RTI, RTS.
|
||
In process of implementing ADC opcodes (most difficult so far and must consider decimal
|
||
mode as well).
|
||
ADC opcodes implemented, not optimized, not tested.
|
||
|
||
1/22/2016
|
||
ADC being tested and optimized.
|
||
Still unsure how Overflow flag (V) should work.
|
||
I used this algorithm:
|
||
Logic:
|
||
t = A + M + P.C
|
||
P.V = (A.7!=t.7) ? 1:0
|
||
P.N = A.7
|
||
P.Z = (t==0) ? 1:0
|
||
IF (P.D)
|
||
t = bcd(A) + bcd(M) + P.C
|
||
P.C = (t>99) ? 1:0
|
||
ELSE
|
||
P.C = (t>255) ? 1:0
|
||
A = t & 0xFF
|
||
But Kowalski's emulator works differently.
|
||
ADC tested in IMM mode.
|
||
Implemented opcodes today:
|
||
ROR, BVS, BCC, BCS.
|
||
Improvements to debugger (step-by-step added, no need to rely on BRK only).
|
||
|
||
1/25/2016
|
||
Implemented opcodes:
|
||
CLV, TSX, CPY, CMP, DEC, DEX, CLD, CPX.
|
||
Created method MKCpu::SubWithCarry() (helper for SBC implementation).
|
||
To do:
|
||
Implement SBC and re-test ADC and SBC - compare to Kowalski's emulator results and
|
||
also to real MOS 6502 results.
|
||
|
||
|
||
2/2/2016
|
||
Finished implementing opcodes.
|
||
|
||
2/7/2016
|
||
Worknig on multiple steps function in debugger.
|
||
Testing opcodes.
|
||
|
||
2/9/2016
|
||
Testing opcodes, testing BCD mode.
|
||
I use Kowalski's emulator as my reference.
|
||
I fixed many bugs, but still something doesn't work near the end of the procedure (4k+ cycles in).
|
||
Code: testbcd.dat (TestBCD.65s).
|
||
|
||
2/10/2016
|
||
There is still problem with SBC opcode.
|
||
I get different results than Kowalski's emulator.
|
||
Around cycle 4969, address $04b7.
|
||
|
||
17:00
|
||
Still working on SBC opcode.
|
||
I may need to re-invent the whole BCD arithmetic thing and implement full internal BCD arithmetics
|
||
emulation.
|
||
|
||
2/12/2016
|
||
I finally gave up and shamelessly ripped ADC code from frodo emulator.
|
||
It almost works.
|
||
I still get unexpected sign flag result in one of the operations.
|
||
|
||
Frodo code:
|
||
|
||
/*
|
||
* Adc instruction
|
||
*/
|
||
|
||
inline void MOS6502_1541::do_adc(uint8 byte)
|
||
{
|
||
if (!d_flag) {
|
||
uint16 tmp;
|
||
|
||
// Binary mode
|
||
tmp = a + byte + (c_flag ? 1 : 0);
|
||
c_flag = tmp > 0xff;
|
||
v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80);
|
||
z_flag = n_flag = a = tmp;
|
||
|
||
} else {
|
||
uint16 al, ah;
|
||
|
||
// Decimal mode
|
||
al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble
|
||
if (al > 9) al += 6; // BCD fixup for lower nybble
|
||
|
||
ah = (a >> 4) + (byte >> 4); // Calculate upper nybble
|
||
if (al > 0x0f) ah++;
|
||
|
||
z_flag = a + byte + (c_flag ? 1 : 0); // Set flags
|
||
n_flag = ah << 4; // Only highest bit used
|
||
v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80);
|
||
|
||
if (ah > 9) ah += 6; // BCD fixup for upper nybble
|
||
c_flag = ah > 0x0f; // Set carry flag
|
||
a = (ah << 4) | (al & 0x0f); // Compose result
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Sbc instruction
|
||
*/
|
||
|
||
inline void MOS6502_1541::do_sbc(uint8 byte)
|
||
{
|
||
uint16 tmp = a - byte - (c_flag ? 0 : 1);
|
||
|
||
if (!d_flag) {
|
||
|
||
// Binary mode
|
||
c_flag = tmp < 0x100;
|
||
v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
|
||
z_flag = n_flag = a = tmp;
|
||
|
||
} else {
|
||
uint16 al, ah;
|
||
|
||
// Decimal mode
|
||
al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble
|
||
ah = (a >> 4) - (byte >> 4); // Calculate upper nybble
|
||
if (al & 0x10) {
|
||
al -= 6; // BCD fixup for lower nybble
|
||
ah--;
|
||
}
|
||
if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble
|
||
|
||
c_flag = tmp < 0x100; // Set flags
|
||
v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
|
||
z_flag = n_flag = tmp;
|
||
|
||
a = (ah << 4) | (al & 0x0f); // Compose result
|
||
}
|
||
}
|
||
|
||
I guess I am going to rip off the Frodo's code for now for ADC and SBC.
|
||
See what tests results will be.
|
||
|
||
OK, I ripped Frodo code for ADC/SBC and the results are good.
|
||
There is only one inconsistency compared to real 6502, where N-flag returned is wrong.
|
||
|
||
I fixed some minor issues and added help and memory write command.
|
||
|
||
I started to implement emulated console Display, class implementation for now.
|
||
|
||
2/15/2016
|
||
|
||
I/O emulation somewhat works.
|
||
Needs more testing.
|
||
NOTE:
|
||
* Fix BRK opcode (it is really 2-byte opcode, one padding by te must be added, also it alters the stack! - I didn't implement that).
|
||
* I compiled Tiny Basic for my emulator, but it doesn't work.
|
||
* Update help function (new commands were added).
|
||
|
||
17:40
|
||
I fixed BRK opcode.
|
||
I updated help function.
|
||
Corrected bug in I/O emulation (actually it was in memory, the array was too short).
|
||
|
||
Tiny BASIC - I think it doesn't work because it performs RAM test at the beginning to determine
|
||
where RAM ends by writing the memory and reading it back.
|
||
I must implement memory protection scheme so I can define addresses where writing is prohibited.
|
||
I think I will add keywords ROM_BEGIN and ROM_END to the memory definition file, which will define it at startup for now.
|
||
|
||
I also need to eliminate separate ROM image from VM. I will invent a different scheme, perhaps MMU-like.
|
||
|
||
18:25
|
||
Some success with Tiny Basic, but still some issues.
|
||
|
||
2/16/2016
|
||
|
||
0:10
|
||
TB works except it does not print numbers.
|
||
E.g.: program listing shows no line numbers, also unable to print integers.
|
||
This seems to be a problem with my emulator, because the same code works in Kowalski's emulator.
|
||
|
||
19:40
|
||
Some improvements to I/O (display) emulation.
|
||
Still no luck with emulation issue.
|
||
|
||
02/17/2016
|
||
|
||
|
||
$0F2C - RCCHR
|
||
$0F31 - SNDCHR
|
||
|
||
|
||
|
||
LBL010: sta $BD
|
||
stx $BC
|
||
jmp LBL069
|
||
JUMP22: ldx $C1 ; entry point to TBIL PN (print number) $07A7
|
||
lda $01,X
|
||
bpl LBL070
|
||
jsr LBL071
|
||
lda #$2D
|
||
jsr LBL042
|
||
LBL070: jsr LBL035
|
||
LBL069: lda #$1F
|
||
sta $B8
|
||
sta $BA
|
||
lda #$2A
|
||
sta $B9
|
||
sta $BB
|
||
ldx $BC
|
||
ldy $BD
|
||
sec
|
||
LBL072: inc $B8
|
||
txa
|
||
sbc #$10
|
||
tax
|
||
tya
|
||
sbc #$27
|
||
tay
|
||
bcs LBL072
|
||
LBL073: dec $B9
|
||
txa
|
||
adc #$E8
|
||
tax
|
||
tya
|
||
adc #$03
|
||
tay
|
||
bcc LBL073
|
||
txa
|
||
LBL074: sec
|
||
inc $BA
|
||
sbc #$64
|
||
bcs LBL074
|
||
dey
|
||
bpl LBL074
|
||
LBL075: dec $BB
|
||
adc #$0A
|
||
bcc LBL075
|
||
ora #$30
|
||
sta $BC
|
||
lda #$20
|
||
sta $BD
|
||
ldx #$FB
|
||
LBL199: stx $C3
|
||
lda $BD,X
|
||
ora $BD
|
||
cmp #$20
|
||
beq LBL076
|
||
ldy #$30
|
||
sty $BD
|
||
ora $BD
|
||
jsr LBL042
|
||
LBL076: ldx $C3
|
||
inx
|
||
bne LBL199
|
||
rts
|
||
|
||
|
||
4:50
|
||
|
||
Testing reveals that my addressing mode IZX (also called IDX), mnemonic (zpg,X) works incorrectly.
|
||
|
||
05:25
|
||
I corrected IZX addressing mode, the TESTALL.DAT now passes, but Tiny Basic still has issue with printing numbers.
|
||
|
||
17:05
|
||
I corrected addressing modes ZPX and ZPY and Tiny Basic issue with printing integers is corrected. Yeeey!
|
||
|
||
2/19/2016
|
||
|
||
I did some refactoring.
|
||
I still can't decide if I should set SoftIrq flag during BRK in RTI.
|
||
|
||
2/22/2016
|
||
Settled with SoftIrq set while emulating RTI opcode. This way user is returned to debugger console
|
||
when address counter is at address just after return from soft brk instead of in the IRQ routine.
|
||
I also made changes to Tiny Basic source code.
|
||
|
||
2/25/2016
|
||
Started implementing opcodes disassembler.
|
||
|
||
2/26/2016
|
||
Implemented animation for registers status when in multi-step mode.
|
||
Implementing disassembler.
|
||
|
||
2/27/2016
|
||
LINUX port.
|
||
Cont. disassembler implementation.
|
||
Displaying last executed opcode with argument (disassembled) in regs status - completed.
|
||
Added stack display to regs status.
|
||
Trapped all unidentified opcodes (illegal opcodes with undefined behavior).
|
||
|
||
2/28/2016
|
||
Added CTRL-Y key combination trap in I/O input mode.
|
||
Added new keywords to memory image definition file.
|
||
Created tbe.dat image that automatically enables character I/O at $E000 and auto-executes Tiny Basic.
|
||
|
||
2/29/2016
|
||
Added ROm emulation toggle switch to menu and ability to setup ROM address range.
|
||
Added new keywords to memory image definition file.
|
||
Corrected start/run address initialization, tb.dat and tbe.dat files.
|
||
Cosmetic changes to character input.
|
||
|
||
3/1/2016
|
||
Code refactoring.
|
||
Added signal handling to Windows version.
|
||
OPerator can interrupt running program from console (CTRL-C, CTRL-Break) and not only during
|
||
character input - it is not an interrupt/signal handled by OS and customized in code.
|
||
Needs testing on Linux.
|
||
|
||
3/2/2016
|
||
Added non-blocking mode to char I/O input.
|
||
|
||
3/7/2016
|
||
Corrected bugs in BRK, RTI and BIT opcodes.
|
||
EhBasic works!
|
||
The functional 6502 test now fails on ADC/SBC test at $35b5:
|
||
; binary ADC / SBC zp
|
||
3594 : 08 php ;save carry for subtract
|
||
3595 : a50d lda ad1
|
||
3597 : 650e adc ad2 ;perform add
|
||
3599 : 08 php
|
||
359a : c50f cmp adrl ;check result
|
||
trap_ne ;bad result
|
||
359c : d0fe > bne * ;failed not equal (non zero)
|
||
|
||
359e : 68 pla ;check flags
|
||
359f : 29c3 and #$c3 ;mask NV----ZC
|
||
35a1 : c511 cmp adrf
|
||
trap_ne ;bad flags
|
||
35a3 : d0fe > bne * ;failed not equal (non zero)
|
||
|
||
35a5 : 28 plp
|
||
35a6 : 08 php ;save carry for next add
|
||
35a7 : a50d lda ad1
|
||
35a9 : e512 sbc sb2 ;perform subtract
|
||
35ab : 08 php
|
||
35ac : c50f cmp adrl ;check result
|
||
trap_ne ;bad result
|
||
35ae : d0fe > bne * ;failed not equal (non zero)
|
||
|
||
35b0 : 68 pla ;check flags
|
||
35b1 : 29c3 and #$c3 ;mask NV----ZC
|
||
35b3 : c511 cmp adrf
|
||
trap_ne ;bad flags
|
||
35b5 : d0fe > bne * ;failed not equal (non zero)
|
||
|
||
18:05
|
||
Corrected ADC/SBC (setting flags).
|
||
Implemented execute/op-codes history.
|
||
Functional opcodes test passed!
|
||
|
||
3/8/2016
|
||
Added disassembler.
|
||
Refactored code.
|
||
|
||
3/9/2016
|
||
Fixed bugs in memory definition loading function.
|
||
Created tool hex2bin to convert binary file to memory definition.
|
||
Created configuration file for CL65 (CC65 package linker) and modified testall.asm
|
||
to be compiled by CL65/CA65.
|
||
|
||
Improvements to disassembler (now also see opcodes and arguments in hex, not only symbolic form).
|
||
Changes to registers UI appearance (more compact form, better readibility).
|
||
Added date/time to bin2hex tool.
|
||
|
||
3/10/2016
|
||
Improved Display/char IO emulation under Windows (DOS console) - now if the DOS console window
|
||
is wider than emulated display, extra NL is added so the lines are properly aligned.
|
||
Need to implement Linux port and also I need to implement what to do if the DOS/Linux shell
|
||
console width is too narrow for char IO emulated console width.
|
||
Added mingw makefile to compile project with standalone mingw (gcc for Windows) installation
|
||
outside Dev C++ IDE.
|
||
NOTE: I must install mingw at my home PC.
|
||
|
||
3/11/2016
|
||
Added microchess port to my emulator - need to contact Peter Jennings for permission to distribute
|
||
derivative work.
|
||
|
||
3/12/2016
|
||
Received permission from Peter Jennings.
|
||
Improved the Microchess port:
|
||
- the chessboard is not printed after each key press now but only after move
|
||
- echo entered characters
|
||
- copyright banner printed only once at the program start
|
||
Update ReadMe.txt file.
|
||
|
||
3/13/2016
|
||
Improvements to char I/O console emulation.
|
||
Now the width of the terminal/DOS session is detected at startup and the emulated console width
|
||
is adjusted accordingly:
|
||
- For terminal width < 80, the emulated console width is reduced.
|
||
- For terminal width > 80, extra NL character is added while showing the emulated console
|
||
contents (this was added already earlier).
|
||
Above was also implemented for Linux port.
|
||
Operator Interrupt flag - bug corrected.
|
||
|
||
3/14/2016
|
||
Improved performance of char I/O emulation (Display::ShowScr()).
|
||
Changed the way character I/O is emulated a bit.
|
||
In the execute mode now the char I/O from 6502 program is directly translated to stdio
|
||
of the user's DOS/shell console. Shadow copy of text screen is kept in Display device
|
||
so it has the same data. In the step-by-step mode, program is emulating the Display
|
||
by refreshing it on the user's console. Like before user can always call the contents
|
||
of the text display device in debug console by issuing 'T' command.
|
||
|
||
3/17/2016
|
||
Added reset option in debugger console and RESET sequence to the emulation.
|
||
Added RESET keyword to memory definition file.
|
||
Added command line arguments to the emulator. Now can load binary image from command line,
|
||
can also initialize CPU reset from command line.
|
||
Modified ReadMe file. Also added Debugger Console Command Reference section to ReadMe file.
|
||
|
||
4/4/2016
|
||
Implemented VM snapshot save.
|
||
|
||
4/10/2016
|
||
Refactoring (replace huge switch/case with array of pointers to methods).
|
||
|
||
4/11/2016
|
||
Refactoring (replace huge switch/case with array of pointers to methods) - completed.
|
||
IRQ added to API (MKCpu, VMachine).
|
||
|
||
4/12/2016
|
||
Working on cycle accurate emulation - DONE.
|
||
|
||
4/17/2016
|
||
Finished implementing various memory image formats.
|
||
Now program can load binary image, binary image with a header, Intel HEX format,
|
||
my own plain text memory image definition format and save binary snapshot (with header).
|
||
Also, bin2hex utility can now convert binary image (must have no VM65 header) to Intel HEX.
|
||
|
||
4/21/2016
|
||
Minor bug fixed.
|
||
|
||
4/22/2016
|
||
Automatic detection of input memory image file added.
|
||
|
||
5/23/2016
|
||
Adding SDL2 to project.
|
||
|
||
5/24/2016
|
||
Working on Graphic Display emulation.
|
||
|
||
5/26/2016
|
||
Memory mapped device.
|
||
|
||
5/27/2016
|
||
Separate class MemMapDev was a wrong idea.
|
||
Cross references between Memory and MemMapDev make it impractical to use.
|
||
Also it will be difficult to manipulate memory image directly.
|
||
I will move everything to Memory class.
|
||
|
||
5/31/2016
|
||
Working on memory mapped devices implementation.
|
||
|
||
6/1/2016
|
||
I got the memory mapped devices implementation working.
|
||
I fixed the cross references between Memory and MemMapDev classes.
|
||
Right now it is only one device - character I/O.
|
||
Shortly I will add raster display.
|
||
|
||
14:45 - raster display added. I am yet to write a test 6502 program using it
|
||
and also update the memory definition file format with new keywords to enable
|
||
or disable graphics display, setup base address etc. and also the binary
|
||
header format.
|
||
Also I need to create programmers reference guide how to program the device.
|
||
|
||
6/2/2016
|
||
I wrote a short BASIC demo program for graphics display which just draws the
|
||
diagonal line pixel by pixel.
|
||
The graphics display works somewhat, but there are problems.
|
||
The SDL window needs to be continuously refreshed and events need to be read
|
||
in a continuous loop. But the loop of my program stops when in debug
|
||
console and waits for input.
|
||
I cannot run it in a separate thread because I lose the event stream from the
|
||
main application thread. So I read events and update SDL window in handler
|
||
function for the display in MemMapDev class and I only read events in VMachine
|
||
in each step when op-code is executed.
|
||
But if no op-code is executed, the window becomes unresponsive.
|
||
When window becomes unresponsive, killing it kills my emulator.
|
||
A bit messy, I must improve upon this.
|
||
E.g: perhaps I should disable system icons on SDL window (minimize, close).
|
||
|
||
6/6/2016
|
||
Demo program now draws sinusoid and some horizontal and vertical lines.
|
||
I disabled DSL windows's title and system icons.
|
||
|
||
Tried to fix loosing alread drawn pixels when SDL window is resized, but
|
||
I failed (I tried blitting (copying) surface to temporary surface and back).
|
||
|
||
6/8/2016
|
||
Added line drawing capability to Graphics Device emulator.
|
||
|
||
6/9/2016
|
||
Added line erasing capability. Corrected scaling.
|
||
|
||
7/22/2016
|
||
Minor bug fixed.
|
||
Changes related to devices and saved images format.
|
||
|
||
7/24/2016
|
||
Finished code related to new header format, new keywords added to image
|
||
definition file (graphics display device).
|
||
|
||
7/27/2016
|
||
Correction to method VMachine::GetMemoryImageType() (not a bug, just match
|
||
additional keywords in file during file format detection process).
|
||
|
||
8/12/2016
|
||
Experimenting with code performance.
|
||
It turns out the SDL graphics device reduces performance of the emulator
|
||
significantly.
|
||
|
||
8/15/2016
|
||
Concerned about performance I added code measuring the speed of CPU emulation
|
||
using 1 MHz CPU as a reference point.
|
||
In the process I discovered that my Char I/O non-blocking input is not, well
|
||
quite non-blocking. It doesn't require CR, sure, but is still waiting for the
|
||
character to be entered from keyboard.
|
||
Reproducing:
|
||
Start EhBasic interpreter.
|
||
Interrupt to return to Debug Console.
|
||
Resume code (x addr).
|
||
Do not perform any keyboard action or run programs inside EhBasic.
|
||
Interrupt to return to Debug Console.
|
||
|
||
Note # of cycles when no keyboard actions are performed in EhBasic
|
||
is very low and always the same.
|
||
Debugging step by step shows the code stops at $FFC0 (LDA $FFE1).
|
||
|
||
Thus my performance measuring routine shows very low emulation speed.
|
||
|
||
8/16/2016
|
||
I fixed the problem with blocking/non-blocking char I/O.
|
||
Turns out I had a bug in MemMapDev::ReadCharKb() method.
|
||
Slightly optimized memory access methods (specifically memory mapped devices
|
||
iteration). Got a bit of performance boost when emmory mapped devices are
|
||
enabled (still a bottleneck though).
|
||
|
||
8/17/2016
|
||
Further performance optimizations in memory access routines.
|
||
Devices are synchronized (cached) locally to vector member in Memory class.
|
||
It turns out iterating through devices in MemMapDev class and creating
|
||
device object copy locally on stack (in the loop context) was slow.
|
||
Device object is not small enough structure to ignore cost associated with
|
||
object creation.
|
||
I also added an integer array of size equal to number of memory pages (256).
|
||
That array keeps device numbers under the indexes of memory page numbers
|
||
where there is any memory mapped device. Under the indexes of memory page
|
||
numbers where no device is present, the array has -1 value.
|
||
That value is used during memory access to decide if the memory mapped device
|
||
needs to be found and handled or not.
|
||
Note that the device number kept for given page is only one number, but
|
||
there may be more devices on given page. Therefore the values in this array
|
||
can only be used as a Y/N flag (is there device? - value >= 0
|
||
or not - value < 0) rather than the device number to obtain device handler.
|
||
If the value on given memory page is >= 0, then we know we need to query
|
||
devices and call corresponding methods that handle given device register
|
||
access.
|
||
|
||
8/18/2016
|
||
Updating benchmark results, documentation.
|
||
Major optimization in MKCpu::ExecOpcode(). The code disassembling previous
|
||
instruction to history was really slow, because I have been disassembling
|
||
to symbolic form at each op-code execution. I changed the code now in such
|
||
a way that:
|
||
- op-code execute history can be enabled/disabled
|
||
- during op-code execute I only add the last instruction op-code and other
|
||
parameters of the instruction and CPU status to the history, but I do not
|
||
parse/disassemble at this point.
|
||
- the history of op-codes execute is disassembled only when history is
|
||
requested via MKCpu::GetExecHistory().
|
||
The code is now 3 times faster with op-code execute history enabled and
|
||
about 4 times faster with op-code execute history disabled (crude estimate).
|
||
|
||
8/19/2016
|
||
Found and corrected bugs in Memory::AddDevice() and Memory::DeleteDevice().
|
||
Cosmetic changes: removed obsolete code and changes towards better code
|
||
readability.
|
||
|
||
8/22/2016
|
||
Experimenting with performance measuring code.
|
||
Added debug traces and ability to enable/disable perf. stats and debug traces.
|
||
Now the perf. stats are measured in time intervals but not more often than
|
||
predefined number of internal cycles (CPU clock ticks).
|
||
Also added proper PressEnter2Cont() function.
|
||
|
||
8/23/2016
|
||
After recent changes, the 'Last instr.' feature in Debug Console stopped
|
||
working. Since real-time disassembling is not performed now in
|
||
MKCpu::ExecOpcode() method, I have to explicitly call MKCpu::Disassemble()
|
||
to produce data for this feature.
|
||
|
||
8/26/2016
|
||
I want to move Display object from VMachine to MemMapDev. I started
|
||
coding but it is not all done.
|
||
There is experimental code that needs testing.
|
||
11:28
|
||
I almost finished this refactoring. Now Display object is maintained
|
||
inside MemMapDev. VMachine still keeps the pointer to Display object locally
|
||
since it is needed to perform few screen actions in Debug Console.
|
||
Also I found a bug in main.cpp where the ioaddr was not refreshed after
|
||
new memory image was loaded with 'L' command.
|
||
Added multitude of debug messages.
|
||
Removed 'cout' statements from memory image loading LoadMEM() and replaced
|
||
then with debug messages.
|
||
Moved Console IO specific functions from VMachine to a separate class
|
||
ConsoleIO.
|
||
Some testing done, but more testing recommended and pending for these changes.
|
||
Interesting idea:
|
||
- dump the debug log to file each time it is updated DBG_TRACE_SIZE times.
|
||
|
||
8/28/2016
|
||
Found and fixed few bugs in main.cpp (step-by-step execute).
|
||
Few cosmetic changes.
|
||
Documentation updates.
|
||
|
||
9/6/2016
|
||
Cosmetic changes. Documentation updates.
|
||
|
||
9/7/2016
|
||
Final touches before commit.
|
||
|
||
9/8/2016
|
||
I started a little research project if I will be able to do character I/O
|
||
on graphics device. I downloaded c64 character ROM and converted it to
|
||
c654_char.dat. This loads from $B000. I created new version of EhBasic
|
||
eh_basic_mk.asm which has RAM top at $AFFF, so I can use it with the
|
||
character ROM in $B000-$BFFF. EhBasic starts at $C000 as before.
|
||
I compile eh_basic_mk.asm with Kowalski's 6502 emulator built-in assembler
|
||
to pure binary ehbas.65b then convert with bin2hex to ehbas_xx.dat:
|
||
bin2hex -f ehbas.65b -o ehbas_xx.dat -w 49152 -x 49152
|
||
and modify to enable I/O.
|
||
|
||
What needs to happen:
|
||
- graphics device need to support memory mapped character data and text
|
||
mode
|
||
- character I/O routines in EhBasic must point to new char I/O device address.
|
||
|
||
9/9/2016
|
||
OK, the Linux portability (and older pre c++11 compiler compatibility)
|
||
was neglected for a long time, not to mention that I added SDL2 library
|
||
in the meantime. So I started to work on Linux. Code compiles now, but my
|
||
recent changes to character I/O left the char I/O not working on Linux.
|
||
I made some additional changes and hopefully when at home in the evening
|
||
char I/O will work on my Linux box.
|
||
|
||
21:39
|
||
Implemented 1-st working version of bitmap text mode in graphics device.
|
||
Now I am going to check the code to SVN, see if it works in Linux.
|
||
|
||
Tested on Linux - char I/O still not working. And there is a bug in local
|
||
echo mode.
|
||
|
||
9/13/2016
|
||
Wow! Many many hours to finally get it working on Linux.
|
||
I use ncurses for text and SDL2 for graphics.
|
||
Performance on Linux over XWindows is poor, but at least it works.
|
||
Huge refactoring, but better layering as a result.
|
||
Now I need to finish text mode in graphics device and update documentation.
|
||
|
||
9/14/2016
|
||
Working on a text mode in graphics display device.
|
||
To improve performance I added method
|
||
void GraphDisp::RenderChar8x8(unsigned char chdef[8], int x, int y, bool reversed);
|
||
Instead of rendering the 8x8 character definition pixel by pixel (and refreshing
|
||
the SDL surface each time) I now paint the whole character and then refresh the
|
||
surface. The performance boost is significant (at least 3-4 times faster).
|
||
Next step - copy the entire character ROM to the internal buffer of GraphDisp class
|
||
each time the address of character table changes. This way, there will be no more
|
||
VM RAM accesses when character is to be rendered.
|
||
Currently each time I render the character I copy the 8 bytes of character definition
|
||
from memory to internal 8 bytes buffer and then pass it to RenderChar8x8() method.
|
||
20:30 - I have done the internal buffer optimization. Performance was further improved
|
||
by nearly 2 times. Now method GraphDisp::RenderChar8x8() is private.
|
||
I added:
|
||
void PrintChar8x8(int code, int col, int row, bool reversed);
|
||
void CopyCharRom8x8(unsigned char *pchrom);
|
||
The 2-nd one copies characters table to internal buffer in GraphDisp class.
|
||
The 1-st one renders the character using data from internal buffer.
|
||
CopyCharRom8x8() must be called each time when address of character ROM changes.
|
||
Code snippet from MemMapDev class shows how its done:
|
||
[...]
|
||
} else if ((unsigned int)addr == mGraphDispAddr + GRAPHDEVREG_CHRTBL) {
|
||
// set new address of the character table, 2 kB bank #0-31
|
||
mGrDevRegs.mGraphDispChrTbl = (unsigned char)(val & 0x003F);
|
||
mCharTblAddr = mGrDevRegs.mGraphDispChrTbl * ((MAX_8BIT_ADDR+1) / 0x20);
|
||
unsigned char char_rom[CHROM_8x8_SIZE];
|
||
for (unsigned int i=0; i<CHROM_8x8_SIZE; i++) {
|
||
char_rom[i] = mpMem->Peek8bitImg((unsigned short)((mCharTblAddr + i) & 0xFFFF));
|
||
}
|
||
mpGraphDisp->CopyCharRom8x8(char_rom);
|
||
[...]
|
||
|
||
3/21/2017
|
||
Reactivated from long break.
|
||
I just studied the project to refresh my memory.
|
||
Replaced one macro with inline function.
|
||
|
||
3/22/2017
|
||
A small refactoring in main.cpp (replaced big if/else branch statement with switch/case).
|
||
|
||
7/28/2017
|
||
Update github repository.
|
||
|
||
5/9/2022
|
||
I have upgraded to MINGW 8.1.0
|
||
|