HDMA background color modification results in wrong colors

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Post Reply
Posts: 3
Joined: Sat Dec 01, 2018 6:02 pm

HDMA background color modification results in wrong colors

Post by FeeeshMeister » Fri Apr 19, 2019 8:26 pm

I now realize I'm an idiot and wrote "DB." instead of ".DB".

I have a simple SNES program that is supposed to color the top part of the screen a light blue color, and the bottom of the screen a dark blue color. Whenever I try to run it in an emulator, the image results in an interlaced picture with two different incorrect colors appearing on different scanlines. The code for the program is as follows.

Code: Select all

.include "Header.inc"
.include "Snes_Init.asm"

; Needed to satisfy interrupt definition in "Header.inc".
	;Set up HDMA
	stz		$420C		; Reset HDMA
	lda		#%00000010	; A to B, Absolute, (3 bits unused), 2 bytes to 1 register
	sta		$4300
	lda		#$22		; Modify address $(21)22 (Background Color)
	sta		$4301
	lda		#$00		; Set HDMA table bank to 7E
	sta		$4302		
	rep		#$20
	lda		HDMA_Table	; Set HDMA table to HDMA table pointer
	sta		$4303		
	lda		#%00000001	; Activate the first HDMA channel
	sta		$420C


    ; Initialize the SNES.
	; Initialize input

    ; Set the background color to blue.
    sep     #$20        ; Set the A register to 8-bit.
    lda     #%10000000  ; Force VBlank by turning off the screen.
    sta     $2100
	lda		#%01110101	; 1-BG4 tilesize = 8x8 (not used), 2-BG3 tilesize = 16x16, 3-BG2 tilesize = 16x16, 4-BG1 tilesize = 16x16, Background priority set to 0 (?), 6-8- Set graphics to Mode 5
	sta		$2105
    lda     #%11000111  ; Load the low byte of the blue color.
    sta     $2122
    lda     #%01100001  ; Load the high byte of the blue color.
    sta     $2122

	; Set up HDMA table
DB.	#$7F		; Wait for 127 scanlines after transferring
DB.	#%11000111	; Low byte of light blue color
DB.	#%01100001	; High byte of light blue color
DB.	#$34		; Wait for 52 scanlines after transferring
DB.	#%11000111	; Low byte of light blue color
DB.	#%01100001	; High byte of light blue color
DB.	#$01		; Do not wait for a scanline, because this is the final transfer
DB.	#%01100100	; Low byte of dark blue color
DB.	#%01011101	; High byte of dark blue color
DB.	#$00		; Null byte to end table

   lda #$80
   sta $4200   ;Enable VBlank NMI

	rep		#$20		; set the accumulator (A) register into 16 bit mode
	sep		#$10		; set the index (X and Y) register into 8 bit mode

	ldy		#$80		;  we will try to write 128 ($80) bytes in one row ...
	sty		$2115		; ... and we will let the PPU let this know.

	stz		$2116		; Set the VRAM address to zero

    lda     #%00001111  ; End VBlank, setting brightness to 15 (100%).
    sta     $2100

    ; Loop forever.
    jmp MainLoop

Note that I am using mode 5 with interlacing, but I do not think that is related to the problem, because even when using other modes, the problem persists. I included the file in the post. I tested it in Snes9x and zsnes.
(256 KiB) Downloaded 140 times

Posts: 8690
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: HDMA background color modification results in wrong colo

Post by lidnariq » Fri Apr 19, 2019 8:45 pm

There's a bunch of problems here, beyond the one you're identifying.

One is that you can't put tables in the middle of code like that. The CPU doesn't know what you're trying to do and just executes it as though it were code, which it isn't.

Another is that you enable NMIs but don't have an NMI handler, so it just starts executing random crap at address $000000.

And finally, the one you identified in your edit.

Aside from all that, I'd strongly recommend using names instead of magic numbers in your code. It'll help make it easier to understand.

Posts: 218
Joined: Sun Mar 27, 2016 7:56 pm

Re: HDMA background color modification results in wrong colo

Post by Nicole » Fri Apr 19, 2019 9:26 pm

Simply writing two bytes to $2122 is not enough to set the background color. The first color you write to $2122 will be written to color 0, which is the background color, but after that, the next color written to $2122 will be color 1, then color 2, and so on.

To fix this, you need to write 0 to $2121, then the new background color to $2122. You could do this with two HDMA channels, the first writing to $2121 and the second writing to $2122. Alternatively, you could have a single HDMA channel that writes four bytes, writing 0 to $2121 twice, then writing the color to $2122.

Another problem, as lidnariq mentions, is that you don't have an NMI/Vblank handler. You do have a label called Vblank, but the ROM you've given actually jumps to $8020 when NMI happens. There must be a problem with the "Header.inc" mentioned in your code, though you don't show it here.

After you fix that, there's another big problem to be aware of. Ideally, code should be written so that no matter when an interrupt happens, it won't break anything. After all, you often don't know when an interrupt might happen. Think of a game that suffers from slowdown when too many enemies are on the screen. You might not finish your calculations for that frame before Vblank happens! Take a look at this example code:

Code: Select all

    lda #5
    sta MyVariable
    jmp main

    lda #10
    sta AnotherVariable
It loads the number 5 into the A register, then stores it in MyVariable. Meanwhile, there's a Vblank interrupt handler, which loads 10 into A, and stores it in AnotherVariable. You might think this isn't a problem... but what happens if Vblank occurs right here?

Code: Select all

    lda #5
    ; Vblank happens here
    sta MyVariable
First, A is set to 5. Then, Vblank occurs. A is set to 10, and stored in AnotherVariable. Then, the handler returns... and, because A is still 10, 10 is stored in MyVariable. The wrong number has ended up in MyVariable!

To solve this problem, interrupt handlers should save all registers they modify at the start of the handler, and restore them at the end. Here's a fix for the previous example:

Code: Select all

    lda #5
    sta MyVariable
    jmp main

    pha         ; push the current value of A onto the stack
    lda #10
    sta AnotherVariable
    pla         ; pull the old value from the stack and store it in A

User avatar
Posts: 4214
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: HDMA background color modification results in wrong colo

Post by koitsu » Fri Apr 19, 2019 10:00 pm

Also: please do not forget to read $4210 once (a single 8-bit read) at the start of your NMI routine (e.g. pha / lda $4210 / ... / pla / rti). Refer to official manual page B-3 for details.

Post Reply