Author |
Message |
- Joined: 12 Feb 2013
- Posts: 80
|
Weird issue setting the palette
Posted: Sat Oct 26, 2019 3:54 pm
|
I'm new to master system programming, but I encountered a strange issue with setting the palette.
To test, I prepared the palette as follows:
PALETTE:
ds.b $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F
ds.b $10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1A $1B $1C $1D $1E $1F
My first thought was to set the palette ram this way:
ld hl, PALETTE
ld b, $20
ld c, Port_VDPData
otir
But the resulting palette came out wrong:
Quote $00 $01 $02 $03 $05 $06 $07 $08 $0A $0B $0C $0D $0E $0F $11 $12
$13 $14 $16 $17 $18 $19 $1A $1C $1D $1E $1F $1F $1A $00 $00 $00
So I tried it this way so I could watch each palette entry get set in the debugger:
ld hl, PALETTE
ld b, $20
ld c, Port_VDPData
outi
ld a, b
jnz, -5
And to my surprise this way worked with the resulting palette correct:
Quote $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F
$10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1A $1B $1C $1D $1E $1F
So what is the difference in the 2 methods?
|
|
|
- Joined: 28 Sep 1999
- Posts: 1197
|
Posted: Sat Oct 26, 2019 4:51 pm
|
The first way should have worked fine, it's common to use OTIR in SMS games.
Maybe the problem comes from another place, like an interrupt could be triggered while you are loading the palette? Or perhaps the code that sets the CRAM write address has a problem? (that part wasn't included in your code)
If you are testing in an emulator I'd see if you get different results in another one. I'd also try removing parts of your program to make a simple test case to narrow down what the problem could be.
|
|
|
- Joined: 08 Sep 2018
- Posts: 270
|
Posted: Sat Oct 26, 2019 4:58 pm
|
Maybe try loading it in 16 color chunks
dont forget to select your palette index first
also set your palette data up like this
PaletteData:
ds.b $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F
PaletteDataEnd:
PaletteData_2:
ds.b $10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1A $1B $1C $1D $1E $1F
PaletteDataEnd_2:
then set VRAM write to the first palette address in CRAM
;where color palette 1 is at $c000 and color palette index 2 is at $c010
ld hl,$c000 ;palette address 1
ld a,h
out ($bf),a
ld a,l
out ($bf),a
then load in palette.
ld hl,PaletteData
ld b,(PaletteDataEnd-PaletteData)
ld c,$be
otir
Also may help do disable interrupts prior to loading stuff into vram then re-enabling it when youre done.
|
|
|
- Joined: 12 Feb 2013
- Posts: 80
|
Posted: Sat Oct 26, 2019 6:00 pm
|
Thanks guys. Must be an interrupt. I can't think of any other reason.
The palette index is set properly with the command word.
Anyway, I'll keep the second version since it works. I just wanted to be sure there wasn't some quirk with the sms hardware I wasn't aware of.
|
|
|
- Joined: 01 Jan 2014
- Posts: 331
|
Posted: Sat Oct 26, 2019 10:47 pm
|
VDP can be in three possible states, vblank, active display or off (which is permanent vblank).
During active display your accesses to the VDP need to be padded by 26 cycles (its busy rending your screen). Failing to do so will result in corrupted data like you are seeing. You can get around this by writing during vblank or turning the screen off.
You can test this in Emulicious. In the master system options you will see the 'Enable VDP Constraints' option which you can toggle on and off.
|
|
|
- Joined: 12 Feb 2013
- Posts: 80
|
Posted: Sat Oct 26, 2019 11:17 pm
|
Yes, that is what I have read, but you can see the code above. It is virtually identical. Why would one have corruption and the other be fine?
And the corrupted values are always the same. So it's not random corruption. Weird.
|
|
|
- Joined: 01 Jan 2014
- Posts: 331
|
Posted: Sat Oct 26, 2019 11:30 pm
|
Its not identical though. otir means 21 cycles per write which is too short so corruption. The second routine is 30 cycles per write so you are safe.
In short you cannot use otir during active display.
|
|
|
- Joined: 12 Feb 2013
- Posts: 80
|
Posted: Sun Oct 27, 2019 1:16 am
|
Interesting. Thanks for the information. It is definitely in active display so that solves the mystery.
This code is close before it and sets active display on:
ld a, $E0
di
out (Port_VDPAddress), a
ld a, $81
out (Port_VDPAddress), a
ei
From what I can see in the code above:
the LSB is $E0 = 11100000
the MSB is $81 = 10000001
The MSB of the command word has the two C-bits at the top and here they are 10 so it is a VDP register write and the first bit being set means Register 1 is updated.
The LSB value is what the register gets so here it is 11100000 with the 7th bit D6 indicating Display visible. Since it is a 1, the display is active.
Whew, that was a weird condition for the corruption. Being new to the master system, I didn't know what was going on. Thanks for all the help everyone.
|
|
|
- Joined: 08 Sep 2018
- Posts: 270
|
Posted: Sun Oct 27, 2019 1:35 am
|
hey also btw it might help to have some kind of interrupt handler and status indicator that way when the VDP enters VBlank you can execute any copies/writes to VRAM or CRAM at the right time.
when you write to the VDP you should disable interrupts or turn off the VDP then turn it back on when you're done writing.
Be mindful of the amount of data you need to write to VRAM as well else itll take too long and cause issues. Typically I like to write my palette, map, tiles, sprites and SAT during a map init routine and then actively swap sprites or palettes and update the map if needed during the main loop since those objects are small relative to rewriting the entire screen and updating a whole tileset.
|
|
|
- Joined: 05 Sep 2013
- Posts: 3763
- Location: Stockholm, Sweden
|
Posted: Sun Oct 27, 2019 9:21 pm
|
you're simply writing too fast with display on (and not during vblank).
you need at least 26 cycles between writes to do that properly, but you're using OTIR which takes just 21 cycles, so you're overwriting approximately 1 byte every 5 writes, and that's why using a slower loop it works fine
edit: OK I didn't see psidum already clarified that, sorry for repeating.
|
|
|