6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 3:49 am

All times are UTC




Post new topic Reply to topic  [ 2 posts ] 
Author Message
 Post subject: 65816 Emulator in C
PostPosted: Sat Nov 18, 2023 6:43 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
Hi everyone!

This is a little project i've been working on for the past month or two.
It mainly started because i wanted to try working on a small Operating System but do it all the work on my PC for easy of development.
So i started looking for some customizable 65816 Simulators/Emulators and couldn't really find much. i did find "lib65816" but the github is very bare, not even mentioning or explaining how to use it and what functions it has... and i wasn't able to find any other documentation about it that wasn't some long dead link.

so i though, "screw it i'll just do it myself", and this is the result of that.

while it is currently very much functional (it runs all programs i made for my SBC with no issues), it is not "complete".
both MVP and MVN are not implemented because i never use them anyways, Decimal mode is a bit broken (SBC specifically, ADC works fine, though in both cases the V flag is sometimes wrong and sometimes correct).

for testing the Emulator i used both my own SBC programs and this test suite made out of 20k test cases for each instruction. basically every test defines an initial state (randomized registers) and what the resulting state should be (including memory).
my Emulator passes most tests except those of the issues mentioned above, plus WAI fails even though it works perfectly fine, same with XCE (according to the test when both e and c are set, executing XCE should clear c, which makes no sense to me as it should just swap the values, and swapping 1 with 1 should result in 1 and 1)

anyways, to the actual Emulator.

like lib65816, it's a library plus a header that you simply link along side your program. the source code of the Emulator itself is all just a single source file and seperate header file.
all relavenent parts of the CPU are kept in a Struct (64 Bytes large on a x86_64 System), there are no global variables or similar to allow users to create multi-processor designs (or just run the same system multiple times in parallel).

there are only 2 actual functions:

"cpuInit", which initializes a CPU Struct, including a pointer to memory, an integer specifying how much memory the system has (starting from $000000), where the IO region is located in memory and how large it is, and finally 2 pointers to user supplied functions that handle IO Reads and Writes.

"cpuExecute", executes instructions for a given CPU struct and amount of cycles, it returns the difference between how many cycles were requested and how many it actually executed.

some more detail about the IO handler functions, the read function takes a single uint32_t input for the address, and returns a uint8_t value. the write function takes a uint32_t address and uint8_t value, and returns void.
the most basic IO functions would look like this:
Code:
uint8_t readIO(uint32_t addr){
   return 0;
}

void writeIO(uint32_t addr, uint8_t val){
   return;
}

note that the address the functions get is relative to the start of the IO region.
for example if you specify IO to start at $01FF00 then when the CPU writes to $01FF04 (size of 256 Bytes), the IO write function would be given the address $000004.
another note, ALL memory accesses are being checked to see if they are within the IO region, including instruction fetches.

next up, interrupts. they do work (except for Abort), you simply call "cpuSendIRQ" or "cpuSendNMI", and next time you call "cpuExecute" it will start handling it. it's good enough to be used for timers and such.
WAI works by simply exiting the "cpuExecute" function, and trying to call it while WAI is still "active" will return immediately again. WAI is only cleared after sending an interrupt or by re-initializing the CPU struct.
STP works similarly, but can only be cleared by re-initializing the CPU struct.
additionally WAI and STP can be tested for by calling chkWAI() and chkSTP(), given a CPU struct of course.

.

To get a basic system running, you roughly need to:
  • Declare/Define your IO Handling functions
  • Declare a "cpuState" variable (which is the CPU struct)
  • Allocate the System's Memory (or use an array)
  • Load your ROM/binary file into Memory
  • Pass it all (plus some values for Memory Size and IO location and Size) to the "cpuInit" function
  • and then just start running the "cpuExecute" function, handling WAI and STP if they come along, sending Interrupts, etc.
  • And that should be it, congrats!

.

Anyways, that's pretty much it. Here the link to the Github repo containing the Source files and instructions on how to compile everything.
It also has a small example setup with built-in File IO capabilities and a timer/periodic IRQ (to the emulated CPU the timer goes in steps of 10ms, but it will usually run faster), it takes the first commandline argument as a path to a binary file to load before executing it.

may somone find use in this, it took me actual days to write that documentation on Github (most of that time was spend procrastinating, aka working on other projects)


Top
 Profile  
Reply with quote  
 Post subject: Re: 65816 Emulator in C
PostPosted: Sat Nov 18, 2023 7:08 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
Sounds good to me! And thanks for finding and linking that validation suite. Nice thing about that is we can check it on real 816.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 11 guests


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: