kc5tja wrote:
Quote:
The basic idea is to run one component without interruption for as long as possible. Things that require an interruption include interaction with another component or another component's interaction with the one running. As long as you can predict these interactions in advance for at least all but one component, then it works.
Except you can't predict this. Ever.
Actually you can. VICE does it this way. It has an internal "alarm" feature that interrupts CPU emulation whenever a device needs to change its internal state, but in between the CPU can run undisturbed. Alarms for example are rasterlines, VIA/CIA timer events etc.
Quote:
The emulator has no knowledge of what my software is going to do. Nor when. Bugs could cause the video fetch pointer to be altered unpredictably, etc. Demo coders are *KNOWN* for doing things unpredictably.
When the CPU updates any of the registers (be it CRTC, VIA, CIA or anything else), the internal alarm queue (yes, actually a queue not a single point in time value) is updated for the chip. For example if the CPU sets VIA timer to expire in 1000 cycles, the alarm is set to 1000 cycles from this point. If the CPU happens to modify the timer again after 900 cycles, then the alarm value for the VIA is updated and requeued into the alarm queue.
Quote:
Quote:
As an example, the graphics chip's behavior should be fairly easily predictable in advance, given that nothing else disturbs it. The CPU's
Except that, by its nature, the CPU is seemingly always disturbing it!!
What is a graphics hardware worth if it keeps the CPU busy 100%. That's, well, not efficient.
Quote:
Quote:
So you run the CPU non-stop until it either interacts with another component, or another component would generate an interrupt. If the CPU interacts with the graphics chip, suspend CPU emulation just before the interaction, run the graphics chip to the present CPU time,
There is a problem with this. To fetch data from RAM, the video chip has a DMA channel with an address register. The DMA channel's address register is the only thing that determines what data is fetched. And, it only increments (to keep the hardware the simplest possible). It never resets to any base value under hardware control.
What this means is, if not touched by the CPU, that counter will eventually cycle through all 16MB of the CPU's address space.
So, what you're proposing is that whenever the CPU writes to RAM at an address >= the current video fetch pointer, then it ought to kick off an MGIA catch-up. But, what if it writes it megabytes above the current video frame data? Every write access, then, would result in an MGIA catch up.
VICE for example uses (IIRC) a block-based pointer table for memory accesses. If the address is occupied by memory, reads and writes directly go to memory. Otherwise a special function is called (like "read_io(ADDRESS char)").
For your DMA, you could have block-based alarms (i.e. every time the DMA crosses a page boundary trigger an alarm) and the alarm routine could replace the pointer to memory with a function pointer for only that page that it actually accesses, and restore the old one for the page it just left. The function could then automatically catch up with DMA, yet only when it happens in the very same page.
Quote:
Another issue: pretend that we disable (raster) interrupts, thus preventing the reload of the video fetch pointer, and enter into a tight loop:
Code:
STI
a: JMP a
This will hard-lock the computer. Yet, the display now should continuously reflect the state of the (now forever incrementing) video fetch pointer. Therefore, the CPU emulation code still needs to be interrupted at regular intervals to allow this to happen.
How can this be integrated into the system?
Exactly how VICE does this for example. Every opcode fetch compares the current cycle time with the low end of the alarm queue, which is a single compare and quite fast. If the alarm happened during the last opcode (or is about to happen during the next one, don't remember exactly) the alarm routine is called.
In fact each "write_*()" function has access to the maincpu clock value (the "maincpu_clk" variable in VICE represents the clock cycle of the opcode fetch, but the routine gets an offset to this as well) so that cycle-exact emulation can be done in the I/O access functions.
Quote:
Quote:
. . .emulation loop doesn't have to constantly poll other hardware, since the times of interaction will be known in advance. Most loops can have a single unchanging limit condition.
But these interactions can never be known in advance. That is the whole problem!
Sorry, but only if those interactions appear "out-of-system". I.e. a key-press for example. But VICE for example has IIRC an alarm (or does it during rasterline alarm) where the host operating system is queried for new events.
Any other interaction - when triggered by the device (like VIA) - is predictable, and the point in time can be computed, so it can be checked in the CPU loop that does the only non-predictable stuff.
Quote:
Quote:
Also, how the heck are you decoding an mp3 using just 2 MHz of time on a 65816?!? Is this stereo 16-bit 44/48 kHz?
uhh...huh? It's an emulator. It runs under Linux, and xmms (under Linux) is playing the MP3. The emulator is impacted by this.
Yes, I was wondering that too ;-)
André
P.S.: I used to be a core VICE emulator. And it took a real while to make it one of the best emulators available, so don't worry. I will be preparing a VICE patch to emulate my CS/A65 systems as well when I have time.
I have to admit, the VICE code is difficult to understand when not "initiated", but if you have any questions, ask me.