Stupid problems with autoread on hardware

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Stupid problems with autoread on hardware
by on (#187385)
I have two problems.

One: the programs I've written have horrible problems with button bounce, much bigger than anything I've written on the NES. Am I doing something wrong, or is this a known flaw?

Two: In trying to diagnose the above button bounce, I wrote a simple program (It's a simplified subset of tepples's spadtest, which does work on my hardware) that draws a series of lines (one pixel per button per vsync) corresponding to the buttons that are pressed. It works correctly in every emulator I've tried (including bsnes-plus but not higan), but not on hardware (always returning no-buttons-pressed). What could I be doing wrong?
Re: Stupid problems with autoread on hardware
by on (#187386)
If you're only reading input from $421x instead of using the NES-style registers, are you remembering to wait for bit 0 of $4212 to be cleared before reading?

bsnes-plus still (incorrectly) treats auto joypad read as being instantaneous, but higan doesn't. (Consider that added to the TODO list, I guess.)
Re: Stupid problems with autoread on hardware
by on (#187391)
Yeah, there's a poll loop for not VBl, then for yes VBl, then for not autoread...

I probably should have just included the core loop in the first place:
Code:
loop:
        seta8

loop1:
        bit VBLSTATUS   ; Wait for NOT vblank
        bmi loop1
loop2:
        bit VBLSTATUS   ; Wait for NEW vblank
        bpl loop2

        lda #$01
padwait:
        bit VBLSTATUS   ; wait for autoread done
        bnz padwait

        ;; first we need to transform the count into a PPU address
        ;;  kjihgfedcba (bits of count)
        ;; kjihgfed0cba
        seta16
        lda count
        asl
        and #$0FF0
        clc
        adc #56*16*2
        sta temp
        lda count
        and #7
        ora temp
        sta temp
        sta PPUADDR

        seta8
        lda JOY1CUR+1
        stz PPUDATA+1
        sta PPUDATA

        seta16
        lda temp
        ora #8
        sta PPUADDR

        inc count

        seta8
        lda JOY1CUR
        stz PPUDATA+1
        sta PPUDATA
        lda #$ff
        sta PPUDATA+1
        stz PPUDATA
        jmp loop
Re: Stupid problems with autoread on hardware
by on (#187392)
Maybe NMIs need to be on? That's just a wild guess, based on nothing other than both those functions being enabled by the same register.

Here's what the code in my NSF player looks like, including the main loop.

Code:
...
        lda #%10000001  ; enable controller and NMI
        sta $4200
...


Code:
;---------------------------------------------------
check_buttons:       
-
        lda $4212
        and #%00000001
        bne -

        lda joy1
        sta joy1old
        lda joy1+1
        sta joy1old+1

        lda $4218
        sta joy1
        lda $4219
        sta joy1+1

        rts

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


Code:
;============================================
;                       Main loop
;                               waits for 60hz interrupt
;                               runs NES code
;                               runs graphics display code
;                               emulates 60hz length counter
;                              [runs temporary linear counter simulator]
;                               updates APU with NES register contents
;                               checks joypads and processes such
;                               resets $4016 (used by $4015 evaluator)
;                             go to beginning of main loop
;--------------------------------------------

wait:
        wai

;        jsr set_bg_nsf

       
        jsl $7F5000     ; NSF play

        phk
        plb

        jsr display_options

        jsr detect_changes
;        jsr emulate_sweep
        jsr backup_regs
;        jsr emulate_linear_counter
        jsr emulate_length_counter
        jsr triangle_tempfix
        jsr update_dsp

        jsr check_buttons
        jsr proc_buttons

;        jsr read_oam

        lda #0
        sta $7F4016

;        jsr set_bg_black

        jmp wait
Re: Stupid problems with autoread on hardware
by on (#187394)
The easy way:

1. Set bit 0 of $4200 (NMITIMEN) to 1. This makes the SNES automatically populate registers $4218-421f with joypad data "shortly" after VBlank (more on that in a second).

2. Wait for bit 0 of $4212 (HVBJOY) to be 0 before reading joypad data (registers $4218-421f); if bit 0 is 1, the joypads aren't ready to be read yet.

Alternately, if you're timing-focused, you can wait approximately 215 microseconds *from the beginning/start of VBlank* before reading the controller registers -- but IMO, just use the $4212 polling method described, it's reliable. It's common to stick some general purpose DMA at the start of VBlank, which often ends up taking more than 215 microseconds (depends on amount of data being transferred), so that's an alternate approach to the $4212 poll. But I still recommend just polling $4212. :-)

I've attached documentation referencing this timing (it's mentioned in 2 places) and all of the above. I may end up getting the files in the wrong order; if so, my apologies. I'll follow up with more attachments in a subsequent post (note to Memblers and/or Tepples: 3 attachments per post is annoying, can we bump that up to 5? I've run into this limit 2 or 3 times in the past couple months).

The superfamicom.org wiki actually covers all of this pretty well (including dealing with held vs. tapped buttons) -- this is not a resource I commonly refer to, but in this case it's great! https://wiki.superfamicom.org/snes/show ... ller+Input

Some additional things:

This is a 65816: you can read $4218 with a 16-bit register and it'll read both $4218 and $4219 (all buttons/directions for joypad #1) in a single lda/ldx/ldy. It's weird that both of you read these registers with 8-bit registers -- maybe habits from 6502/NES days? While I'm on that subject...

The SNES has registers $4016 and $4017 which are NES-compatible for joypad reads (the only read-1-bit-at-a-time method). If you choose to use it, you need to make sure bit 0 of $4200 is set to 0, after which you can just read $4016/4017 like on the NES. And also when bit 0 of $4200 is 0: bit 0 of $4016 *on a read* acts as an indicator to tell you if the joypad is connected or not (I've never actually used this myself, I just always assume joypads are connected, but the code for detection makes sense); see the final attachment describing the registers.

There is a weird "edge case" for controller input (this would be of interest to the hardware folks, as it pertains to level vs. edge detection) _if_ you ever disable joypad reading after enabling it. If you want those details, including code, I can provide it -- just ask.

Edit: few edits to clarify additional points, and fix a register typo.

(2018/08/29 Edit: attachments removed.)
Re: Stupid problems with autoread on hardware
by on (#187395)
(2018/08/29 Edit: attachments removed.)
Re: Stupid problems with autoread on hardware
by on (#187407)
Another thought: are you initializing $2115?

Since you're always writing the high byte of PPUDATA first, 00 is the value it seems like it should have, but if you're assuming it has that state on powerup then it might explain why there are problems in higan and on the real hardware.
Re: Stupid problems with autoread on hardware
by on (#187414)
The PNG file states that it takes 576 slow cycles to finish reading. This is just barely more than the time to send an entire 544-byte display list to OAM using a DMA copy.

koitsu wrote:
bit 0 of $4016 *on a read* acts as an indicator to tell you if the joypad is connected or not (I've never actually used this myself, I just always assume joypads are connected, but the code for detection makes sense)

The standard controller does in fact return all 1's after the report (8-bit for NES or 16-bit for Super NES), and my controller detection demo for NES currently relies on this. But I've seen third-party NES controllers that instead return all 0's, particularly the U-Force.
Re: Stupid problems with autoread on hardware
by on (#187416)
tepples wrote:
The standard controller does in fact return all 1's after the report (8-bit for NES or 16-bit for Super NES), and my controller detection demo for NES currently relies on this. But I've seen third-party NES controllers that instead return all 0's, particularly the U-Force.

I honestly couldn't give less of a shit about licensed third-party controllers, especially garbage like the UForce, but your point about $4016 behaviour is obviously is still valid. This comment now forces me to post the timing thing I mentioned. I don't know whether or not it's relevant to this situation, however. I've pieced together the relevant bits into a single picture.

(2018/08/29 Edit: attachments removed.)
Re: Stupid problems with autoread on hardware
by on (#187461)
koitsu wrote:
This is a 65816: you can read $4218 with a 16-bit register and it'll read both $4218 and $4219 (all buttons/directions for joypad #1) in a single lda/ldx/ldy. It's weird that both of you read these registers with 8-bit registers -- maybe habits from 6502/NES days?
Only because I'm splitting the data across two tiles. Doing it in two 8-bit accesses made more sense to me than relaying the 16-bit value via RAM and then handling it separately. (I've tried the latter and it failed in the same way.)

(I've handled all the other things you mention as common pitfalls)

Well, I've cleaned up the full source a little bit. Maybe someone else can figure out what I'm doing wrong ... or at least establish whether it also fails on their hardware.
Re: Stupid problems with autoread on hardware
by on (#188011)
From anomie's docs:
Code:
 When enabled, the SNES will read 16 bits from each of the 4 controller port
 data lines into registers $4218-f. This begins between H=32.5 and H=95.5 of
 the first V-Blank scanline, and ends 4224 master cycles later. Register $4212
 bit 0 is set during this time.

So waiting for "begin of vblank", does somehow ensure that your code will collide with autoread.
Even if you've tested the joypad busy bit to be cleared - it will be set shortly thereafter.
Re: Stupid problems with autoread on hardware
by on (#188013)
Ohhhhhhhhhhhhhhhhh. That tiny delay between vblank starting and autoread starting is just enough CPU cycles to screw everything up.

So evidently I can just poll for "autoread" then "not autoread" and always end up a few scanlines into vblank.

That you so much!