2023-01-24

What if VIC-II had no borders

Ciriciao folks!
Let's start doing real juicy modifications with an easy one, that fully illustrates my thoughts.

When I think to what Commodore could have done differently with the graphics processor of the Commodore 64 I'm not thinking to complete redesigns of the chip (at least not initially), but to small changes that could have been introduced without altering the complexity or the cost of the VIC-II.
I'm not even thinking to something new, but something that existed in graphics chips from competitors or previous products of Commodore, such as the VIC chip (the graphics processor of the VIC 20).

For instance VIC could display a variable size video matrix, the default dimension was 22 columns by 23 rows, but that could be changed programmatically. VIC-II can not, the video matrix is hardwired to 40x25.
VIC could move the video matrix to some extent by many characters, VIC-II can only move it by 7 pixels.
VIC could produce an interlaced video signal (only in NTSC), VIC-II can not.

It must be said that with clever programming some of the VIC-II limitations can be overtaken, but at the expense of the CPU spending some or most of its time mangling with VIC-II registers.
One such trick is what the VIC-II article calls the hyperscreen, commonly named "opening the borders".

Why the VIC-II has borders and why programmers want to remove them?

One reason to have borders is to mask out parts of sprites when they move in or out of the sides of the screen, one reason not to have borders is to see sprites when they are out of the screen!
This is often used in games to display score and other information in the upper or lower borders, because opening the upper/lower border is relatively easy. Opening the left/right border on the other hand is trickier and consumes a lot of CPU time.

So what if VIC-II had two flags in his registers to programmatically disable the upper/lower borders or the left/right borders?

For reasons that are explaned in the VIC-II Article and are reflected in VICE sources, one flag named DVB will disable the vertical border, but the other named DMB (Disable Main Border) will disable both vertical and horizontal borders. Sorry, no flag for disabling only the horizontal border.
There is a difference though in disabling only the main border or disabling both: the idle pattern is shown in the vertical border only when DVB is set.

Implementation in Vice

Two new macros to query the value of the new flags are added in file vice/src/viciisc/viciitypes.h

/* VIC-II WIV flags */
#define WIV_CRE (vicii.regs[0x13] & 0x01) /* Control Registers Enable: reads to $13 and $14 give control registers instead of light pen position */
#define WIV_DVB (vicii.regs[0x13] & 0x04) /* Disable Vertical Border */
#define WIV_DMB (vicii.regs[0x13] & 0x08) /* Disable Main Border */

The macros are used in file vice/src/viciisc/vicii-cycle.c where the internal status of border unit is changed:

    if (line == (rsel ? VICII_25ROW_STOP_LINE : VICII_24ROW_STOP_LINE)) {
        vicii.set_vborder = IS_WIV && WIV_DVB ? 0 : 1;
    }
...
    /* Right border starts at cycles 56 (csel=0) or 57 (csel=1) on PAL. */
    if (cycle_is_check_border_r(cycle_flags, csel)) {
        vicii.main_border = IS_WIV && WIV_DMB ? 0 : 1;
    }

Implementation in VIC-II Kawari

Two flags are added to the CR3 register in file hdl/registers.v:

           output reg wiv_cre = 1'b0, // VIC-WIV control registers read enable
           output reg wiv_dvb = 1'b0, // VIC-WIV disable vertical border
           output reg wiv_dmb = 1'b0, // VIC-WIV disable main border

That can be read:

                    /* 0x13 */ `REG_LIGHT_PEN_X: begin
                        if (wiv_cre) begin
                            dbo[0] <= wiv_cre;
                            dbo[2] <= wiv_dvb;
                            dbo[3] <= wiv_dmb;
                        end else begin

and written:

                    /* 0x13 */ `REG_LIGHT_PEN_X: begin
                        wiv_cre <= dbi[0];
                        wiv_dvb <= dbi[2];
                        wiv_dmb <= dbi[3];
                    end

The new flags are outputs of the registers module and inputs of the border module, file hdl/border.v

`ifdef WIV_EXTENSIONS
           input wiv_dvb, // VIC-WIV disable vertical border
           input wiv_dmb, // VIC-WIV disable main border
`endif

They are used to disable the vertical border:

`ifdef WIV_EXTENSIONS
                set_vborder = ~wiv_dvb;
`else
                set_vborder = 1;
`endif

and disable the main border:

`ifdef WIV_EXTENSIONS
            main_border = ~wiv_dmb;
`else
            main_border = 1;
`endif

This is almost the same as in Vice!

Flags travelling from one component to another need 'wires' declared in hdl/vicii.v:

`ifdef WIV_EXTENSIONS
wire wiv_cre; // VIC-WIV control registers read enable
wire wiv_dvb; // VIC-WIV disable vertical border
wire wiv_dmb; // VIC-WIV disable main border
`endif

and wires are connected to the components later in the same file:

border vic_border(
           ...
`ifdef WIV_EXTENSIONS
           .wiv_dvb(wiv_dvb),
           .wiv_dmb(wiv_dmb),
`endif
           ...
       );
registers vic_registers(
              ...
`ifdef WIV_EXTENSIONS
              .wiv_cre(wiv_cre),
              .wiv_dvb(wiv_dvb),
              .wiv_dmb(wiv_dmb),
`endif
              ...
          );

And that's all, who thought that making a modification to a chip would have been so easy!

BASIC example

Display sprites in the border from a BASIC program, yay!

10 rem * pattern under vertical border
20 poke 16383,34
30 rem * square sprites under borders
40 for i=0to62:poke 16320+i,255:next
50 for i=0to7:poke 2040+i,255:next
60 poke 53269,255:poke 53264,12
70 poke 53248,100:poke 53249,25
71 poke 53250,150:poke 53251,25
72 poke 53252,90 :poke 53253,50
73 poke 53254,90 :poke 53255,100
74 poke 53256,200:poke 53257,255
75 poke 53258,250:poke 53259,255
76 poke 53260,0  :poke 53261,150
77 poke 53262,0  :poke 53263,200
110 poke 53267,0
120 print"full borders"
130 input a$
140 poke 53267,4
150 print"no vertical border"
160 input a$
170 poke 53267,8
180 print"no main border"
190 input a$
200 poke 53267,12
210 print"both disabled"
220 input a$
230 goto 110

Output:

output

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)"

2023-01-17

What if VIC-II...

Ciriciao folks!

What if Commodore made VIC-II (the heart of the Commodore 64) slightly different from the start or evolved its original design over the years to include new features?

I've often wondered about how difficult it would have been to change this thing or add that other, now thanks to the VIC-II Kawari project I can experiment on this, let's go!

I will proceed in small steps, for each one I'll give the modifications to VIC-II Kawari and Vice, and a BASIC program to test them.

But before I start making the real juicy new features I need a starting test bed in Vice.
Let's add a new VIC-II chip model, actually two of them for the PAL and NTSC variants, called WIV for What If VIC-II, that will be shown in the settings:

In order to achieving that, the new models need to be added to an enumeration in vice/src/vicii.h:

/* PAL, hypotetical 6569 evolution */
#define VICII_MODEL_WIVPAL   7
/* NTSC, hypotetical 6567 evolution */
#define VICII_MODEL_WIVNTSC   8

And in the displayed list in vice/src/arch/gtk3/c64scui.c:

/** \brief  List of VIC-II models
 *
 * Used in the VIC-II model widget
 */
static const vice_gtk3_radiogroup_entry_t c64sc_vicii_models[] = {
    { "6569 (PAL)",             VICII_MODEL_6569 },
    { "8565 (PAL)",             VICII_MODEL_8565 },
    { "6569R1 (old PAL)",       VICII_MODEL_6569R1 },
    { "6567 (NTSC)",            VICII_MODEL_6567 },
    { "8562 (NTSC)",            VICII_MODEL_8562 },
    { "6567R56A (old NTSC)",    VICII_MODEL_6567R56A },
    { "6572 (PAL-N)",           VICII_MODEL_6572 },
    { "WIV (PAL)",              VICII_MODEL_WIVPAL },
    { "WIV (NTSC)",             VICII_MODEL_WIVNTSC },
    { NULL, -1 }
};

Other modifications are needed in vice/src/viciisc/vicii-chip-model.c, in vice/src/viciisc/vicii-color.c, and in vice/src/viciisc/vicii-resources.c.

Finally in file vice/src/viciisc/vicii-resources.h a handy macro is added that will be useful later to conditionally enable WIV features:

#define IS_WIV (vicii_resources.model == VICII_MODEL_WIVPAL || vicii_resources.model == VICII_MODEL_WIVNTSC)

That's all for today, no modifications to VIC-II Kawari and no BASIC test needed, that'll be for the next posts.