I'm reading the FX documentation but it's very confusing. The version I have was downloaded January 2023, I'm pretty sure it's later than 0.3.1.
This is the code I have. It's meant for 4-bit mode (a.k.a. 16-color). It doesn't work, just outputs some @ characters. Not what I'm looking for! I have many questions, for example, how do I set the initial address if DCSEL = 0 is used to indicate the major increment? I'm thinking there's something fundamental I'm not understanding. Any help would be appreciated.
unsigned char color;
int i;
register volatile unsigned char *p;
color = GREEN;
/* Line helper mode */
VERA.display.video |= 0x01;
/* Select ADDR0 */
VERA.control = 0x00;
/* Always increment by 160 */
VERA.address_hi = 0xD0;
/* Select ADDR1 */
VERA.control = 0x01;
/* Occasionally decrement by 0.5 */
VERA.address_hi = 0x0C;
/* Select ADDR3 */
VERA.control = 0x03;
/* Set slope */
VERA.display.video = 16;
/* Draw the line */
for (p = &VERA.data0, i = 0; i <= 30; ++i)
*p = color;
/* Restore traditional VERA behavior */
VERA.display.video &= 0xFC;
How to Use VERA FX "Line Helper"?
Re: How to Use VERA FX "Line Helper"?
Just outputs some @ characters?
What _bitmap_ mode do you have the layer configured for that you are using?
What _bitmap_ mode do you have the layer configured for that you are using?
Re: How to Use VERA FX "Line Helper"?
" how do I set the initial address if DCSEL = 0 is used to indicate the major increment? I'm thinking there's something fundamental I'm not understanding."
https://github.com/X16Community/x16-doc ... ference.md
$9F20 ADDRx_L (x=ADDRSEL) VRAM Address (7:0)
$9F21 ADDRx_M (x=ADDRSEL) VRAM Address (15:8)
$9F22 ADDRx_H (x=ADDRSEL) Address Increment DECR Nibble Increment Nibble Address VRAM Address
$9F25 CTRL Reset DCSEL ADDRSEL
CTRL & 1 => address selector..
LDA #1 ; DCSEL = 0, addrsel =1
STA $9F25 ; now we can set address for DATA1
STZ $9F25 ; now we can set address for DATA0
STZ $9F20 ; set ADDRx_L to 0 ( H:MMMMMMMM:LLLLLLLL)
STZ $9F21 ; set ADDRx_M to 0
LDA #$10
STA $9F22 ; set increment to 1, VRAM 'bank' to 0 ...
Now assuming we have ADDRSEL set to 0, we can write to DATA0 $9F23 and increment by 1 byte each time ..
LDA #$20
STA $9F22
now we increment by 2 each time ...
and so on based on the table in the doc -
Register value Increment amount
0 0
1 1
2 2
3 4
4 8
5 16
6 32
7 64
8 128
9 256
10 512
11 40
12 80
13 160
14 320
15 640
to decrement after writing instead, OR 8 .. to increment/decrement by nibble, OR 4 .. to set the nibble offset to 1, OR 2 ...
is what is meant here -
$9F22 ADDRx_H (x=ADDRSEL) Address Increment DECR Nibble Increment Nibble Address VRAM Address
https://github.com/X16Community/x16-doc ... ference.md
$9F20 ADDRx_L (x=ADDRSEL) VRAM Address (7:0)
$9F21 ADDRx_M (x=ADDRSEL) VRAM Address (15:8)
$9F22 ADDRx_H (x=ADDRSEL) Address Increment DECR Nibble Increment Nibble Address VRAM Address
$9F25 CTRL Reset DCSEL ADDRSEL
CTRL & 1 => address selector..
LDA #1 ; DCSEL = 0, addrsel =1
STA $9F25 ; now we can set address for DATA1
STZ $9F25 ; now we can set address for DATA0
STZ $9F20 ; set ADDRx_L to 0 ( H:MMMMMMMM:LLLLLLLL)
STZ $9F21 ; set ADDRx_M to 0
LDA #$10
STA $9F22 ; set increment to 1, VRAM 'bank' to 0 ...
Now assuming we have ADDRSEL set to 0, we can write to DATA0 $9F23 and increment by 1 byte each time ..
LDA #$20
STA $9F22
now we increment by 2 each time ...
and so on based on the table in the doc -
Register value Increment amount
0 0
1 1
2 2
3 4
4 8
5 16
6 32
7 64
8 128
9 256
10 512
11 40
12 80
13 160
14 320
15 640
to decrement after writing instead, OR 8 .. to increment/decrement by nibble, OR 4 .. to set the nibble offset to 1, OR 2 ...
is what is meant here -
$9F22 ADDRx_H (x=ADDRSEL) Address Increment DECR Nibble Increment Nibble Address VRAM Address
Re: How to Use VERA FX "Line Helper"?
Ohhhhh...
/* Line helper mode */
VERA.display.video |= 0x01;
yeah....no..
DCSEL is 2x ... I dunno why thyy did that..
the bits are assigned with bit 0=ADDRSEL
then the higher bits from DCSEL ..
Its bit7 - #128 => reset
then bits 6-1 for DCSEL .... 0,2,4,6,8,10,12 are only ones used becuse its 0-6 ...
then bit 0 for ADDRSEL
1 bit RESET | 6 bits for DCSEL | 1bit ADDRSEL
Then you're setting .... ADDRESS low to ????
There is no ADDR3 ... only ADDR0 and ADDR1, for DATA0 and DATA1 ..
ADDR != DCSEL
FX features are controlled mainly by registers $9F29-$9F2C with DCSEL set to 2 through 6. FX_CTRL ($9F29 w/ DCSEL=2) is the master switch for enabling or disabling FX behaviors. When writing an application that uses FX, it is important that the FX mode be preserved and disabled in interrupt handlers in cases where the handler accesses VERA registers or VRAM, including the PSG sound registers. Reading from FX_CTRL returns the current state, and writing 0 to FX_CTRL suspends the FX behaviors so that the VERA can be accessed normally without mutating other FX state.
Which addresses correspond to which ? seems you have VERA.control is $9F25 ..
then $9F29 - DC_VIDEO becomes FX_CTRL in DCSEL=2
/* Select ADDR3 */
VERA.control = 0x03; /* then in that case, this is setting DCSEL=2 and ADDRSEL=1
and then!
/* Line helper mode */
VERA.display.video |= 0x01; /* now instead of DC_VIDEO, we are DCSEL=2, this is $9F29 ? this is FX_CTRL now and can set line helper or other ADDR1 mode as per the doc.
Until you set DCSEL=2 , VERA.display.video would be DC_VIDEO .. and |=1 would do nothing in most cases.. unless you are using composite/S-Video, in which case you just turned your screen off.
The video output mode can be selected using OUT_MODE in DC_VIDEO.
OUT_MODE Description
0 Video disabled
1 VGA output
2 NTSC (composite/S-Video)
3 RGB 15KHz, composite or separate H/V sync, via VGA connector
/* Line helper mode */
VERA.display.video |= 0x01;
yeah....no..
DCSEL is 2x ... I dunno why thyy did that..
the bits are assigned with bit 0=ADDRSEL
then the higher bits from DCSEL ..
Its bit7 - #128 => reset
then bits 6-1 for DCSEL .... 0,2,4,6,8,10,12 are only ones used becuse its 0-6 ...
then bit 0 for ADDRSEL
1 bit RESET | 6 bits for DCSEL | 1bit ADDRSEL
Then you're setting .... ADDRESS low to ????
There is no ADDR3 ... only ADDR0 and ADDR1, for DATA0 and DATA1 ..
ADDR != DCSEL
FX features are controlled mainly by registers $9F29-$9F2C with DCSEL set to 2 through 6. FX_CTRL ($9F29 w/ DCSEL=2) is the master switch for enabling or disabling FX behaviors. When writing an application that uses FX, it is important that the FX mode be preserved and disabled in interrupt handlers in cases where the handler accesses VERA registers or VRAM, including the PSG sound registers. Reading from FX_CTRL returns the current state, and writing 0 to FX_CTRL suspends the FX behaviors so that the VERA can be accessed normally without mutating other FX state.
Which addresses correspond to which ? seems you have VERA.control is $9F25 ..
then $9F29 - DC_VIDEO becomes FX_CTRL in DCSEL=2
/* Select ADDR3 */
VERA.control = 0x03; /* then in that case, this is setting DCSEL=2 and ADDRSEL=1
and then!
/* Line helper mode */
VERA.display.video |= 0x01; /* now instead of DC_VIDEO, we are DCSEL=2, this is $9F29 ? this is FX_CTRL now and can set line helper or other ADDR1 mode as per the doc.
Until you set DCSEL=2 , VERA.display.video would be DC_VIDEO .. and |=1 would do nothing in most cases.. unless you are using composite/S-Video, in which case you just turned your screen off.
The video output mode can be selected using OUT_MODE in DC_VIDEO.
OUT_MODE Description
0 Video disabled
1 VGA output
2 NTSC (composite/S-Video)
3 RGB 15KHz, composite or separate H/V sync, via VGA connector
-
- Posts: 13
- Joined: Sat Jan 27, 2024 7:22 pm
Re: How to Use VERA FX "Line Helper"?
Okay, getting closer. Because VERA as defined in cx16.h doesn't reflect VERA FX, I redefined them below.
It didn't say which DATA to write to, but I guessed DATA1 and now it's drawing something, but like showing 15 points instead of 30, and it might be corrupting memory because some diagnostic strings are messed up. It's not recognizing the 0.5 increment, instead jumping two pixels, but I'm setting FX_CTRL to 4-bit, and ADDRx_H to "Nibble Increment".
Is there something I'm forgetting?
#define VERA_ADDRx_L (*((unsigned char *)0x9F20)) #define VERA_ADDRx_M (*((unsigned char *)0x9F21)) #define VERA_ADDRx_H (*((unsigned char *)0x9F22)) #define VERA_DATA0 (*((volatile unsigned char *)0x9F23)) #define VERA_DATA1 (*((volatile unsigned char *)0x9F24)) #define VERA_CTRL (*((unsigned char *)0x9F25)) /* DCSEL = 2 */ #define VERA_FX_CTRL (*((unsigned char *)0x9F29)) #define VERA_FX_TILEBASE (*((unsigned char *)0x9F2A)) #define VERA_FX_MAPBASE (*((unsigned char *)0x9F2B)) #define VERA_FX_MULT (*((unsigned char *)0x9F2C)) /* DCSEL = 3 */ #define VERA_FX_X_INCR_L (*((unsigned char *)0x9F29)) #define VERA_FX_X_INCR_H (*((unsigned char *)0x9F2A)) #define VERA_FX_Y_INCR_L (*((unsigned char *)0x9F2B)) #define VERA_FX_Y_INCR_H (*((unsigned char *)0x9F2C)) int i; /* DCSEL = 2 */ VERA_CTRL = 0x04; /* 4-bit and line helper mode */ VERA_FX_CTRL = 0x05; /* Select ADDR1 */ VERA_CTRL = 0x05; /* Set ADDR1 */ VERA_ADDRx_L = 0x40; VERA_ADDRx_M = 0x02; /* Always increment by 160 */ VERA_ADDRx_H = 0xD0; /* Select ADDR0 */ VERA_CTRL = 0x04; /* Set ADDR0 */ VERA_ADDRx_L = 0x00; VERA_ADDRx_M = 0x00; /* Occasionally increment by 0.5 */ VERA_ADDRx_H = 0x04; /* DCSEL = 3 */ VERA_CTRL = 0x06; /* Set slope */ VERA_FX_X_INCR_L = 0x00; VERA_FX_X_INCR_H = 0x01; /* Draw the line */ for (i = 0; i < 30; ++i) VERA_DATA1 = GREEN; /* Restore traditional VERA behaviour */ VERA_CTRL = 0x00;
It didn't say which DATA to write to, but I guessed DATA1 and now it's drawing something, but like showing 15 points instead of 30, and it might be corrupting memory because some diagnostic strings are messed up. It's not recognizing the 0.5 increment, instead jumping two pixels, but I'm setting FX_CTRL to 4-bit, and ADDRx_H to "Nibble Increment".
Is there something I'm forgetting?
#define VERA_ADDRx_L (*((unsigned char *)0x9F20)) #define VERA_ADDRx_M (*((unsigned char *)0x9F21)) #define VERA_ADDRx_H (*((unsigned char *)0x9F22)) #define VERA_DATA0 (*((volatile unsigned char *)0x9F23)) #define VERA_DATA1 (*((volatile unsigned char *)0x9F24)) #define VERA_CTRL (*((unsigned char *)0x9F25)) /* DCSEL = 2 */ #define VERA_FX_CTRL (*((unsigned char *)0x9F29)) #define VERA_FX_TILEBASE (*((unsigned char *)0x9F2A)) #define VERA_FX_MAPBASE (*((unsigned char *)0x9F2B)) #define VERA_FX_MULT (*((unsigned char *)0x9F2C)) /* DCSEL = 3 */ #define VERA_FX_X_INCR_L (*((unsigned char *)0x9F29)) #define VERA_FX_X_INCR_H (*((unsigned char *)0x9F2A)) #define VERA_FX_Y_INCR_L (*((unsigned char *)0x9F2B)) #define VERA_FX_Y_INCR_H (*((unsigned char *)0x9F2C)) int i; /* DCSEL = 2 */ VERA_CTRL = 0x04; /* 4-bit and line helper mode */ VERA_FX_CTRL = 0x05; /* Select ADDR1 */ VERA_CTRL = 0x05; /* Set ADDR1 */ VERA_ADDRx_L = 0x40; VERA_ADDRx_M = 0x02; /* Always increment by 160 */ VERA_ADDRx_H = 0xD0; /* Select ADDR0 */ VERA_CTRL = 0x04; /* Set ADDR0 */ VERA_ADDRx_L = 0x00; VERA_ADDRx_M = 0x00; /* Occasionally increment by 0.5 */ VERA_ADDRx_H = 0x04; /* DCSEL = 3 */ VERA_CTRL = 0x06; /* Set slope */ VERA_FX_X_INCR_L = 0x00; VERA_FX_X_INCR_H = 0x01; /* Draw the line */ for (i = 0; i < 30; ++i) VERA_DATA1 = GREEN; /* Restore traditional VERA behaviour */ VERA_CTRL = 0x00;
Re: How to Use VERA FX "Line Helper"?
I love how this doc has a TODO: add example for 4-bit line drawing..
https://docs.google.com/document/d/1q34 ... 54PTg/edit
but...
Set your ADDR1 increment in the direction you will always increment each step (+1, -1, -320 or +320)
Set your ADDR0 increment in the direction you will only increment when a horizontal or vertical pixel-line is crossed (+1, -1, -320 or +320)
so your address 1 is set increment 160 bytes, always.. OK.. drawing a line going always down, sometimes going left/right...
address 0 is set... wait... is that 'increment 0 + a nibble' or 'increment zero nibbles' ?
jumping 2 pixels ? hmm... will have to try it..
"the diagnostic strings" bit sounds like maybe there's an interrupt or something else trying to use the VERA at the same time?
In my own programming I have a MUTEX to keep the interrupt from touching VERA during sections of main program that are altering VRAM ... ( and debug output is done by the main program before giving up the MUTEX ) .
is just short snippet provided here, without larger framework seems what is here should work.. or at least give 30 dots out..
the slope is 0.5 ..
https://docs.google.com/document/d/1q34 ... 54PTg/edit
but...
Set your ADDR1 increment in the direction you will always increment each step (+1, -1, -320 or +320)
Set your ADDR0 increment in the direction you will only increment when a horizontal or vertical pixel-line is crossed (+1, -1, -320 or +320)
so your address 1 is set increment 160 bytes, always.. OK.. drawing a line going always down, sometimes going left/right...
address 0 is set... wait... is that 'increment 0 + a nibble' or 'increment zero nibbles' ?
jumping 2 pixels ? hmm... will have to try it..
"the diagnostic strings" bit sounds like maybe there's an interrupt or something else trying to use the VERA at the same time?
In my own programming I have a MUTEX to keep the interrupt from touching VERA during sections of main program that are altering VRAM ... ( and debug output is done by the main program before giving up the MUTEX ) .
is just short snippet provided here, without larger framework seems what is here should work.. or at least give 30 dots out..
the slope is 0.5 ..
0
1
2
3
4
5
-
- Posts: 13
- Joined: Sat Jan 27, 2024 7:22 pm
Re: How to Use VERA FX "Line Helper"?
Thank you for reviewing. I saw that document, too, and was disappointed that there wasn't any sample code!
Using 4-bit mode:
Here's a screenshot using increments of always 160 and occasionally 1.
/* Just line helper mode */ VERA_FX_CTRL = 0x01; ... /* Occasionally increment by 1 */ VERA_ADDRx_H = 0x10;
It's drawing 30 points, albeit they are skipping horizontal pixels (because in 4-bit mode), and the diagnostic output, including "ready." at the end is correct.
Here's a screenshot using increments of always 160 and occasionally 0.5.
/* 4-bit and line helper mode */ VERA_FX_CTRL = 0x05; ... /* Occasionally increment by 0.5 */ VERA_ADDRx_H = 0x04;
It's drawing only 15 points, skipping horizontal pixels, and the diagnostic output is messed up:
I'm thinking the missing pixels are being written somewhere unexpected, and corrupting memory. This is suggesting to me that either the documentation is incorrect, the emulator has a bug, or VERA has a bug.
Using 4-bit mode:
Here's a screenshot using increments of always 160 and occasionally 1.
/* Just line helper mode */ VERA_FX_CTRL = 0x01; ... /* Occasionally increment by 1 */ VERA_ADDRx_H = 0x10;
It's drawing 30 points, albeit they are skipping horizontal pixels (because in 4-bit mode), and the diagnostic output, including "ready." at the end is correct.
Here's a screenshot using increments of always 160 and occasionally 0.5.
/* 4-bit and line helper mode */ VERA_FX_CTRL = 0x05; ... /* Occasionally increment by 0.5 */ VERA_ADDRx_H = 0x04;
It's drawing only 15 points, skipping horizontal pixels, and the diagnostic output is messed up:
I'm thinking the missing pixels are being written somewhere unexpected, and corrupting memory. This is suggesting to me that either the documentation is incorrect, the emulator has a bug, or VERA has a bug.
Re: How to Use VERA FX "Line Helper"?
I'm making two guesses before I check to be sure:
1) The value you write to VERA_DATA1, "GREEN", is probably the value 0x05, which in 4-bit bitmap mode will have the effect of drawing a black/transparent "left" pixel and a green "right" pixel for each written byte, including in 4-bit FX mode where only one nybble is written to VRAM at a time. Instead of writing the value 0x05, you probably want 0x55.
2) The Kernal doesn't know the VERA is still in 4-bit FX (or FX line-helper) mode when it goes to print strings to the screen, so remember to turn those functions off before printing anything.
Edit: Yes, I checked both the Kernal ROM and the VERA's verilog, so I think these are indeed the issues you're experiencing.
1) The value you write to VERA_DATA1, "GREEN", is probably the value 0x05, which in 4-bit bitmap mode will have the effect of drawing a black/transparent "left" pixel and a green "right" pixel for each written byte, including in 4-bit FX mode where only one nybble is written to VRAM at a time. Instead of writing the value 0x05, you probably want 0x55.
2) The Kernal doesn't know the VERA is still in 4-bit FX (or FX line-helper) mode when it goes to print strings to the screen, so remember to turn those functions off before printing anything.
Edit: Yes, I checked both the Kernal ROM and the VERA's verilog, so I think these are indeed the issues you're experiencing.
Re: How to Use VERA FX "Line Helper"?
I wonder if after that, the last thing is this -
/* Occasionally increment by 1 */
VERA_ADDRx_H = 0x10;
but.. you want occasionally increment by 1 _nibble_ ...
VERA_ADDRx_H = 0x14;
the auto-increment doesn't automatically factor in the FX being in 4bit...
The FX routines also pay no attention to the target being 4bit or 8bit... which could be fun, as using 4bit mode with BYTE increments against an 8bit target could allow you to overwrite either nibble, possibly just switching palette offset of the pixel, maybe with a given palette that means could lighten/darken or do color shifts just changing 1 nibble each byte... .
/* Occasionally increment by 1 */
VERA_ADDRx_H = 0x10;
but.. you want occasionally increment by 1 _nibble_ ...
VERA_ADDRx_H = 0x14;
the auto-increment doesn't automatically factor in the FX being in 4bit...
The FX routines also pay no attention to the target being 4bit or 8bit... which could be fun, as using 4bit mode with BYTE increments against an 8bit target could allow you to overwrite either nibble, possibly just switching palette offset of the pixel, maybe with a given palette that means could lighten/darken or do color shifts just changing 1 nibble each byte... .
Re: How to Use VERA FX "Line Helper"?
According to the FX docs:
...and I can indeed confirm this in the VERA's verilog: if you want to increment by 1 nybble at a time, you must have FX 4-bit mode enabled (bit 2 of $9F29[DCSEL=2]), and Address Increment (upper nybble of $9F22) must be set to 0. Otherwise, the VERA will not care about $9F22.2 (Nibble Increment) at all.For the Nibble Increment bit to have effect, the main Address Increment must be set to 0, and the 4-bit Mode bit must be set in FX_CTRL ($9F29, DCSEL=2).
So in other words, instead of writing 0x14, just write 0x04, and then you'll increment by 1 nybble (or 0.5 bytes, as the docs refer to it). Unfortunately, you can't increment by both a byte amount and a nybble at the same time (e.g., 2.5 bytes), but you already can't increment by arbitrary values either (e.g., 5 bytes), so it seems reasonable.
Yep! It's also nice when you just want to do general-purpose pixel plotting in 16-color bitmap mode; you don't need to grab a copy of the current byte (2 pixels) from VRAM before modifying just one pixel within. In addition to FX 4-bit mode, there's also FX Transparent Writes mode ($9F29.7), where writing a "0" results in no change to VRAM, which can also simplify blitting code.The FX routines also pay no attention to the target being 4bit or 8bit... which could be fun, as using 4bit mode with BYTE increments against an 8bit target could allow you to overwrite either nibble, possibly just switching palette offset of the pixel, maybe with a given palette that means could lighten/darken or do color shifts just changing 1 nibble each byte... .