2023-02-06

What if VIC-II had 1K aligned charsets (and bitmaps)

Ciriciao folks!

Another feature that VIC has (I'm talking of the graphics chip of the VIC-20) and VIC-II hasn't is the ability to point to charsets at 1K boundaries.
Charset are 2K big both for VIC-20 and C64, but VIC-20 can place them in memory at 1K boundaries while C64 must place them at 2K boundaries.
One reason VIC-20 needed that is the low amount of memory accessible by the VIC, that theoretically is 16K but pratically it's only 9K, 4K are ROM (two charsets, both in plain and reverse versions), 5K are RAM, but of those 5K 256 bytes are shared with the zero page and another 256 bytes are shared with the stack, then some of the next 512 bytes are used by the kernal, leaving little more than 4K of RAM available for the programs.

So how the 1K boundary of the charset comes into play?

Let's suppose you wanted to write a game, you need your charset, containing the shapes of your background and sprites, to point to RAM, but you also need to print messages and scores, so you also copy some of the characters from ROM to RAM, what a waste!
Instead you point your charset to be 1K in RAM and 1K in ROM, so you'll have 128 tiles for your graphics and 128 standard characters.
Plain/reverse charactes will be swapped, but the RV bit might come to the rescue! (More on this in another article may be)

Register $05 of VIC, mapped at $9005, looks like register $18 of VIC-II, mapped at $D018

VIC |Bt7|Bt6|Bt5|Bt4|Bt3|Bt2|Bt1|Bt0|Function
----+---+---+---+---+---+---+---+---+---------------
9005|V13|V12|V11|V10|C13|C12|C11|C10|Memory pointers
----+---+---+---+---+---+---+---+---+---------------
VIC2|Bt7|Bt6|Bt5|Bt4|Bt3|Bt2|Bt1|Bt0|Function
----+---+---+---+---+---+---+---+---+---------------
D018|V13|V12|V11|V10|C13|C12|C11| - |Memory pointers
----+---+---+---+---+---+---+---+---+---------------

The difference is the CB10 bit (C10 in my table), bit #10 of the address of character data.

Why?

Look at chaper 3.7.3.1 of the Vic-II Article, how the g-access address is composed:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 13| 12| 11| 10|  9|  8|  7|  6|  5|  4|  3|  2|  1|  0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|C13|C12|C11| D7| D6| D5| D4| D3| D2| D1| D0|RC2|RC1|RC0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

If we want to put bit C10 into the picture then we need to add it to D7, and propagate the carry up to C13, that is, instead of a simple juxtaposition of data coming from different sources, wee need to add some of them, and when dealing with circuits juxtaposition is free and immediate, while an adder has a cost in terms of space (number of gates) and time (will it introduce delays?).

That's about characters, what about bitmaps?

Same and worse. VIC-II uses only CB13 bit, the other bits come from the video counter VC and row counter RC:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 13| 12| 11| 10|  9|  8|  7|  6|  5|  4|  3|  2|  1|  0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|C13|VC9|VC8|VC7|VC6|VC5|VC4|VC3|VC2|VC1|VC0|RC2|RC1|RC0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

Here we need to put 3 more bits of CB into the picture, added to 3 bits of VC, with carry added to CB13.

Implementation in Vice

In Vice we may not worry too much of the 'performance loss' that a sum will introduce with respect to a bitwise or.
As usual a new macro is defined in file vice/src/viciisc/viciitypes.h

/* VIC-II WIV flags */
...
#define WIV_XMP (vicii.regs[0x13] & 0x02) /* eXtended Memory Pointers: enable all bits of register $18 */

Reading register $18 will give the lowest bit only of XMP is set, file vice/src/viciisc/vic-mem.c

        case 0x18:              /* $D018: Video and char matrix base address */
            VICII_DEBUG_REGISTER(("Video memory address register: $%02X",
                                  vicii.regs[addr]));
            value = vicii.regs[addr] | (IS_WIV && WIV_XMP ? 0x0 : 0x1);
            break;

And finally in file vice/src/viciisc/vic-fetch.c when the memory address is built, a sum takes place instead of a bitwise or:

inline static uint16_t g_fetch_addr(uint8_t mode)
{
    ...
    /* BMM */
    if (mode & 0x20) {
        a = (vicii.vc << 3) | vicii.rc;

        if (IS_WIV) {
            a += (vicii.regs[0x18] & (WIV_XMP ? 0xf : 0x8)) << 10;
            a &= 0x3fff;
        } else {
            a |= (vicii.regs[0x18] & 0x8) << 10;
        }
    } else {
        a = (vicii.vbuf[vicii.vmli] << 3) | vicii.rc;

        if (IS_WIV) {
            a += (vicii.regs[0x18] & (WIV_XMP ? 0xf : 0xe)) << 10;
            a &= 0x3fff;
        } else {
            a |= (vicii.regs[0x18] & 0xe) << 10;
        }
    }
    ...
}

Implementation in VIC-II Kawari

In file hdl/registers.v the output cb (characters base) is expanded from 3 to 4 bits:

`ifdef WIV_EXTENSIONS
           output reg [3:0] cb,
`else
           output reg [2:0] cb,
`endif

A new flag wiv_xmp is added to register CR3:

           output reg wiv_cre = 1'b0, // VIC-WIV control registers read enable
           output reg wiv_xmp = 1'b0, // VIC-WIV extended memory pointers: enable all bits of register $18
           output reg wiv_dvb = 1'b0, // VIC-WIV disable vertical border
           output reg wiv_dmb = 1'b0, // VIC-WIV disable main border

Read and written to:

                            dbo[0] <= wiv_cre;
                            dbo[1] <= wiv_xmp;
                            dbo[2] <= wiv_dvb;
                            dbo[3] <= wiv_dmb;
...
                        wiv_cre <= dbi[0];
                        wiv_xmp <= dbi[1];
                        wiv_dvb <= dbi[2];
                        wiv_dmb <= dbi[3];

Used to enable reading the least significant bit of the expanded cb:

                    /* 0x18 */ `REG_MEMORY_SETUP: begin
`ifdef WIV_EXTENSIONS
                        dbo[0] <= cb[0] | ~wiv_xmp;
                        dbo[3:1] <= cb[3:1];
`else
                        dbo[0] <= 1'b1;
                        dbo[3:1] <= cb[2:0];
`endif
                        dbo[7:4] <= vm[3:0];
                    end

While writing always goes through:

                    /* 0x18 */ `REG_MEMORY_SETUP: begin
`ifdef WIV_EXTENSIONS
                        cb[3:0] <= dbi[3:0];
`else
                        cb[2:0] <= dbi[3:1];
`endif
                        vm[3:0] <= dbi[7:4];
                    end

The new flag is used in file hdl/addressgen.v where a sum is used when XMP is set:

`ifdef WIV_EXTENSIONS
                    if (wiv_xmp)
                        vic_addr = {cb + { 1'b0, vc[9:7]}, vc[6:0], rc}; // bitmap data
                    else
                        vic_addr = {cb[3], vc, rc}; // bitmap data
`else
                    vic_addr = {cb[2], vc, rc}; // bitmap data
`endif
                else
`ifdef WIV_EXTENSIONS
                    if (wiv_xmp)
                        vic_addr = {cb + { 3'b000, char_ptr[7]}, char_ptr[6:0], rc}; // character pixels
                    else
                        vic_addr = {cb[3:1], char_ptr, rc}; // character pixels
`else
                    vic_addr = {cb, char_ptr, rc}; // character pixels
`endif

BASIC example

Text mode example

The first example shows how to use the new feature in text mode: the charset points to the second half of the lowercase ROM font, so thet the upper 128 chars are in RAM and free to be redefined and displayed.

10 rem ** half charset from rom
11 rem ** and half charset from ram
20 poke 53267,2:poke 53272,23
30 rem ** smiley face
40 data 129,36,36,0,66,66,60,129
50 for i=0to7:read a:poke 8192+i,a:next
60 print"smile! "chr$(18)+"@@@"

Output:

Output

Bitmap mode example

The second example shows how the bitmap buffer can now be set at 1K boundaries.

10 rem ** move start of bitmap
11 rem ** with 1kb steps
20 print chr$(147)
30 poke 53267,2:poke 53265,55
40 for i=0to15
50 poke 53272,16+i
60 for p=0to1000:next
70 next
80 goto 40

Output:

Output

No comments:

Post a Comment