LAUGHTON ELECTRONICS |
|||
Cheap Video à la Lancaster and the back story re: my KIM-1 |
|||
|
|||
Cheap Video and Lying To the Machine |
|||
Cheap Video is a means of outputting video without the need for DMA hardware or a Video Controller chip with dedicated video memory. Instead, a portion of the existing system RAM serves as video memory, and, instead of DMA, what's used is programmed I/O. In other words, video is generated as the output of an actual program running on the computer. This would ordinarily be impossible due to the very high data rate required, but Cheap Video slips a joker in the deck — a simple hardware trick (described later) which fools the CPU. At the heart of the video program is a loop, and each iteration of this inner loop outputs one row of pixels, corresponding to one horizontal sweep (or "scan") of the CRT/LCD monitor. Each loop iteration begins with the CPU making a Jump To Subroutine (JSR) to some address within a portion of memory you've chosen to use as the video buffer. That's right — it jumps to an address where data is stored! Thanks to the unusual hardware I mentioned, what the CPU "sees" in the buffer is not pixel data. Instead, those addresses appear to contain a dubious subroutine composed of dozens of ORA # $00 instructions. (Alternatively something like AND # $FF or CMP # $C9 instructions could be used. The effect is the same — namly a two-byte, two-cycle NOP.). Naturally the CPU follows orders and executes these virtual NOPs. Then the do-nothing subroutine terminates with an RTS. What's noteworthy is that the address bus rapidly and steadily increments, once per cycle, as the NOPs and the first two cycles of the RTS execute. In other words, the CPU's PC register spends a few dozen cycles behaving like an ordinary 16-bit counter... and, it is counting its way through a selected portion of the video buffer. This is the "action" part of the sequence, and the CPU is doing what we would want a DMA controller to do — quickly read a series of bytes from memory. Each byte is immediately sent to a shift register that serializes the bits in order to output the video bit stream. After the RTS
occurs the spell is broken. We stop fetching from the video buffer, and
the RTS's return address takes us back to finish
the rest of the loop.We output a horizontal sync pulse (typically via
a parallel port
bit), we compute a new address to be used by the
next JSR, then
usually the loop reiterates. There's no
loop exit until there have been enough
scans (horizontal lines) to refresh the entire screen from top to
bottom — ie; one frame. To produce a continuous
succession of frames, the inner loop is wrapped in an outer loop that
ultimately outputs the Vertical
Sync pulse and
rolls the JSR address back to its top-of-the-screen value. The scheme just described produces a bit-mapped display and no interlacing. (Interlacing can be had by altering the software.) Character-based displays are also readily possible. One option is to throw hardware at the problem and install a Character Generator ROM. But that approach may not be worthwhile, given that the same result can be obtained by software. You can simply use the bit-mapped display and have it updated by an assembly-language character-drawing routine. |
|||
![]() |
![]() |
||
The sneaky trick mentioned earlier is what causes the CPU to see the buffer area as containing quasi-NOPs rather than what's really there (the video data). Here's how it's done: Usually when a CPU sends out an address, memory will faithfully reply with the byte stored at that address. But with Cheap Video a major connection — that between the data buses — gets temporarily severed. This lets Cheap Video "lie" about what's in memory. (See the diagrams above, Business as Usual and Cheap Video.) During the "action" part of each scan, the bytes fetched onto the memory data bus don't get relayed back to the CPU's data bus. Instead, the bytes (ie; the pixel data we needed to fetch) get shipped off to the video display. Meanwhile, some Cheap Video flimflam logic feeds the CPU bus a brazen fabrication, a persistent ORA # $00 (and eventual RTS) which appear to reside at the addresses actually containing data. Obviously there needs to be a mechanism that cues hardware regarding when to suspend reality and produce dummy op-codes. Lancaster's version takes its cues from the values appearing on the address bus. A portion of the 64K map — perhaps 4K or 8Kbytes in size — is recognized by the decode hardware as the video buffer. When scanning is enabled, from the CPU point of view the entire buffer region is filled with repeated images from a 32-byte PROM containing mostly ORA # $00 instructions plus an RTS. From this, clever wiring and coding can yield 40- and 80-byte-wide displays, although of course power-of-two widths such as 64 are easier. With all cheap-video schemes, proper scan timing depends hugely on how the code is written — particularly that the execution time mustn't vary from one line to the next. The KimKlone is not cued by addresses. Instead of JSR, the inner loop uses a KimKlone JSR variant (coded as opcode $33) to initiate the scan. Scans terminate according to a VIA timer cycling at the horizontal frequency. Under this system the video buffer — or an array of them — can reside anywhere in KK's 16 MByte space. Lancaster realized that a microprocessor is capable of burst-reads of memory, sustained a rate of one byte every cycle, even though conventional processing uses only sporadic accesses to small chunks of data. But prolonged sequences of memory reads do occur as the chip fetches the bytes of its program. The CPU unwittingly mimics a 16-bit counter or a DMA controller, with its address bus outputting an ascending 16-bit count. I am indebted to Mr Lancaster for the lesson I learned from Cheap Video, namely that a microprocessor can readily be manipulated by hardware tricks in order to produce unusual behaviors that are useful. The KimKlone, of course, relies very heavily on this principle. |
|||
|
|||
visit LAUGHTON ELECTRONICS |
Projects Servicing the unserviceable Main/extra index |
||
![]() |