Corrupt File?
Moderator: Moderators
Re: Corrupt File?
Further on why Japanese expansion controllers listed separately...from the wiki...
https://wiki.nesdev.com/w/index.php/Standard_controller
Famicom $4016:
76543210
---- ----
oooo oMFD
|||| ||||
|||| |||+- Player 1 serial controller data
|||| ||+-- If connected to expansion port (and available), player 3 serial controller data (0 otherwise)
|||| |+--- Microphone in controller 2 on traditional Famicom, 0 on AV Famicom
++++-+---- Open bus
Information only comes in 1 bit at a time, each time the port is read. 8 reads for 8 buttons.
https://wiki.nesdev.com/w/index.php/Standard_controller
Famicom $4016:
76543210
---- ----
oooo oMFD
|||| ||||
|||| |||+- Player 1 serial controller data
|||| ||+-- If connected to expansion port (and available), player 3 serial controller data (0 otherwise)
|||| |+--- Microphone in controller 2 on traditional Famicom, 0 on AV Famicom
++++-+---- Open bus
Information only comes in 1 bit at a time, each time the port is read. 8 reads for 8 buttons.
nesdoug.com -- blog/tutorial on programming for the NES
-
- Posts: 318
- Joined: Mon Jan 30, 2017 5:20 pm
- Location: Colorado USA
Re: Corrupt File?
Could you explain why you shift bits and push the accumulator to the stack? I don't know much about shifting bits and pushing and pulling the stack. I know how to count in binary. Is that what shifting bits is? Just a glorified increment? Isn't the stack what remembers the address of the command It's executing?
Re: Corrupt File?
Bit shifts are used here to combine multiple reads from a serial port into one byte.
Re: Corrupt File?
A bit shift is different from an increment. It moves all bits left or right, which in binary is equivalent to multiplying or dividing by 2.
For example:
0000_0100 is 4
when shifted left once it becomes
0000_1000 which is 8
but when shifted right once becomes
0000_0010 which is 2
The controller hardware only yields a single bit at a time, so shifts are used here to pack all the bits together into a single byte.
For example:
0000_0100 is 4
when shifted left once it becomes
0000_1000 which is 8
but when shifted right once becomes
0000_0010 which is 2
The controller hardware only yields a single bit at a time, so shifts are used here to pack all the bits together into a single byte.
-
- Posts: 318
- Joined: Mon Jan 30, 2017 5:20 pm
- Location: Colorado USA
Re: Corrupt File?
Okay, I understand bit shifting. what about pushing and pulling from the stack? Why do we need that in reading the controllers?
Re: Corrupt File?
Here's what the code snippet marked "SMB3" is doing:
Read the controller state into $00.
Load $00 and save it on the stack.
Read the controller state into $00 again.
Load the value saved on the stack, which is the first controller state.
Compare it to the value in $00, which is the second controller state.
It's trying to read the controller twice, compare the results, and accept the result only if the two reads match. This avoids a bug in the 2A03 CPU, used in the Famicom, NTSC NES, and RGB systems, that causes misreads during DPCM playback.
In general, reading the controllers need not involve the stack. The routine in the file I linked, pads.s, reads the controllers to $0000 and $0001 and saves the first read to $0002 and $0003 instead of the stack.
But the use of the stack to save a temporary value is a common idiom in assembly language programming. We can only speculate why the stack was used instead of a variable on zero page in this particular instance. One possibility is saving code size, as PHA and PLA are one byte shorter than STA to zero page or LDA from zero page. Another is overwriting fewer local variables so as not to disturb variables in use by the caller.
Read the controller state into $00.
Load $00 and save it on the stack.
Read the controller state into $00 again.
Load the value saved on the stack, which is the first controller state.
Compare it to the value in $00, which is the second controller state.
It's trying to read the controller twice, compare the results, and accept the result only if the two reads match. This avoids a bug in the 2A03 CPU, used in the Famicom, NTSC NES, and RGB systems, that causes misreads during DPCM playback.
In general, reading the controllers need not involve the stack. The routine in the file I linked, pads.s, reads the controllers to $0000 and $0001 and saves the first read to $0002 and $0003 instead of the stack.
But the use of the stack to save a temporary value is a common idiom in assembly language programming. We can only speculate why the stack was used instead of a variable on zero page in this particular instance. One possibility is saving code size, as PHA and PLA are one byte shorter than STA to zero page or LDA from zero page. Another is overwriting fewer local variables so as not to disturb variables in use by the caller.
Re: Corrupt File?
Just to be clear, you don't need to use the stack to read the controllers, it's just that one specific implementation used it. Everything in programming can be done a thousand different ways.DementedPurple wrote:Okay, I understand bit shifting. what about pushing and pulling from the stack? Why do we need that in reading the controllers?
Anyway, that routine is just saving a copy of the button states to the stack, so it can read the controllers again (which overwrites the variable) and compare both values. There's a glitch in the NES that sometimes corrupts controller reads when DPCM samples are playing, so one solution to avoid that problem is to read the controller repeatedly until 2 consecutive reads match.
You can use PHA and PLA to store things in the stack temporarily, but you have to be careful to always execute the same number of PHAs and PLAs, otherwise the program can easily crash due to stack overflows or underflows.
Re: Corrupt File?
Variables are actually just definitions (labels) of RAM registers so you could use the = or .equ directive in ASM6 like:DementedPurple wrote:And maybe teach me how to make a variable?
Code: Select all
var0 = $0000 ;general purpose variable
var1 = $0001
;...
Example RAM map:
Code: Select all
.enum $0000 ;$0000-$00FF (Zero Page)
var0 .dsb 1 ;general purpose variable
var1 .dsb 1
pointer0 .dsb 2 ;general purpose pointer
frame_count .dsb 1 ;frame counter
con_stat .dsb 2 ;controller button states
p1_x .dsb 1 ;player 1 X-coordinate
p1_y .dsb 1 ;player 1 Y-coordinate
p1_rest .dsb 1 ;player 1 lives
p1_score .dsb 1 ;player 1 score
;...
.ende
.enum $0100 ;$0100-$01FF (hardware stack)
;Reserved for the stack.
.ende
.enum $0200 ;$0200-$02FF (shadow OAM)
shadow_oam .dsb 256
.ende
.enum $0300 ;$0300-$07FF
array0 .dsb 16
array1 .dsb 32
;...
.ende
Dividing the RAM in pages like this you must of course make sure that your variables doesn't spill out of the page and into the next one.
I think you can make RAM definitions about anywhere in the source file (I'm not sure), but it makes sense to put them in the beginning like in the templates.
-
- Posts: 318
- Joined: Mon Jan 30, 2017 5:20 pm
- Location: Colorado USA
Re: Corrupt File?
With dougeffs example, how would I jump to a certain location when a button is pressed?
Re: Corrupt File?
I do something like this...
Edit, fixed wrong button order.
(in NMI, after all PPU / Sprite / Scrolling stuff)
(if you want to do something, anytime B is pressed, even if held)
;etc, on down the line
(if you want to do something, only on NEW B presses...)
the AND here does a bit mask...removing all the other buttons from the Accumulator, so that we are only concerned with just that bit. If the result is zero (because the button is not pressed) it will set the z flag, and we can BEQ / BNE away from the subroutine.
Code: Select all
enum $000
joypad1: .db 0
joypad1_last_frame: .db 0
.ende
;A, B, Select, Start, Up, Down, Left, Right
right = $01
left = $02
down = $04
up = $08
start = $10
select = $20
b_button = $40
a_button = $80
(in NMI, after all PPU / Sprite / Scrolling stuff)
Code: Select all
LDA joypad1
STA joypad1_last_frame ; save the last frame's buttons
ReadController:
LDX #$01
STX $4016
DEX
STX $4016
LDX #$08
ControllerLoop:
LDA $4016
LSR
ROL joypad1
DEX
BNE ControllerLoop
;new buttons now stored in joypad1 RAM address
Code: Select all
LDA joypad1
AND #b_button ;check if b press
BEQ b_exit
JSR Whatever_B_Does
b_exit:
LDA joypad1
AND #a_button ;check if a press
BEQ a_exit
JSR Whatever_A_Does
a_exit:
(if you want to do something, only on NEW B presses...)
Code: Select all
LDA joypad1_last_frame
AND #b_button ;check if b still held from last frame
BNE b_exit
LDA joypad1
AND #b_button ;check if new b press
BEQ b_exit
JSR Whatever_B_Does
b_exit:
;check some other button now
the AND here does a bit mask...removing all the other buttons from the Accumulator, so that we are only concerned with just that bit. If the result is zero (because the button is not pressed) it will set the z flag, and we can BEQ / BNE away from the subroutine.
Last edited by dougeff on Tue Mar 21, 2017 11:21 am, edited 2 times in total.
nesdoug.com -- blog/tutorial on programming for the NES
-
- Posts: 318
- Joined: Mon Jan 30, 2017 5:20 pm
- Location: Colorado USA
Re: Corrupt File?
What does the AND command do? How would I change DougEFFs example to work with other buttons? I would think that the AND command is a logic command. How do you and a bit? I don't know what that means.
-
- Posts: 271
- Joined: Sun Mar 27, 2011 10:49 am
- Location: Victoria, BC
Re: Corrupt File?
A bit can be 0 or 1.
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
Hopefully it should be obvious why.
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
Hopefully it should be obvious why.
-
- Posts: 318
- Joined: Mon Jan 30, 2017 5:20 pm
- Location: Colorado USA
Re: Corrupt File?
But why is it needed to read controllers?
-
- Posts: 318
- Joined: Mon Jan 30, 2017 5:20 pm
- Location: Colorado USA
Re: Corrupt File?
And how would I get the other bit? Do you load a memory address and the use "AND other memory location"
-
- Posts: 318
- Joined: Mon Jan 30, 2017 5:20 pm
- Location: Colorado USA
Re: Corrupt File?
I've figured out that the reason I was having a corrupt file was not the ROM, but rather the emulator. I got a corrupt file in Nestopia, but when I loaded it into Fceux, It worked just fine.