disable/enable LCD screen (bit7 rLCDC $FF40)

Discussion of programming and development for the original Game Boy and Game Boy Color.
Post Reply
sdm
Posts: 412
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

disable/enable LCD screen (bit7 rLCDC $FF40)

Post by sdm »

I'm having trouble turning my screen off and on. Writing to Bit7 rLCDC pops up error (BGB) "disabling screen outside vblank" - so I made a buffer to which it writes changes in rLCDC, which transfers it in VBLANK:


LD A,%00010011 ; disable screen (off bit7)
LD [rLCDC_Buffer],A

LD A,%10010011 ; enable screen (on bit7)
LD [rLCDC_Buffer],A



in VBlank_Handler:

LD A,[rLCDC_Buffer]
LDH [rLCDC],A ; $FF40


Unfortunately, I still have this error (BGB "disabling screen outside vblank") when I write to the buffer, and yet it is transferred to rLCDC in the VBLANK interrupt, where do I make a mistake?

Please correct me in case of an error - GB, like NES, allows writing / reading in PPU and VRAM only during VBLANK or when the screen is off (set off bit7 rLCDC ($ FF40)?
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: disable/enable LCD screen (bit7 rLCDC $FF40)

Post by lidnariq »

The Game boy permits writes to most of VRAM during hblank too, depending on how many sprites are present on the scanline, where exactly they are, and where the "window" is.

You should be able to check the contents of the LY register when the write happens - it should be in the range of 144 to 153 for it to be safe to turn off the LCD.

Also consider using Emulicious - I don't know how the two compare in terms of debugging capabilities.
sdm
Posts: 412
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: disable/enable LCD screen (bit7 rLCDC $FF40)

Post by sdm »

Ok, I think everything is working, I also had to turn off interrupts before turning off the screen, and turn on interrupts after turning on the screen.

SELECT / START changes game screens, no bugs in BGB.
DisableScreen:

LDH A,[rLCDC]
AND A,%10000000
JR Z,Screen_OFF
LD B,$91
CALL wait_ly

LD A,%00010011
LDH [rLCDC],A

Screen_OFF:
RET


;----------

EnableScreen:

LD A,%10010011
LDH [rLCDC],A

RET

;----------

wait_ly:

LD C,LOW(rLY)

no_same_ly:

LD A,[$FF00+C]
CP A,B
JR NZ,no_same_ly

RET

;----------------------

DI
CALL DisableScreen

; update nametable and sprites

CALL EnableScreen
EI
Attachments
gbtest.gb
(128 KiB) Downloaded 45 times
sdm
Posts: 412
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: disable/enable LCD screen (bit7 rLCDC $FF40)

Post by sdm »

I still have problems writing to VRAM during VBLANK. I have a simple code that transfers a value from variable frames to a screen address and displays the appropriate digits.
Everything should work, but BGB shows "accessing inaccessible VRAM" error. Of course, other emulators like Emulicious work fine, but it's weird nonetheless.
VBlank routine is executed whenever LY = 144. During this period video hardware is not using VRAM so it may be freely accessed.
So I don't understand why this is wrong. It may be "oversensitivity" of the BGB emulator, but it still makes me wonder why.
VBlank_Handler:

PUSH AF
PUSH BC
PUSH DE
PUSH HL

CALL Run_DMA ; Update OAMMirror to OAM via DMA.
CALL UpdateScore ; Update BGR Score Tiles.

XOR A
LDH [rSCY],A ; ScrollY.
LDH [rSCX],A ; ScrollX.

LD A,1
LD [VBlank_Flag],A

POP HL
POP DE
POP BC
POP AF

RETI
UpdateScore:

LD HL,$9A07 ; vram nametable addres
LD B,$30 ; start tile graphics digits ($30-39 / 0-9)
LD A,[Score_Digit5]
ADD B
LD [HL],A

LD HL,$9A08
LD B,$30
LD A,[Score_Digit4]
ADD B
LD [HL],A

LD HL,$9A09
LD B,$30
LD A,[Score_Digit3]
ADD B
LD [HL],A

LD HL,$9A0A
LD B,$30
LD A,[Score_Digit2]
ADD B
LD [HL],A

LD HL,$9A0B
LD B,$30
LD A,[Score_Digit1]
ADD B
LD [HL],A

LD HL,$9A0C
LD B,$30
LD A,[Score_Digit0]
ADD B
LD [HL],A

RET
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: disable/enable LCD screen (bit7 rLCDC $FF40)

Post by tepples »

On what value of LY does the first "inaccessible" write occur? The debugger in bgb should display that.

If the sum of Run_DMA and UpdateScore exceed 10 scanlines' worth of CPU time, which equals 1140 cycles, the last write will occur out of vblank. Something with several divisions by 10 is likely to cause that. UpdateScore as you've defined it doesn't appear to cause that. However:

The register IF contains a set of interrupts that are waiting to happen. While interrupts are disabled, these continue to accumulate. If you don't write 0 to IF before enabling interrupts, then all these interrupts will happen immediately. To see if this is the problem, put a breakpoint (ld b, b) just before the ei instruction and then step a few instructions in the debugger.

Your code:

Code: Select all

CALL EnableScreen
EI
To investigate:

Code: Select all

CALL EnableScreen
ld b, b
EI
If that ends up being the problem, the solution might look something like this:

Code: Select all

CALL EnableScreen
xor a
ldh [rIF], a
EI
sdm
Posts: 412
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: disable/enable LCD screen (bit7 rLCDC $FF40)

Post by sdm »

LY was at 02.

Thanks! I didn't actually reset the rIF before EI :)
Everything works fine now.

XOR A
LDH [rIF], A
EI

---------

I still have a question related to waiting for the VBLANK - if I have the screen off, DI interrupt disabled and I would like to make a few or a dozen delay frames before turning the screen back on, how should I do it? HALT is blocked, so it will freezee my code completely. Do bit0 FF0F (rIF) status check need to be performed? (Reactivating the EI, doing a few HALTs, and disabling the DI will not work - Here I do not fully understand, why when I have the LCD off, EI cannot be performed?).
DI
CALL Screen_OFF

; add few frames delay before enable LCD.

CALL Screen_ON
XOR A
LDH [rIF],A
EI
Attachments
gb_test.gb
(512 KiB) Downloaded 39 times
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: disable/enable LCD screen (bit7 rLCDC $FF40)

Post by tepples »

What you're seeing is that the vblank interrupt isn't generated while the LCD is off. This is true of the 8-bit Game Boy, in contrast with the NES, Super NES, and GBA. To keep time while the LCD is off, you'll need to use the timer (TIMA/TMA/TAC registers).

Aside: If interrupts are disabled (di), the halt instruction still works so long as interrupts are still getting generated. It waits for the next interrupt enabled in IE and doesn't call the handler. I do this in Border Crossing to wait for vblank while the screen is on. You have to remember to clear IF yourself each time, and if you have other interrupts enabled (STAT, timer, serial, or key input), you need to distinguish them from vblank somehow.
sdm
Posts: 412
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: disable/enable LCD screen (bit7 rLCDC $FF40)

Post by sdm »

It's working now. The problem was a bug in the INIT code. I didn't have the bit2 rIE flag set and the rTAC set - when I did it everything works. That's why HALT was freezing my code, even though I had EI and DI correctly used. All in all, I still do not understand why this was happening, after all Bit2 rIE should not affect it (and not set rTAC)?
LD A,%00000100 ; $FF07 - 4096Hz.
LDH [rTAC],A

XOR A
LDH [rIF],A

LD A,%00000101 ; Allow VBlanks and TIMER.
LDH [rIE],A
HALT now works between screen off and on:

DI
CALL Screen_OFF

XOR A
LDH [rIF],A
EI

HALT ; delay
HALT
HALT
HALT
HALT
HALT
HALT
HALT
HALT
HALT

DI

CALL Screen_ON
XOR A
LDH [rIF],A
EI
Post Reply