So here it goes.
The project, due to its scope, is currently at concept stage (no actual code has been written yet).
The idea is to create a "universal" emulator for (fantasy or not) microcomputers. EVC would be fully customizable by loading specific modules for things like CPU, GPU and so on that are basically DLLs written to specific specs. Example cores would be included and entirety would be open source.
There are two types of modules that you would be able to write for the EVC: Core and Generic. Core modules are fixed by their role (GPU module, CPU module and so on). This is planned list for the core modules:
- CPU module
- GPU module
- Drive I/O module
- Peripheral I/O module
- Sound module
- System ROM (firmware)
Last one is kinda special as this would be just a regular EVC ROM compatible with the hardware selected for i/o, gpu and cpu and is executed like any program. System ROM will always be loaded at address 0.
The Generic modules are additional extensions that can be loaded for doing specialized things, such as network communication, ram extenders (if maximum is too low) and so on. Up to 3 Generic Modules can be connected at the same time.
Both Core and Generic modules will have read/write access to the EVC's basic RAM which can be set to anywhere betweem 16kb and 2mb.
EVC ROM specification.
Each EVC rom begins with the EVC header which consists of the following:
32bit integer denoting CPU identifier the rom is made for
32bit integer denoting GPU identifier the rom is made for
32bit integer denoting Sound module identifier the rom is made for
32bit integer denoting Disk I/O module identifier the rom is made for
32bit integer denoting Peripheral I/O module identifier the rom is made for
32bit integer denoting Generic Module #1 identifier required (-1 if less than 1 needed)
32bit integer denoting Generic Module #2 identifier required (-1 if less than 2 needed)
32bit integer denoting Generic Module #3 identifier required (-1 if less than 3 needed)
The rest of the ROM is completely arbitrary and rom size isn't limited in any way. It is up to I/O module to interpret it and load properly. In theory, it should be possible to create set of core and extension modules to emulate say, NES, but then every ROM would need to be converted to contain EVC header before standard INES one.
EVC will be written in Free Pascal using Lazarus IDE
That's all I got for now, please poke holes in this design as it's important to get it as good as it can be before any actual work would start.
Have you considered that what you are describing is a very intentionally limited language runtime? Though it doesn't specify:
- A particular machine or intermediate language.
- Standard library.
- Graphics library.
- Sound library.
I sort of feel like what you want is to make something like Pico8, but without the limitations. What I don't understand is why you are building a system with limitations that lets you build systems without limitations? Put another way, why would I want to implement Micro16 in EVC instead of just writing it in C when the latter sounds way easier?
Implementing a real emulator on top of that (like an NES) is going to need architectural level support for things like interrupts of various kinds, memory mapped I/O, etc. I think you might be in for more than you expect if you really want to do that. On the other hand, they will be more or less useless for something like Pico8, which celebrates the feel of classic consoles and not the nitty gritty (and honestly, not so fun) minutia of their actual hardware.
|Have you considered that what you are describing is a very intentionally limited language runtime? Though it doesn't specify:|
1 A particular machine or intermediate language.
2 Standard library.
3 Graphics library.
4 Sound library.
1 Up to loaded CPU module, how it will interpret what's in the RAM.
2 Up to System ROM.
3 Up to loaded GPU module
4 Up to loaded Sound module.
//edit: regarding interrupts: It's CPU module's job to implement them as those "modules" are equivalent to pieces of hardware you put, say, into the computer you're using right now. So it's up to CPU module to manage registers, stack and program counter.
The thing about EVC is that, based on the modules that are loaded in, it can do whatever you want it to do as long as you've installed "right" module DLLs ;).
The tricky part will be implementing flexible API for writing said modules.
But I can understand how this might be confusing since such thing wasn't really done before.
No, OpenEMU/RetroArch/Mame/MESS doesn't count since they have per-system hacks in the core itself.
EVC, on the other hand, will be fully modular. The core EXE will be minimal and only would execute module DLLs (which basically are just fancy named plugins). Again, the tricky part will be designing easy API for writing these and best way to communicate with those (communication between plugins will be done through RAM by writing/reading certain values to/from it).
MAME/MESS/etc. are like monolithic kernel. They keep most of the stuff in the main "blob" (let me tell you, MESS' name is spot on when we're talking about its code) which leaves very little for actual modules to do.
EVC on the other hand is more akin to microkernel architecture - base EVC executable does only loading modules and base communication with them (for example telling Disk I/O module to load specific ROM into RAM then leaving it to do its job or retrieving screen bitmap from GPU module). It's fully modular is what I'm saying and exact limitations it has depends on the list of Core and Generic/Extension modules that are loaded in it.
I'd encourage you to test out your process with a very simple "computer". Maybe something like a calculator? It has only a handful of instructions, a single beep of sound, and some text output.
Should be doable a lot quicker than the full thing?
Will really give you some motivation to tackle the larger problem.
This still sounds strangely like a runtime for runtimes on top of the Free Pascal runtime. If making a runtime sounds like a fun project, then go for it. If making a runtime for runtimes sounds like fun, then do that I guess?
At least the implementation is simple enough:
rom = readFromFile(romFile) mem = alloc(ramAmount) dlopen(cpuModule) cpuInit() dlopen(gpuModule) gpuInit() ... cpuRun(rom, ram)
@SunSailor: That's awesome! Perhaps in future when it's ready there could be set of Core Modules that emulate Nano89?
@matt: Well, such simple set of cores will be a first thing I'll do for EVC to test if it all works. I don't want to waste my "coding juice" to make a blob that won't further making EVC a thing and create it as a separate thing though.
@slembcke That kinda what it is ;).
@BenMcLean: Perhaps, but I want to do my own thing.
Some more design thoughts:
The core runtime will be based on the concept of "ticks". Each tick will be 1/60 of a second and each of the modules (esp. CPU and GPU modules) will be able to set how many times they want to be "updated" per tick, to achieve certain frequency. For example, if a cpu module is supposed to have roughly 1MHz clock, the proper value for updates per tick would be 16667.
Graphics will be double buffered and GPU module will have access to internal bitmap that will have to be returned to the EVC after GPU update(s). I'm not linking it to specific area of EVC RAM (dictated by GPU module or not) for a reason: I want it to be flexible so pretty much any type of display can be emulated. From G&W-like LCD displays through vector to pixel display. For example, in case of calculator someone mentioned, the "display" would be calculator-like number display which in EVC's ram would be represented by few bytes.
A would not recommand a tick of 1/60s if you want to achieve 60fps.
With 60fps, 1/60 tick per second == 1 tick per frame, so you have to forget all the things that could happen during that time, like midscan display changes, rasters, etc...
Same for the sound.
For exemple a propose NES emulator run at the bare mlinimum of ~1/(260*60) tick per second aka 15.6Khz (for a NTSC emulator (260 is rougly the number of scanline) and if you want really accurate emulation, you will need to tick on the pixel level an not the scanline level.
The most precise emulators are tick console main clock: ~21.477272 Mhz for NTSC an ~26.601712 for PAL emulators then each part of the emulator will have it's own tick counter based on the master frequency.
You need precise running between each modules, and on a frame basis, it's clearly not enough for really precise things.
No, I've meant one tick takes 1/60 of a second.
Also, CPU/GPU/etc. modules would be able to say how many times they want to be updated per tick. So "tick loop" would look like this (pseudocode):
while (running) for i=1 to CPUMaxUpdates updatecpu(); end for for i=1 to GPUMaxUpdates updategpu(); end for for i=1 to DiskIOMaxUpdates updatediskio(); end for for i=1 to PeripheralIOMaxUpdates updateperipheralio(); end for waitfornexttick() end while
As for the sound, sound module will have direct access to whatever sound library I'll decide to use as a backend's sound buffer that will be writable directly by it and sound will be updated constantly in a separate thread (or in main thread but constantly outside the tick loop).
Do you have better ideas how it should be designed? Seriously, this thread exist almost purely (the other 2% is to gauge interest in such thing) to iron out any design bugs BEFORE I'll start coding the thing.
Obviously I want it to be modular so I don't want in the main runtime exe any hacks specific to certain modules.
Here's a Space Invaders game in Blitz Basic.
[Please log in to post a comment]