Y'know, those guys at MOS Technology really knew what they were doing when they laid down the designs for the various chips in the 6xxx family, and having spent a couple of days immersed in some fairly heavy research into the 656x VIC, my respect for them just deepens - some of the tricks this little slab of silicon can pull are frankly amazing, and even though I knew it could do most of these things 30 years ago, it's still an eye-opener when you get to figure-out how it does them, and write some code yourself to make things happen and understand why they happen.
Before continuing, I really must strongly recommend "Programming The VIC" by Raeto Collin West, for anyone who likes to read such things, which I discovered last week and have not been able to put down! I've always considered Nick Hampshires' "The VIC Revealed" to be the essential reference for anyone doing VIC-20 programming, but I have to say that I think PTV might just be superior to it - it's written in a more approachable style, and yet covers just about everything TVR does (if not more) without ever leaving the reader gasping for air by trying to connect two concepts without quite having understood the explanations. And if nothing else, the diagrammatic explanation of the VIC registers is just breathtaking in its' clarity and simplicity - I show it here with full acknowledgement of copyright. The commentary for each register is startlingly concise too, leaving no stone unturned.So I was doing a bunch of research on the 656x so that I could put together the VIC settings and code necessary for 40-column text generation - this has all been done before, of course, but I wanted to see if I could push the envelope a little. As it turns out, I can't really do much that's different from other earlier 40-column implementations simply because there's a hard limit to what the VIC can do, but at least I can explain what that limit is.
There are three critical chunks of memory that the VIC needs in order to do its' thing: a Video Matrix (VM), a Colour Matrix (CM), and a Character Generator (CG). The standard VIC setup allocates 506 bytes of user RAM for the VM (at either $1E00 or $1000, depending on expansion) which makes sense as the screen is 22 columns by 23 rows, and 22*23=506. In fact, two actual 'pages' are reserved for the screen (one page is 256 bytes) so the screen has 512 bytes available, but the last 6 are just sort of 'lost' down the back of the sofa - the screen doesn't need them and they're not obviously available for use, but they are in fact usable just like any other chunk of RAM if you know they're there. ;)
The CM has 2K (2048 bytes) of 4-bit nybbles built-in at $9400 - I've mentioned before about how these nybbles are different from the rest of RAM, and more importantly about how you have to take care when reading their values - always AND the value with $0F (15) to mask-off the upper nybble to get rid of any data-bus noise that might be in it. The CG of course is just a chunk of binary data describing characters (or glyphs) that can be drawn on-screen, and this is the 4K ROM at $8000 (which contains two 2K character sets, each made up of 256 glyphs of 8 bytes each - 8*256=2048).
Now here's how it works - the VM is really just a grid of pointers into the CG - when you put a value into the VM to display a character, the VIC just takes that value, multiplies it by 8, and adds the result to the start address of whichever half of the CG it's currently looking at (characterset #1 starts at $8000; characterset #2 starts at $8800). That gives it a handle on the 8 consecutive bytes that define the glyph shape, which it then does some Dark Magic with to encode it as a video signal for the TV/monitor to display. At the same time, it takes the address of the VM location, maps that to the corresponding address of the CM (which is also divided into two 1K halves, although which half it uses is derived from where the VM is in memory) and reads the colour nybble for that screen location - this is added to the incantation performed during video signal encoding to get whatever colour it is on to the display.
I summarise glibly, of course - the chip is doing some VERY clever stuff behind the scenes, but this is the general idea and gives us enough information to look at some neat stuff - like User Defined Graphics, also known as UDGs. This technique allows the VIC to display new shapes that don't exist in the CG ROM, and is pretty-much the basis for any non-trivial game you ever saw on the VIC-20 - and, incidentally, the foundation for doing hi-res graphics, but we have to walk before we can run... UDGs are actually pretty simple - you just tell the VIC to look somewhere else for its' glyph data than the CG ROM. Allocate a chunk of RAM to hold some new character designs, point the chip at that instead of the built-in charactersets, and it does exactly the same multiplier-lookup from the VM but into your new glyph data. You need to do a little bit of maths to determine where you can put your new glyphs in memory, because the VIC has some restrictions - it can't see some areas of RAM due to the way the hardware is wired-up - but once you've figured that out and made sure your data is somewhere it can get to it, it's just a matter of tweaking $9005 to the right value so that the chip knows where to look.
The same basic principle applies for doing hi-res graphics on the VIC-20 too; if you tell the VIC to look somewhere in RAM for glyph data instead of the CG ROM, and then fill the VM with all 256 unique character codes, and then modify the glyph data on-the-fly - you're effectively getting a hi-res image made-up of 256 dynamically-generated UDGs. Of course you need to allocate quite a big chunk of memory for 256 glyphs (256*8=4096 bytes) and on an unexpanded VIC you simply don't have the room for a bitmap that big - so the traditional choice was either to make the bitmap smaller (using fewer than 256 glyphs) so that it would fit with some room left over for some code to do something with it, or slap an expansion RAM cartridge in the machine.
But hold on - 256 glyphs doesn't fill the screen, does it? The standard layout has 506 character locations in the matrix, so that means we can only ever do hi-res graphics over half the screen...?
Well, no. There are a couple of things you can do to fill the screen - the most obvious being to make it smaller. Several games did this to conserve memory by making the visible playfield smaller and thus requiring no more than 256 glyphs (at most) to bitmap the whole area - Arcadia was one such, which adjusted the rows and columns of the screen to yield a taller, thinner area which required no more than 256 glyphs. But if you're going for a 'full screen' application (as VIC++ is) you can't do that because screen real-estate is quite important. So the other option for bitmapping a large screen is to flip the Double Height Characters bit in $9003, which tells the VIC to do something rather cunning. With this bit set, the VIC switches from reading 8 bytes for a glyph to reading 16, which means that 256 character positions on the screen actually occupy twice the vertical space - thus, 256 glyphs fill a 512-character area.
Well, in theory they do - in practice, the physical screen dimensions tend to be geared towards displaying a specific type of application, and this means that invariably you have a screen arrangement that doesn't actually utilise all 256 double-height glyphs; this is the case with VIC++ in which my original idea of using 5-bit character glyphs over 32 lines needed more bitmap memory than the VIC could address - I could have gotten around that by doing some fancy raster-synced refreshing of the bitmap data, but that typically consumes lots of processor time, and I want as much as possible available for user code.
So instead I've dropped the glyph size to 4 bits (which means 40 characters occupy 160 pixels, or 20 'standard' 8-bit character positions) and the row count is down to 24, giving me a 160x192 bitmap for drawing 40-column text into but staying within the VIC memory constraint at 3840 bytes. The VM weighs-in at 480 bytes, or just under two pages (leaving 32 bytes unused, just like the 6 'lost' bytes in the standard screen) although I might extend the screen by another half-line and use those spare bytes as a constantly-displayed status row.
Since the VM only needs two pages, I've altered the configuration so that it sits rather neatly in the two pages at $0200-$02FF instead of at $1000, and this puts the CM in the upper-half of the colour nybble space at $9600 - which, incidentally, means I could use the lower-half at $9400 for something like a set of 4-bit flags maybe, or perhaps as part of the row-builder buffer needed for generating 40-column display lines. The BRK and IRQ vector words I had stashed at $0200 & $0202 are relocated for the moment to Zero Page, but I think they might fit into the last few spare bytes after the VM - waste not, want not. ;)
And so, finally, a test of the new configuration. If I've set everything up correctly, I should have the VM at $0200, CM at $9600, and the CG (which is now effectively just a bitmap area) at $1000, Let's see...
That looks like a horrible mess, doesn't it? Surely a retrograde step from the last screenshot where there were actual recognisable characters visible. But in fact this is an incremental test bit-pattern splatted into the bitmap to prove that everything is working as expected. In other words, whatever bits I choose to switch on in the CG are uniquely byte-indexed by the VC, and show-up on the screen in the appropriate colour selected in the CM. So I'm now in a position to write some utility code which maps character data from the 4-bit-glyph ROM (at $8000) into the desired area of the bitmap, showing-up as lines of text.
The caveat to remember is that the CM maps across 8-bit display cells, so at the moment colour changes will affect pairs of 4-bit characters, rather than individual 8-bit ones. I'm going to have to look at enabling Multicolour Mode (which would mean moving the screen again to get the CM into the lower-half of the colour nybble RAM, as it needs two bits per pixel) but for the moment I want to concentrate on getting the basics of the display working.
The other thing that occurred to me is that two 4-bit glyphs fit into the space of one 8-bit glyph in the characterset ROM, so I only need half the space to store a characterset - and since I'm only having one set anyway (rather than the two Commodore ported from the PET) that means 3K of the 4K ROM will be unused. So I'm going to use it to hold more OS code and/or data, meaning my upper size limit goes up from 16K to 19K. Yay!

No comments:
Post a Comment