2023-01-23

What if VIC-II... had two additional registers

Ciriciao folks!
This is another post with preparatory work for the ones that will follow.

I'd like to add features to VIC-II, these features will be controlled by flags in control registers the same way the current features are controlled now, e.g. to enable multicolor mode bit #6 of Control Register 1 ($D011) needs to be set.

I want to add two 8 bit registers that I'll call Control Register 3 and 4 (CR3 and CR4), at what addresses?
VIC-II uses 47 registers mapped to C64 memory locations from $D000 to $D02E, the range from $D02F to $D03F is available-ish, then all registers repeat every 64 bytes until $D400 where the SID registers begin.
On one hand VIC-II evolutions use some or all of the available registers after $2E (VIC-IIe of the Commodore 128, VIC-II Kawari, VIC-III of the Commodore 65, VIC-IV of the Mega 65), and on another hand if I added some new features in a new register, say at $D031, how would I initialize it and reset it?

Let's look at how the Kernal initializes the other VIC-II registers:

E5A8   A2 2F      LDX #$2F
E5AA   BD B8 EC   LDA $ECB8,X
E5AD   9D FF CF   STA $CFFF,X
E5B0   CA         DEX
E5B1   D0 F7      BNE $E5AA

Registers from $CFFF + $2F down to $CFFF + $01, that is in the $D000-$D02E range, are initialized with values from a table at $ECB9:

ECB9   .BY $00,$00 ; sprite 0 x,y
ECBB   .BY $00,$00 ; sprite 1 x,y
ECBD   .BY $00,$00 ; sprite 2 x,y
ECBF   .BY $00,$00 ; sprite 3 x,y
ECC1   .BY $00,$00 ; sprite 4 x,y
ECC3   .BY $00,$00 ; sprite 5 x,y
ECC5   .BY $00,$00 ; sprite 6 x,y
ECC7   .BY $00,$00 ; sprite 7 x,y
ECC9   .BY $00     ; sprite x MSBs
ECCA   .BY $9B     ; control register 1: text mode
ECCB   .BY $37     ; raster = $137
ECCC   .BY $00     ; light pen X
ECCD   .BY $00     ; light pen Y
ECCE   .BY $00     ; enabled sprites
ECCF   .BY $08     ; control register 2: hires
ECD0   .BY $00     ; sprite Y expansion
ECD1   .BY $14     ; video at $400, chars at $1000
ECD2   .BY $0F     ; akcnowledge all interrupts
ECD3   .BY $00     ; disable all interrupts
ECD4   .BY $00     ; sprite priority
ECD5   .BY $00     ; sprite multicolor
ECD6   .BY $00     ; sprite X expansion
ECD7   .BY $00     ; sprite-sprite collision
ECD8   .BY $00     ; sprite-display collision
ECD9   .BY $0E     ; border color
ECDA   .BY $06     ; background color 0
ECDB   .BY $01     ; background color 1
ECDC   .BY $02     ; background color 2
ECDD   .BY $03     ; background color 3
ECDE   .BY $04     ; sprite multicolor 0
ECDF   .BY $00     ; sprite multicolor 1
ECE0   .BY $01     ; sprite 0 color
ECE1   .BY $02     ; sprite 1 color
ECE2   .BY $03     ; sprite 2 color
ECE3   .BY $04     ; sprite 3 color
ECE4   .BY $05     ; sprite 4 color
ECE5   .BY $06     ; sprite 5 color
ECE6   .BY $07     ; sprite 6 color
ECE7   .BY $4C     ; sprite 7 color

Notice that also the two read-only registesr for the light pen are written to, with a nice $00 value.
This is where I want to add my two new registers, CR3 at $D013 and CR4 at $D014.
The default behavior will be that reads will give the light pen X and Y values and writes will go to the new registers.
To begin with, only bit #0 of CR3 will be implemented, it will be used to enable reading control registers 3 and 4. The Kernal reset routine will clear the flag for us so that after reset the registers will behave like in a standard VIC-II.

Implementation in Vice

First let's add a macro in file vice/src/viciisc/viciitypes.h that will be used to access the new flag Control Registers Enable:

/* VIC-II WIF flags */
#define WIV_CRE (vicii.regs[0x13] & 0x01) /* Control Registers Enable: reads to $13 and $14 give control registers instead of light pen position */

Then in file vice/src/viciisc/vicii-mem.c mainly two new store functions are added:

inline static void d013_store(const uint8_t value)
{
    VICII_DEBUG_REGISTER(("WIV Control register 3: $%02X", value));

    vicii.regs[0x13] = value;
}

inline static void d014_store(const uint8_t value)
{
    VICII_DEBUG_REGISTER(("WIV Control register 4: $%02X", value));

    vicii.regs[0x14] = value;
}

to be called from the switch() for write accesses to registers:

/* Store a value in a VIC-II register.  */
void vicii_store(uint16_t addr, uint8_t value)
{
    // ...
    
    switch (addr) {
        
        //...

        case 0x13:                /* $D013: VIC-II WIV Control Register 3 */
            if (IS_WIV) {
                d013_store(value);
            }
            break;

        case 0x14:                /* $D014: VIC-II WIV Control Register 4 */
            if (IS_WIV) {
                d014_store(value);
            }
            break;

and return the new registers content for read accesses to registers:

/* Read a value from a VIC-II register.  */
uint8_t vicii_read(uint16_t addr)
{
    // ...

    switch (addr) {

        // ...

        case 0x13:                /* $D013: Light Pen X or VIC-II WIV Control Register 3 */
            if (IS_WIV && WIV_CRE) {
                VICII_DEBUG_REGISTER(("WIV Control Register 3: $%02X", vic.regs[addr]));
                value = vicii.regs[addr];
                break;
            }

            VICII_DEBUG_REGISTER(("Light pen X: %d", vicii.light_pen.x));
            value = vicii.light_pen.x;
            break;

        case 0x14:                /* $D014: Light Pen Y or VIC-II WIV Control Register 4 */
            if (IS_WIV && WIV_CRE) {
                VICII_DEBUG_REGISTER(("WIV Control Register 4: $%02X", vic.regs[addr]));
                value = vicii.regs[addr];
                break;
            }

Implementation in VIC-II Kawari

Let's add a new configuration file for our variant of the VIC-II Kawari firmware, that I will call WIV1, where I define a macro to guard my new extensions, the rest is based on the MAIN variant.

I'm adding the new configuration file boards/rev_4H/config.vh.WIV1LH only in the rev_4H directory because I'm developing on a mini board.

...
`define VARIANT_NAME1   8'h57  // W
`define VARIANT_NAME2   8'h49  // I
`define VARIANT_NAME3   8'h56  // V
`define VARIANT_NAME4   8'h31  // 1

`define VARIANT_SUFFIX_1 8'd76 // L
`define VARIANT_SUFFIX_2 8'd72 // H
`define VARIANT_SUFFIX_3 8'd0
`define VARIANT_SUFFIX_4 8'd0
...
`define WIV_EXTENSIONS 1
...

Then in file hdl/registers.v, similarly to what has been done for Vice, reading and writing to registers is extended to implenmet CR3 and CR4.
Reading:

`ifdef WIV_EXTENSIONS
    /* 0x13 */ `REG_LIGHT_PEN_X: begin
        if (wiv_cre) begin
            dbo[0] <= wiv_cre;
            dbo[7:1] <= wiv_cr3_unused;
        end else begin
            dbo[7:0] <= lpx;
        end
    end
    /* 0x14 */ `REG_LIGHT_PEN_Y: begin
        if (wiv_cre) begin
            dbo[7:0] <= wiv_cr4_unused;
        end else begin
            dbo[7:0] <= lpx;
        end
    end
`else

and writing:

`ifdef WIV_EXTENSIONS
    /* 0x13 */ `REG_LIGHT_PEN_X: begin
        wiv_cre <= dbi[0];
    	wiv_cr3_unused[7:1] = dbi[7:1];
    end
    /* 0x14 */ `REG_LIGHT_PEN_Y: begin
    	wiv_cr4_unused[7:0] = dbi[7:0];
    end
`endif //WIV_EXTENSIONS

BASIC example

This simple BASIC example writes two even numbers (with bit 0 clear) to CR3 and CR4 and tries to read it back, then tries with two odd numbers (with bit 0 set).
Reading from CR3 and CR4 when bit 0 of CR3 is clear gives back the X and Y positions of the light pen, reading when bit 0 is set gives back the content of the brand new CR3 and CR4 registers, yay!

5 rem ** read/write new registers
10 c3=53267
15 c4=53268
20 poke c3,100
25 poke c4,100
30 printpeek(c3)" = 100? (might be)"
35 printpeek(c4)" = 100? (might be)"
40 poke c3,101
45 poke c4,101
50 printpeek(c3)" = 101! (must be)"
55 printpeek(c4)" = 101! (must be)"

No comments:

Post a Comment