6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu May 02, 2024 5:30 am

All times are UTC




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Wed Feb 05, 2020 10:23 pm 
Offline

Joined: Wed Feb 05, 2020 9:35 pm
Posts: 5
Location: Nantes (France)
Hi!
This is my first post here after I read so much of this site documentations and tutorials.

The story
I am 45, working in the IT industry for too long, mostly in web development stuff.
A couple of months ago, a friend of mine sent me a link saying « check that youtube video, I think you'll like it », it was the first of Ben Eaters' videos about how to do a "hello world" starting with a WDC 65C02 chip. I bought a kit. Of course it did not (and still not) work properly, but with my son (9yo) we made the project to create a LED table to play games (we would code) on that computer we were building. I started to think about the tool we would need to actually debug programs coded with VASM (I am terrible at assembly). So I took a piece of paper (aka opened a Vim session) and wrote I wanted an interactive & friendly CLI environment capable of loading programs in memory, executing instructions step by step or until a condition is met, being able to disassemble, show memory or registers at any point. Once written down my wish list, I also decided it was time to learn the Rust programming language (I am terrible at C either).

What.a.journey!

I must say “thank you” for all the documentation / soft you people have made. Thank you for the 65C02 functional test, it sounds soft65C02 is passing the tests, it was a good way to test if the CLI was useful. I also coupled it with an optional frame buffer. It is very « alpha » release for now but it starts looking good (here is a recorded short session).
Here is a link to the Github page. It is a pet project under GPL license.

Regards,
Greg


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 5:22 am 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
Sounds like we are in certain areas going in a similar direction! I too am fairly terrible at many of the details of assembly (off-by-one errors are my bane) and needed an environment to help out with this. And it would be CLI, of course, because that's the kind of guy I am.

As with you, an emulator running on the cross-development system is a key component for me, but rather than focusing on debugging I choose to go the unit test route. (My experience of the past twenty years doing unit testing has shown me that if you've got good unit tests you need very little in the way of debugging support. I even use unit tests in preference to a REPL most of the time.) I choose to use pytest for unit tests (because it's one of the best unit test frameworks I've ever used) and that led to using the py65 simulator to run the tests. I intend to extend this to other CPUs as well, probably 6809 next and 8080/Z80 after that.

I've not "productized" my system yet, but it seems to be starting to become stable. You can see and play with the code in my 8bitdev repo if you feel adventurous. I'll probably be splitting out the test framework (and probably some of the build support for Macroassembler AS and ASxxxx) later this year and creating some introductory documentation and a tutorial. But if you have any questions before then, feel free to get in touch. (PMs here, email, whatever.)

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 7:38 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Welcome Greg, looks great, thanks for publishing and for sharing the animated CLI session. You mention the framebuffer - I'm guessing that's modelled from the 6502 perspective but there's no UI? I'd be hopeful that Rust + SDL would deliver a portable graphical interface, but that might be heaps of work.


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 9:22 am 
Offline

Joined: Wed Feb 05, 2020 9:35 pm
Posts: 5
Location: Nantes (France)
Hello,

BigEd wrote:
Welcome Greg, looks great, thanks for publishing and for sharing the animated CLI session. You mention the framebuffer - I'm guessing that's modelled from the 6502 perspective but there's no UI? I'd be hopeful that Rust + SDL would deliver a portable graphical interface, but that might be heaps of work.


When you add the framebuffer memory subsystem using by example a “memory sub add #0x0200 minifb” command, a 128×96 (magnified × 4) window pops out and shows the content of the video memory at 2 pixels per byte. It uses the minifb crate which afaik is fairly portable. I made it to run in a separate thread to be able to refresh itself and hopefully to send interrupts (that's my WIP right now). The 65C02 functional test is still working with a video memory starting at 0x0000, one can actually see the parts of the memory the test uses (mainly the first bytes of the zero page and the stack).

The idea behind this memory submodule mechanism is to implement a 6522 to build other blocks and be able to set this kind of component everywhere we want in the memory map.

I had a look at the SDL crate and yes, it would make a decent software video card with real palette, scrolling, maybe zooming & rotations but that's a bit more work to re-create a 8 bits game machine ;)

cjs wrote:
As with you, an emulator running on the cross-development system is a key component for me, but rather than focusing on debugging I choose to go the unit test route. (My experience of the past twenty years doing unit testing has shown me that if you've got good unit tests you need very little in the way of debugging support. I even use unit tests in preference to a REPL most of the time.) I choose to use pytest for unit tests (because it's one of the best unit test frameworks I've ever used) and that led to using the py65 simulator to run the tests. I intend to extend this to other CPUs as well, probably 6809 next and 8080/Z80 after that.


I am fully with you, Soft65C02 is heavily unit tested (more than 250 unit tests and 1k assertions). I discovered that Rust encouraged the implementation of unit tests directly as submodules of the tested modules, making easy to test private implementations and in the same time, compiling the test code only when launching tests suite. I found your idea was great and imho necessary as soon as you want to create decent software but the purpose of soft65C02 is to « see to understand » what's going on under the hood. When I read ASM code, I see a lot of patterns and I do not get how (hence why) they work. Making visible the inner machinery for me to be able to comprehend how programs work was the goal.

Regards,
Greg


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 2:23 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1400
Location: Scotland
chanmix51 wrote:
I had a look at the SDL crate and yes, it would make a decent software video card with real palette, scrolling, maybe zooming & rotations but that's a bit more work to re-create a 8 bits game machine ;)


FWIW: My Ruby 6502/816 SBC system currently sends all graphics (and text) out via a 115200 baud serial link to my Linux desktop to a terminal program that can either be minicom for pure text or my own terminal program which uses SDL for text and graphics. The commands sent over the serial link are identical to the "VDU" codes used in the BBC Micro (plus a few more, so circles and sprites as well as points, lines and triangles)

This might be an idea for your simulator - arrange the process running the 6502 code to implement a pipe of sorts to another process doing the graphics?

A video if it in action: https://www.youtube.com/watch?v=rPGCT0lah4Q

Each circle there is generated by sending a plot code, then the circle code, then 2 bytes for X and another 2 for Y, so 6 bytes in total, then the graphics engine does the actual circle plotting.

I'm just using SDL 1.3, so my video card is not really doing zoom/rotate stuff, although it could. It's actually the graphics back-end from a BASIC interpreter I wrote to run under Linux some years back. Eventually I'll be using a 6522 to pump data directly into a little $5 SBC with HDMI output to work as it's "graphics card".

However if you're into Rust, then the minifb thing might be more to your linking, but the principle of separating the display process from the simulated 6502 (or a real on in my case) is fine and can work well.

Cheers,

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 3:24 pm 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
chanmix51 wrote:
I am fully with you, Soft65C02 is heavily unit tested (more than 250 unit tests and 1k assertions). I discovered that Rust encouraged the implementation of unit tests directly as submodules of the tested modules....

Sorry, I wasn't clear. While of course my framework has unit tests for the framework itself as well, the purpose of the framework is to unit test the 6502 code you write. So, for example, I have a 6502 routine called bi_readhex which reads an ASCII string representing an arbitrary-precision integer, and converts it to native binary representation. One set of tests for it is:
Code:
#   Buffers used for testing deliberately cross page boundaries.
INBUF  = 0x6FFE
OUTBUF = 0x71FE

@pytest.mark.parametrize('input, bytes', [
    (b"5",               [0x05]),
    (b'67',              [0x067]),
    (b'89A',             [0x08, 0x9A]),
    (b'fedc',            [0xFE, 0xDC]),
    (b'fedcb',           [0x0F, 0xED, 0xCB]),
    (b"80000",           [0x08, 0x00, 0x00]),
    (b"0",               [0x00]),
    (b"00000000",        [0x00]),
    (b"087",             [0x87]),
    (b"00000087",        [0x87]),
])
def test_bi_readhex(M, input, bytes):
    print('bi_readhex:', input, type(input), bytes)
    S = M.symtab

    M.deposit(INBUF, input)
    M.depword(S.buf0ptr, INBUF)
    M.depword(S.buf1ptr, OUTBUF)
    size = len(bytes) + 2               # length byte + value + guard byte
    M.deposit(OUTBUF, [222] * size)     # 222 ensures any 0s really were written

    M.call(S.bi_readhex, R(a=len(input)))
    bvalue = M.bytes(OUTBUF+1, len(bytes))
    assert (len(bytes),     bytes,  222,) \
        == (M.byte(OUTBUF), bvalue, M.byte(OUTBUF+size-1))

Here M is the "machine," the emulated system on which I'm running the 6502 code, S.xxxx are symbol lookups (from the symbol table output of the assembler), and R is the constructor for an object representing some or all of the 6502 registers. M.call can run for a given number of instructions, to a given instruction, and throws an exception if any instruction from a given list is executed (the default list is just BRK). If an assertion fails, it prints out the expected and actual values, for register sets printing them more or less the way you do except with a hyphen for registers or flags that you're ignoring in the comparison.

So basically I'm doing the same kind of thing you're doing in your command interpreter, but specified as unit tests that can easily be re-run. The reason I find little need for an interactive command interpreter is that I can just take what would be typed as commands, write a unit test with those commands instead, and then run the test. (Or, more often, just tweak an existing test.) So long as the test fails (easily achieved by adding an assert 0 at the end) the stdout/stderr output will be printed, too, so you can just add print() statements to dump bits of memory at any point you like if you need to see what's going on. (In the test above this is the purpose of the print() at the start; you won't see its output if the test passes, but if the test fails it gives you some useful debugging information.)

(And I just noticed I'm forgetting to check the guard byte before the buffer above; oops! I really need to add something to automatically add guard bytes to buffers and check them.)

Quote:
...but the purpose of soft65C02 is to « see to understand » what's going on under the hood.

Right. My way is a bit slower than than the interactive method if you're at a specific point in a program and you want to do a lot of exploring, but it's a lot faster if you need to just see "what happened at this point in the run," and "what happened at that point"; having the test system re-run the code and print at the appropriate points is a lot easier than setting up data and running your 6502 routine it over and over again by hand. And of course you come out at the end with a set of unit tests, too.

That said, I've considered adding a "drop into an interactive monitor" feature to the system so that at any point in a test you could stop execution and then interactively explore. I've just not felt the need for that yet.

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 3:29 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
chanmix51 wrote:
... When you add the framebuffer memory subsystem using by example a “memory sub add #0x0200 minifb” command, a 128×96 (magnified × 4) window pops out and shows the content of the video memory at 2 pixels per byte. It uses the minifb crate which afaik is fairly portable.

Ah - that's great! I hadn't understood. (Not quite working for me on my Mac, but that could be anything. No window pops up.)

Code:
  git clone https://github.com/chanmix51/soft65c02.git
  cd soft65c02/
  sudo port install rust cargo
  cargo build
  ./target/debug/soft65c02

Code:
Welcome in Soft-65C02 version 1.0.0-alpha2
No previous history.
>> memory sub add #0x0200 minifb
>> 2020-02-06 09:41:18.164 soft65c02[10665:444500] Names (null)


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 5:09 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8169
Location: Midwestern USA
chanmix51 wrote:
This is my first post here after I read so much of this site documentations and tutorials.

Welcome to 6502 land.

Quote:
...it was the first of Ben Eaters' videos about how to do a "hello world" starting with a WDC 65C02 chip. I bought a kit. Of course it did not (and still not) work properly...

Can you describe in some detail what is not working about it? Perhaps we can help you debug the device and get it running. Simulators are fun (I use one now and then to test algorithms) but working with actual hardware is more fun, especially if it is something you built.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 5:35 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
(But in a new thread please, if so!)


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 06, 2020 8:41 pm 
Offline

Joined: Wed Feb 05, 2020 9:35 pm
Posts: 5
Location: Nantes (France)
Good evening,

Sorry to read the framebuffer is not working properly on OSX :? I think I need to let this idea to sink for a moment, I need a graphical output but I also need to understand how such thinks are handled “in real life” to choose a “virtual” implementation. I secretly wished I could code something funny like a fire or something like that (I need a random generator…)

cjs wrote:
Quote:
...but the purpose of soft65C02 is to « see to understand » what's going on under the hood.

Right. My way is a bit slower than than the interactive method if you're at a specific point in a program and you want to do a lot of exploring, but it's a lot faster if you need to just see "what happened at this point in the run," and "what happened at that point"; having the test system re-run the code and print at the appropriate points is a lot easier than setting up data and running your 6502 routine it over and over again by hand. And of course you come out at the end with a set of unit tests, too.


Yes, there are two different kind of tools. Unit tests have to be automated and as clear as possible when something is not as expected. The debugger is here to show you what went like our biased mind did not expect. Especially useful for newcomers like me who are trying to guess the behaviour of the different opcodes.

BigDumbDinosaur wrote:
Quote:
...it was the first of Ben Eaters' videos about how to do a "hello world" starting with a WDC 65C02 chip. I bought a kit. Of course it did not (and still not) work properly...

Can you describe in some detail what is not working about it? Perhaps we can help you debug the device and get it running. Simulators are fun (I use one now and then to test algorithms) but working with actual hardware is more fun, especially if it is something you built.


Yes … I suspect the wiring … I will post something on a dedicated thread about this.

Regards,
Greg


Top
 Profile  
Reply with quote  
PostPosted: Fri Feb 07, 2020 8:33 am 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
chanmix51 wrote:
Yes, there are two different kind of tools. Unit tests have to be automated and as clear as possible when something is not as expected. The debugger is here to show you what went like our biased mind did not expect. Especially useful for newcomers like me who are trying to guess the behaviour of the different opcodes.

Well, no. A debugger is certainly a very different thing than a unit test system, but much less capable; my unit test system can provide 90% or more of the capabilities of a debugger, but a debugger provides only a small fraction of the capabilities of a unit test system.

With a bit of creativity, a unit test system is a great way to do things like figure out the behaviour of different opcodes. For example, when I was trying to understand how signed vs. unsigned arithmetic and the N, V, C, and Z flags on the 6502 worked, I wrote arithmetic_test.py. This was faster and easier than doing this in a debugger, and also left me with a detailed, fully commented record of how this works and what I tried, one that I can go back to at any time.

The reason that debuggers were much more common and popular in the 8-bit days was that a debugger is a much smaller and easier thing to write. These days, when you have most of the heavy lifting already done for you (a full high-level lanugage interpreter such as Python, a unit test framework for it, and a 6502 simulator), it's quite practical to go for a unit-test system instead, and use that to debug code as well.

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 09, 2020 7:52 pm 
Offline

Joined: Wed Feb 05, 2020 9:35 pm
Posts: 5
Location: Nantes (France)
Hi

cjs wrote:
The reason that debuggers were much more common and popular in the 8-bit days was that a debugger is a much smaller and easier thing to write. These days, when you have most of the heavy lifting already done for you (a full high-level lanugage interpreter such as Python, a unit test framework for it, and a 6502 simulator), it's quite practical to go for a unit-test system instead, and use that to debug code as well.


This made me think because I really like your approach and I felt that in its actual state, Soft65C02 could do both: be an interactive debugger and a testing tool. Since Soft65C02 has a CLI application that takes commands on the standard input, it is de facto a scriptable tool, I added the functions assert and memory write so now, I can write the following test cases in a text file:

Code:
// myTestCase.soft-65c02
memory load #0x8000 "program.bin"
run #0x8000 until CP=0x801a       // start of the program loop
assert A=0x00                // Accumulator is 0
assert #0x0020 = 0x10   
assert #0x0021 = 0x1C  // pointer is initialized
assert #0x1C10 = 0x21  // the pixel is the right color
run until CP=0x801a
assert #0x0020 = 0x10   
assert #0x0021 = 0x1C  // pointer is unchanged
memory write 0x0000 0x(21) // key X is pressed
run until CP=0x801a
assert #0x0020 = 0x11   
assert #0x0021 = 0x1C  // pointer is updated
assert #0x1C10 = 0x00  // this pixel is black
assert #0x1C11 = 0x21  // this pixel is not


and play it with
Code:
./soft65C02 < myTestCase.soft-65c02
The program would exit with an error code if an assertion fails.

Regarding unit tests for subroutines, the best way would be to directly use Soft65C02 library in Rust and use the tooling of the language. I find that providing the right methods for unit testing is a good way to determine what the lib should expose. As of today, everything is already implemented, that's just re-arranging the code. I will work on that the next coming days.

Regards,
Greg


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 06, 2020 11:23 am 
Offline

Joined: Wed Feb 05, 2020 9:35 pm
Posts: 5
Location: Nantes (France)
Hello,

In order to test the physical implementation of my breadboards, I coded a simple program that write into zero page then use these bytes to write in other parts of the memory using zero page indirect addressing mode. I tested the program with soft65C02, it made me able to spot several bugs and then ran in on the physical processor, following the execution with the arduino board. The physical board starts ok then the execution differs after some iterations. It came out to me that the real processor was not initialized the same way the simulator was. So I looked into the documentation and, yes, the stack pointer is not initialized but also the status flags … here is my problem:

The documentation only states (p.10 3.1 reset) that registers and status flags must be initialized by software (except 4 flags: - B d and I). So, as a simulator, what should the registers be like and init ? all 0x00 except CP anf status?

Regards,
greg


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 06, 2020 12:06 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
I think it's up to you, or to whoever writes the emulator. (One tactic which can be used to shake out bugs is to initialise randomly.)


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 06, 2020 1:57 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
On a CMOS CPU, the I flag will be set and the D flag will be cleared on entry to any interrupt handler (IRQ, NMI, BRK, RST). The NVZC flags' state and the contents of all other registers are unpredictable (unchanged from whatever they were previously, which is essentially random on RST), except for the PC which is initialised from the vector table. The B "flag" doesn't physically exist in the status register, and is only valid in the copy of the status register pushed during the interrupt; it indicates only whether it was a BRK or not.

The above is also true in NMOS CPUs, except that the D bit is also unchanged by the interrupt, and should therefore be considered unpredictable on at least RST. If you can guarantee that D is never changed after being initialised in the RST handler, then you can avoid setting it again in IRQ/NMI handlers.

On the '816, behaviour is identical to the CMOS CPU, with the addition of the M, X and E bits being set upon RST (forcing 65SC02 emulation mode, 8-bit accumulator, and 8-bit index registers), and the DBR, PBR and DPR being zeroed. Hence on RST, the '816 can reliably be used as if it were a 65SC02, provided the opcodes undefined on the later are avoided. The PBR is also zeroed when taking any interrupt, as the vector table contains only 16-bit addresses. A different vector table is used when in Native mode than in Emulation mode, so you can assume the mode on that basis.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: