Here are some things that you need to be aware of when programming BeamRacer.
If you ever did any VIC-II programming, you are probably very familiar with the concept of so-called badlines. Briefly, badlines are the display lines during which VIC-II is doing additional memory fetches from color RAM and video matrix1). The side-effect is that during 40 cycles of such line the main CPU is fully blocked from accessing the buses, and thus sits there doing nothing. What's worse, VIC is so preoccupied with the extra work that it has no time left to listen to writes to its own registers, in effect becoming totally deaf to whatever is going around it.
Thus, although VASYL runs concurrently and is not bound by limitations of the system bus, it cannot affect VIC-II in any way during these busy cycles. If you issue a write to a VIC register while it is not paying attention, VASYL will stop and wait until VIC finishes doing extra fetches and becomes interested in the outside world again.
Simple example will demonstrate this clearly.
WAIT 98, 16 ; Regular line MOV $21, 2 MOV $21, 5 MOV $21, 6 WAIT 107, 16 ; Badline MOV $21, 2 MOV $21, 5 MOV $21, 6 END
Once you activate this display list, two visual markers show up on screen. The first is created in a regular line, and is able to change VIC-II color register at will, while the second is in a badline - VIC-II is busy for basically all of the line's cycles and only starts accepting writes at its very end. VASYL has no choice but to wait for VIC-II, which is the reason why you see the marker on the right-hand side of the screen.
Naturally, only writes to VIC-II registers stall VASYL. Otherwise, it is not bothered by bad lines - it executes the display list and accepts writes to its own registers without any delay.
Buffered 6510 writes
This section is somewhat more hardware-oriented than the rest of documentation. If you just want a TL;DR summary: do not use “shortcuts” (
ASL $d019 or
DEC $d019) when acknowledging VIC-II interrupts while a display list is active. That's it.
Let's start by understanding what happens when VASYL writes to a VIC-II register.
As explained above, any write to VIC-II register will be postponed until the access is physically possible, i.e. until VIC-II is willing to listen to us. This means that XFER, MOV and BADLINE can stall while they are waiting for AEC line to go up.
Once VIC-II is available, BeamRacer will start by separating system and local buses (address and data).
On the local side, the write proceeds as usual - first with a write to the address bus and VIC-II CS and RW lines getting asserted, followed by a write to the data bus.
6510 continues unimpeded on the system bus side, and if it does not want anything from VIC/VASYL IO address space, it will not lose any cycles. However, if it accesses
$d000-$d3ff, it will be halted until VASYL is done writing to VIC-II, which may be arbitrarily long (e.g. if the display list makes a sequence of VIC-II writes in successive cycles).
Now, if the 6510 was trying to make a write access, it naturally won't stop immediately, but will continue execution until the next read cycle. BeamRacer cannot use VIC-II's approach of asserting RDY ahead of time, because display lists are dynamic and VASYL only finds out what needs to happen once it fetches and decodes its current instruction. It cannot let the CPU make the access either, because that would mean postponing display list's write access and likely result in a visual glitch.
So what happens is that 6510 is allowed to continue, and the writes by the CPU and by VASYL happen concurrently. The system and local buses are isolated, so VASYL write gets to VIC-II, while CPU write gets recorded by BeamRacer and preserved for future use. When the time comes for the CPU to run again, it will need to wait one more cycle, during which the previously recorded write access is replayed to VIC/VASYL.
In other words, VASYL writes always have priority over CPU writes.
As is well known, the 6510 can perform up to three write accesses in sequence, and consequently needs as much time to be stopped. That's why VIC-II asserts BA line three cycles ahead of pulling down AEC - giving the CPU enough time to stop no matter what it is doing at the moment. How can then BeamRacer function with just a single-access buffer?
First, the only situation where three write accesses in a row happen is at the start of interrupt service sequence2), when CPU pushes program counter (two bytes) and status register (one byte) to the stack. This is of no concern to BeamRacer, because the stack is always in the fixed location (
$100-$1ff), certainly outside VIC-II address space, and so the writes to it never need to be postponed.
Two writes in a row happen:
- During execution of JSR instruction - again, saving CPU program counter to the stack.
- During execution of RMW instructions, when memory data is read, modified and finally written back to the same location.
The JSR is not an issue, for the same reason three-writes-in-a-row situation was not - BeamRacer does not need to stop the CPU during stack writes.
That leaves the final scenario - execution of one of ASL, LSR, DEC, INC, ROL or ROR instructions. Each of these instruction makes - in addition to read accesses - two successive write accesses. The first stores the original value which was read from the target location, the second - a modified one. For instance, if there is a value
$01 at memory location
DEC $8000 will write values
$01 and then
$00 to address
This is sometimes exploited to acknowledge VIC-II raster interrupt - the appropriate bit is the least significant one in register
$d019, so if it is set, executing, e.g.
DEC $d019 will with its first write achieve the equivalent of
LDA #1; STA $d019 (and the second write stores a value of zero, which is irrelevant in this case).
This “trick” is not supported by BeamRacer, because only the second write gets stored and subsequently replayed if CPU and VASYL writes happen simultaneously. In any case, it is recommended that VASYL interrupts are used instead, as they have the advantage of being triggerable at any rasterline cycle, not just at its beginning.