I'm trying to test my code. It assembles correctly, but when I load my ROM into an emulator, I get a corrupt file. Here is my code:
Code:
;----------------------------------------------------------------
; constants
;----------------------------------------------------------------
PRG_COUNT = 1 ;1 = 16KB, 2 = 32KB
MIRRORING = %0001 ;%0000 = horizontal, %0001 = vertical, %1000 = four-screen
;----------------------------------------------------------------
; variables
;----------------------------------------------------------------
.enum $0000
;NOTE: declare variables using the DSB and DSW directives, like this:
;MyVariable0 .dsb 1
;MyVariable1 .dsb 3
.ende
;NOTE: you can also split the variable declarations into individual pages, like this:
;.enum $0100
;.ende
;.enum $0200
;.ende
;----------------------------------------------------------------
; iNES header
;----------------------------------------------------------------
.db "NES", $1a ;identification of the iNES header
.db $01 ;number of 16KB PRG-ROM pages
.db $01 ;number of 8KB CHR-ROM pages
.db $00|$01 ;mapper 0 and mirroring
.dsb 9, $00 ;clear the remaining bytes
;----------------------------------------------------------------
; program bank(s)
;----------------------------------------------------------------
.base $10000-(PRG_COUNT*$4000)
Reset:
LDA #$30
STA $3F01
LDA #$3F
STA $3F00
STA $3F02
STA $3F03
LDY #%10001001
STY $2000
LDX #%00011110
STX $2001
LDX #128
STX $0000
STX $0001
LDA #3
STA $4014
JMP ReadController
UP:
LDX $0001
INX
STX $0001
JMP Wait
DOWN:
LDX $0001
DEX
STX $0001
JMP Wait
LEFT:
LDX $0000
DEX
STX $0000
JMP Wait
RIGHT:
LDY $0000
INY
STY $0000
JMP Wait
ReadController:
LDA #1
STA $4016
LDA #0
STA $4016
NOP
NOP
LDA 4016
TAY
CPY #5
BEQ UP
CPY #6
BEQ DOWN
CPY #7
BEQ LEFT
CPY #8
BEQ RIGHT
JMP Wait
Wait:
JMP Wait
NMI:
LDA #$3F
STA $2006
LDA #$00
STA $2006
LDA 0001
STA $2004
LDY #0
STY $2003
STY $2003
LDA 0000
STA $2004
IRQ:
;NOTE: IRQ code goes here
;----------------------------------------------------------------
; interrupt vectors
;----------------------------------------------------------------
.org $fffa
.dw NMI
.dw Reset
.dw IRQ
;----------------------------------------------------------------
; CHR-ROM bank
;----------------------------------------------------------------
.incbin "Graphics.chr"
It runs for me (I padded the CHR ROM using a .dsb directive). How big is your "Graphics.chr" file? It should be 8 kB (8192 byte) to fill out all of the CHR ROM space.
This is unrelated, but I also noticed that your NMI- and IRQ-handlers are not ending in RTI instructions. Without an RTI they won't be able to return back to the main code after they have finished, and your game will not work as you want.
It doesn't matter. None of this code is good.
Look on the bright side. If your file weren't corrupt, it would correctly find it's way to the start of your code (reset), at which point, lots of bad things happen that don't make sense, and wouldn't do what you want. At some point, an NMI would fire, and it would go to NMI, and never come back (without an RTI).
Removed comment. Sounded too harsh.
Would it be ok if I rewrote your code, with code that actually works? Or would you rather work it out for yourself?
It works when I use a .dsb directive, but when I .incbin "Graphics.chr", I get a corrupt file. One thing I've noticed is that when I do use the .dsb directive and run it in an emulator, I don't see the value in the address storing the X-position increasing when I press the right arrow.
DementedPurple wrote:
It works when I use a .dsb directive, but when I .incbin "Graphics.chr", I get a corrupt file. One thing I've noticed is that when I do use the .dsb directive and run it in an emulator, I don't see the value in the address storing the X-position increasing when I press the right arrow.
And yes, I used RTI
There were a lot of mistakes in your code, I fixed some of them in a way that it works as you want :
Code:
;----------------------------------------------------------------
; constants
;----------------------------------------------------------------
PRG_COUNT = 1 ;1 = 16KB, 2 = 32KB
MIRRORING = %0001 ;%0000 = horizontal, %0001 = vertical, %1000 = four-screen
;----------------------------------------------------------------
; variables
;----------------------------------------------------------------
.enum $0000
;NOTE: declare variables using the DSB and DSW directives, like this:
;MyVariable0 .dsb 1
;MyVariable1 .dsb 3
.ende
;NOTE: you can also split the variable declarations into individual pages, like this:
;.enum $0100
;.ende
;.enum $0200
;.ende
;----------------------------------------------------------------
; iNES header
;----------------------------------------------------------------
.db "NES", $1a ;identification of the iNES header
.db $01 ;number of 16KB PRG-ROM pages
.db $01 ;number of 8KB CHR-ROM pages
.db $00|$01 ;mapper 0 and mirroring
.dsb 9, $00 ;clear the remaining bytes
;----------------------------------------------------------------
; program bank(s)
;----------------------------------------------------------------
.base $10000-(PRG_COUNT*$4000)
Reset:
LDA #$3F
STA $2006
LDA #$00
STA $2006
LDA #$3F
STA $2007
LDA #$30
STA $2007
LDA #$3F
STA $2007
LDA #$3F
STA $2007
LDY #%10001001
STY $2000
LDX #%00011110
STX $2001
LDX #128
STX $0000
STX $0001
Wait:
LDA #1
STA $4016
LDA #0
STA $4016
;Check A
LDA $4016
;Check B
LDA $4016
;Check Select
LDA $4016
;Check Start
LDA $4016
;Check Up
LDA $4016
AND #%00000001
CMP #%00000001
BEQ UP
;Check Down
LDA $4016
AND #%00000001
CMP #%00000001
BEQ DOWN
;Check Left
LDA $4016
AND #%00000001
CMP #%00000001
BEQ LEFT
;Check Right
LDA $4016
AND #%00000001
CMP #%00000001
BEQ RIGHT
JMP Wait
UP:
LDX $0001
INX
STX $0001
JMP Wait
DOWN:
LDX $0001
DEX
STX $0001
JMP Wait
LEFT:
LDX $0000
DEX
STX $0000
JMP Wait
RIGHT:
LDY $0000
INY
STY $0000
JMP Wait
NMI:
LDY #$00
STY $2003
LDY $0001
STY $2004
LDY #0
STY $2004
STY $2004
LDY $0000
STY $2004
RTI
IRQ:
;NOTE: IRQ code goes here
RTI
;----------------------------------------------------------------
; interrupt vectors
;----------------------------------------------------------------
.org $fffa
.dw NMI
.dw Reset
.dw IRQ
;----------------------------------------------------------------
; CHR-ROM bank
;----------------------------------------------------------------
.incbin "Graphics.chr"
Next time, do this when necessary:
Code:
OPC $nn ;ZP mode, Optimal
instead of:
Code:
OPC $00nn ;THIS IS RUBBISH!!!
This way it is optimized by using the Zero Page model for opcodes instead of Absolute, It makes a BIG difference in your code when doing this
OPC is an example opcode, Not really a valid 65-series opcode!
Actually, I think ASM6 will optimize for ZP addressing even if you write addresses in the form of $00xx. Whenever I needed to access ZP using absolute addressing (usually in timed code part of Atari 2600 programs) I had to either use mirrors or output the instructions in hex using .db (e.g. .db $ad, $01, $00 for lda $0001).
Anyway, you shouldn't be using hardcoded addresses to begin with... A person can easily forget what each address is used for, which addresses are free, and so on. To avoid bugs that can result from misusing memory positions, you should be naming your variables instead. This is what ENUM, DSB and DSW are for.
Yeah declare the variables after the ".enum $0000" part like the template explains. Hard-coding RAM registers isn't very useful.
DementedPurple wrote:
It works when I use a .dsb directive, but when I .incbin "Graphics.chr", I get a corrupt file. One thing I've noticed is that when I do use the .dsb directive and run it in an emulator, I don't see the value in the address storing the X-position increasing when I press the right arrow.
As I thought, your CHR file is the wrong size. Make sure it's exactly 8192 byte.
The X-position doesn't change because your code has too many problems to work, like people already pointed out. But fix one problem at a time, first make sure the file is the correct size, then you can worry about the code.
DementedPurple wrote:
And yes, I used RTI
I don't see an RTI in your code. The missing RTI isn't the only problem though. You read the controller only once. You need to keep reading it every frame (polling) to see if the player pushes any buttons or it will not work. So it must be inside the endless loop like in Farid's fix.
Both @DementedPurple and @FARID...those controller routines are rubbish.
Don't put sprites in the #1 page ($100-1ff). That's the hardware stack.
PPU state and/V-blank are not guaranteed on Reset. You can't write to the PPU outside of v-blank, unless rendering is off. Therefore, you must explicitly disable rendering before writing to PPU, at the start of init code...
LDA #0
STA $2001
and, you SHOULD wait 2 v-blanks before writing to PPU, to ensure it has gone through a 'warm up'.
Controller reads should be done once per frame and rolled/shifted into a RAM address, probably inside the NMI routine. You can then do logic based on a consistent value, throughout the frame (by reading this RAM address).
See the wiki.
EDIT, to clarify. You need to read 4016 8 times to get 8 bits, but only do those 8 reads, once per frame.
Well, I changed the code after putting it on here. And my CHR bank was the exact right size.
And also, could you maybe give an example of a good controller routine?
And maybe teach me how to make a variable?
3 approaches...
-----------------------------------
SMB2,US (edited)
Reads both controllers, and Japanese expansion controllers
Code:
ReadController:
LDX #$01
STX $4016
DEX
STX $4016
LDX #$08
ControllerLoop:
LDA $4016
LSR
ROL $00F5 ;controller 1
LSR
ROL $06F4 ;japanese expansion controller 1
LDA $4017
LSR
ROL $00F6 ;controller 2
LSR
ROL $06F5 ;japanese expansion controller 2
DEX
BNE ControllerLoop
RTS
Simplified =
Code:
ReadController:
LDX #$01
STX $4016
DEX
STX $4016
LDX #$08
ControllerLoop:
LDA $4016
LSR
ROL $00F5 ;controller 1
DEX
BNE ControllerLoop
RTS
-----------------------------------
SMB3
(ran twice, or more, because of possible DMC sound bug, if DMC channel is active)
Code:
LDY #$01
CheckBothControllers:
JSR ReadControllers
ControllerCheckAgain:
LDA $00
PHA
JSR ReadControllers
PLA
CMP $00
BNE ControllerCheckAgain
LDA $00 ;edited
ORA $01 ;edited combine regular controller and expansion controller bits
STA $00F7,Y ;controller 1 at f7, controller 2 at f8
DEY
BPL CheckBothControllers: ;loops twice, once for each controller
...
(somewhere else)
Code:
ReadControllers:
LDA #$01
STA $4016
LSR
STA $4016
LDX #$08
ControllerLoop:
LDA $4016,Y ; $4017 first time, 4016 second time
LSR
ROL $00 ; Controller 2 on first loop, Controller 1 on second loop
LSR
ROL $01 ; Japanese Expansion Controller
DEX
BNE ControllerLoop:
RTS
--------------------------
Silver Surfer
;each button shifted to a different RAM address
;A, B, Select, Start, Up, Down, Left, Right
Code:
LDX #$01
STX $4016
DEX
STX $4016
LDA $4016
LSR
ROR $0321 ;A
LDA $4016
LSR
ROR $0320 ;B
LDA $4016
LSR
ROR $031F ;Select
LDA $4016
LSR
ROR $031E ;Start
LDA $4016
LSR
ROR $031D ;Up
LDA $4016
LSR
ROR $031C ;Down
LDA $4016
LSR
ROR $031B ;Left
LDA $4016
LSR
ROR $031A ;Right
(similarly for controller 2)
The advantage here, is you can tell how long a player has been pushing a certain button, up to 8 frames worth.
EDIT - I didn't test these. There has been slight editing in some. I'll check them a little later.
Edit2 - typo
And here's the one I use, which treats the 2nd player's bits as a ring counter to avoid having to load X:
pads.s
Further on why Japanese expansion controllers listed separately...from the wiki...
https://wiki.nesdev.com/w/index.php/Standard_controllerFamicom $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.
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?
Bit shifts are used here to combine multiple reads from a
serial port into one byte.
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.
Okay, I understand bit shifting. what about pushing and pulling from the stack? Why do we need that in reading the controllers?
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.
DementedPurple wrote:
Okay, I understand bit shifting. what about pushing and pulling from the stack? Why do we need that in reading the controllers?
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.
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.
DementedPurple wrote:
And maybe teach me how to make a variable?
Variables are actually just definitions (labels) of RAM registers so you could use the = or .equ directive in ASM6 like:
Code:
var0 = $0000 ;general purpose variable
var1 = $0001
;...
But that would make it not possible to insert new variables in between the ones you wrote earlier. So assemblers has directives that automatically increments an internal counter of the assembler for definition purposes. In ASM6 it's the .dsb (define storage byte) and .dsw (define storage word) directives together with .enum (enumerate) and .ende (end enumerate).
Example RAM map:
Code:
.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
Here var0 will be defined as $0000 and var1 will be $0001. The number after the .dsb directive is how many bytes you reserve for the label. I used two byte for con_stat, that way you can have controller I button states in con_stat+0 and controller II button states in con_stat+1. Zero Page should only be used for often accessed variables and for your pointers (as they require the ZP). Bigger things like arrays may be put in later pages so they don't take up too much of your Zero Page space. Page 1 is used by the hardware stack, but you can use part of it if you run out of RAM and don't use a ton of stack. The stack builds from $01FF and down, so use the first part of this page for variables in that case. Page 2 is usually used for shadow OAM, it's not necessary to use this page, but you have to use one whole RAM page for that if you are going to use OAM DMA, so you might as well use page 2 since that's what everyone does. Commercial games as well as homebrew normally uses it.
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.
With dougeffs example, how would I jump to a certain location when a button is pressed?
I do something like this...
Code:
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
Edit, fixed wrong button order.
(in NMI, after all PPU / Sprite / Scrolling stuff)
Code:
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
(if you want to do something, anytime B is pressed, even if held)
Code:
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:
;etc, on down the line
(if you want to do something, only on NEW B presses...)
Code:
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.
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.
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.
But why is it needed to read controllers?
And how would I get the other bit? Do you load a memory address and the use "AND other memory location"
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.
As mentioned earlier, shifts are used to combine what the hardware gives you (a single bit per read) into one byte. This byte represents the entire controller state, using a single bit for each button on the controller (conveniently, the NES controller has 8 buttons).
Shown below are typically the values for each button (depending upon how controller reading is performed, they may be backwards if reading is done in reverse):
Code:
RIGHT_BUTTON = $01
LEFT_BUTTON = $02
DOWN_BUTTON = $04
UP_BUTTON = $08
START_BUTTON = $10
SELECT_BUTTON = $20
B_BUTTON = $40
A_BUTTON = $80
As an example, here's what happens if the player is holding LEFT, START, A and B:
Code:
%1101_0010 = $d2
|| | |
|| | +-------- LEFT_BUTTON = $02
|| +------------ START_BUTTON = $10
|+-------------- B_BUTTON = $40
+--------------- A_BUTTON = $80
Using AND is a way to strip out a single bit at a time, to see if the corresponding button is currently being pressed.
For the previous example, here's what happens when that value is AND'd with the value for RIGHT_BUTTON, which is not currently being pressed:
Code:
%1101_0010 = $d2
AND
%0000_0001 = $01 (RIGHT_BUTTON)
----------
%0000_0000
The result is zero, because RIGHT is not being pressed. If instead, the value was AND'd with the value for B_BUTTON:
Code:
%1101_0010 = $d2
AND
%0100_0000 = $40 (B_BUTTON)
----------
%0100_0000
The result is $40, which is non-zero, because B *is* being pressed.
Whether the result is zero or non-zero is useful, because then we can use BEQ or BNE to jump to another piece of code.
Maybe it'll help if I rewrite this part:
Code:
;A, B, Select, Start, Up, Down, Left, Right
right = $01
left = $02
down = $04
up = $08
start = $10
select = $20
a_button = $40
b_button = $80
Like this:
Code:
;A, B, Select, Start, Up, Down, Left, Right
right = %00000001
left = %00000010
down = %00000100
up = %00001000
start = %00010000
select = %00100000
a_button = %01000000
b_button = %10000000
Looking at these numbers in binary, you can clearly see that they are masks used to isolate individual bits in a byte. By ANDing them with the byte that contains the states of all buttons you can isolate any bit you're interested in checking. AND is a bitwise operation that only results in 1 if both bits are 1, so the result will be 0 when the button is not pressed, and not 0 when it's pressed.
DementedPurple wrote:
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.
Errr... Nestopia is not crazy, if it says your file is corrupted, your file is not a valid NES ROM. FCEUX might be doing something behind the scenes to make it work, but this is something you should definitely be looking into. What's the exact byte size of the file?
DementedPurple wrote:
And how would I get the other bit? Do you load a memory address and the use "AND other memory location"
Yes, this. Operations on the 6502 generally use what's in the accumulator as an implicit argument and as the implicit destination. So, for example, AND #$10 will perform a bitwise AND with whatever is in the accumulator and the constant number $10 (that's 10 in hexadecimal, a.k.a. 16 in decimal) and finally put the result in the accumulator. By "bitwise", I mean the rightmost bit of the accumulator will get AND'd with the rightmost bit of the number $10 to form the rightmost bit of the result.
Oh crap, did I get the button order wrong?
It should be...
b_button = %01000000 = $40
a_button = %10000000 = $80
Not...
a_button = %01000000
b_button = %10000000
Right?
Dustmop corrected me, tokumaru copied incorrectly from me.
Okay, I think I get it, it's a way to see if two buttons are pressed. But I have one final question. How come when I run FARIDS edited code, I get a screen filled with the first sprite. I knew I would see the first sprite, but I expected to only see it in the middle of the screen. Is it a bug?
There's no way to know what's wrong with your modified code unless you post code. i, personally, am not psychic.
This is the glitched code
Code:
There were a lot of mistakes in your code, I fixed some of them in a way that it works as you want :
Code:
;----------------------------------------------------------------
; constants
;----------------------------------------------------------------
PRG_COUNT = 1 ;1 = 16KB, 2 = 32KB
MIRRORING = %0001 ;%0000 = horizontal, %0001 = vertical, %1000 = four-screen
;----------------------------------------------------------------
; variables
;----------------------------------------------------------------
.enum $0000
;NOTE: declare variables using the DSB and DSW directives, like this:
;MyVariable0 .dsb 1
;MyVariable1 .dsb 3
.ende
;NOTE: you can also split the variable declarations into individual pages, like this:
;.enum $0100
;.ende
;.enum $0200
;.ende
;----------------------------------------------------------------
; iNES header
;----------------------------------------------------------------
.db "NES", $1a ;identification of the iNES header
.db $01 ;number of 16KB PRG-ROM pages
.db $01 ;number of 8KB CHR-ROM pages
.db $00|$01 ;mapper 0 and mirroring
.dsb 9, $00 ;clear the remaining bytes
;----------------------------------------------------------------
; program bank(s)
;----------------------------------------------------------------
.base $10000-(PRG_COUNT*$4000)
Reset:
LDA #$3F
STA $2006
LDA #$00
STA $2006
LDA #$3F
STA $2007
LDA #$30
STA $2007
LDA #$3F
STA $2007
LDA #$3F
STA $2007
LDY #%10001001
STY $2000
LDX #%00011110
STX $2001
LDX #128
STX $0000
STX $0001
Wait:
LDA #1
STA $4016
LDA #0
STA $4016
;Check A
LDA $4016
;Check B
LDA $4016
;Check Select
LDA $4016
;Check Start
LDA $4016
;Check Up
LDA $4016
AND #%00000001
CMP #%00000001
BEQ UP
;Check Down
LDA $4016
AND #%00000001
CMP #%00000001
BEQ DOWN
;Check Left
LDA $4016
AND #%00000001
CMP #%00000001
BEQ LEFT
;Check Right
LDA $4016
AND #%00000001
CMP #%00000001
BEQ RIGHT
JMP Wait
UP:
LDX $0001
INX
STX $0001
JMP Wait
DOWN:
LDX $0001
DEX
STX $0001
JMP Wait
LEFT:
LDX $0000
DEX
STX $0000
JMP Wait
RIGHT:
LDY $0000
INY
STY $0000
JMP Wait
NMI:
LDY #$00
STY $2003
LDY $0001
STY $2004
LDY #0
STY $2004
STY $2004
LDY $0000
STY $2004
RTI
IRQ:
;NOTE: IRQ code goes here
RTI
;----------------------------------------------------------------
; interrupt vectors
;----------------------------------------------------------------
.org $fffa
.dw NMI
.dw Reset
.dw IRQ
;----------------------------------------------------------------
; CHR-ROM bank
;----------------------------------------------------------------
.incbin "Graphics.chr"
You never initialized the name tables, so if an emulator initializes them with zeroes you're gonna get the first tile repeated over and over. If you want a blank background you have to include a blank tile in your tileset and write the index of that tile to the entire name table being displayed. Never count on anything being initialized automatically, always clear everything yourself. And please, add the PPU warm up loop in your reset code before using the PPU.
Another question. Can you use multiple AND commands like this:
Code:
LDA Controller
CMP #1
AND #2
AND #4
BEQ Example
You can, but each AND will act upon the result of the previous operation, which is probably not what you want. If you want to use multiple masks at once you can do this:
Code:
AND #(1|2|4)
The symbol "|" performs a bitwise OR during assembly, to combine the separate masks into one.
For example, if you want to both START and A to perform the same action, such as start a game, you can do something like this:
Code:
LDA PressedButtons
AND #(MASK_START|MASK_A)
BNE StartGame
DementedPurple wrote:
Another question. Can you use multiple AND commands like this:
Code:
LDA Controller
CMP #1
AND #2
AND #4
BEQ Example
No that will not work as you want.
You must read joypad port ($4016)
eight times, in order to find out the status of all buttons.
With each read of $4016 the state of
only one button is revealed in this order : A B Select Start Up Down Left Right
When you read $4016 a value is loaded into reg A, right?
That value is in this format : #%xxxxxxxj
But the only bit related to joypad buttons is bit0 (j)
bit0 can have two possible values : it can be either 0 or 1, right?
If it is 0 then it shows that the button was not pressed.
If it is 1 then it shows that the button was pressed.
So we have to separate bit0 from other bits (bit1 ~ bit7), because other bits are related to other things which we don't care, they can be any random value
That is why we have to use AND opcode
We AND reg A value with #%00000001, so all of the bits from bit1 ~ bit7 are set to 0, but bit0 keeps it's own value (it means if it was 0 it will be 0, if it was 1 it will be 1)
Then we can use CMP opcode to check the value of bit0 and do the desired action according to it's state
Analyze this to find out what is going on :
Code:
;Check A:
LDA Controller
AND #%00000001
CMP #%00000000
BEQ A_was_not_pressed
CMP #%00000001
BEQ A_was_pressed
;Check B:
LDA Controller
AND #%00000001
CMP #%00000000
BEQ B_was_not_pressed
CMP #%00000001
BEQ B_was_pressed
.
.
.
;Check Right:
LDA Controller
AND #%00000001
CMP #%00000000
BEQ Right_was_not_pressed
CMP #%00000001
BEQ Right_was_pressed
Once you understand what is going on you can use more optimized codes to read joypad
After creating this line in my input asm file my easiness with the button byte increased dramaticly!
Hope it can help you too.
Code:
;abetudlr
Since select and start both begin with 's' I chose the second letter of both of them... that way there are 8 different letters each representing its bit!
But you don't need to remember the bit order at all if you simply define 8 symbols to use as masks.
tokumaru wrote:
But you don't need to remember the bit order at all if you simply define 8 symbols to use as masks.
But it really helps me because I can open the trace file (created by FCEUX 2.2.2) with notepad and find "A:41 X:08" and it instantly goes to the first time I pressed a. It's very easy to check controller usage that way!
edit: I'll make that easier to understand later today. : )
unregistered wrote:
tokumaru wrote:
But you don't need to remember the bit order at all if you simply define 8 symbols to use as masks.
But it really helps me because I can open the trace file (created by FCEUX 2.2.2) with notepad and find "A:41 X:08" and it instantly goes to the first time I pressed a. It's very easy to check controller usage that way!
edit: I'll make that easier to understand later today. : )
So in our read controller routine is a loop somewhat like this:
Code:
ldx #08
-loop lda $4016
and #03
Cmp #01
rol currentControllerButtons
Dex
BNE -loop
That's its heart. I think I found something like that in Michael Martin's NES 101 Tutorial. Anyways, when making a trace log file in FCEUX 2.2.2. ...the Trace Logger screen appears and I have checked
* Log state of registers
* Log Frames count
* Log emulator messages
* Log Processor status flags
* Log breakpoint hits
* Use Stack Pointer for code tabbing (nesting visualization)
* To the left from disassembly
I click Log to File. Then reset your game because that causes it to start at frame 0. A frame is one cycle through vblank interrupt (NMI) and after the rti it loops once through my main loop. Then it waits for the next vblank to start. Frame 0 ends, frame 1 begins, the i flag is set and frame 1's vblank begins. Causing it to start at 0 decreases the file size.
Move to point in your game where you want to start logging... have to go to sleep will finish tomorrow night.
So you've quickly reached a point in your game where you want to start the trace log... Then press the pause button on your keyboard. With the game paused press the '-' key to set your game to run at the slowest speed possible (~2 seconds per frame). Next click the 'Start Logging' button on the trace logger window. Now, the game is still paused so your file isn't growing at all... you have ample time to open the hex editor (click Debug>Hex Editor...) or click Tools>Memory Watch... to watch up to 24 decimal values.
Press the pause key again and the file will slowly build. I'm testing a direction and
a being pressed together... so I hold
down and then press
a many times. The game is running at a rate of two seconds per frame so pressing
a once will probably not be recognized. Having the hex editor or memory watch window open should allow you to view exactly when your controller bytes change. My controller bytes read 84 (or 132 decimal) once
down and
a are pressed together. Once you know your controller input has been recognized correctly, wait a few frames for your game to react... then click the stop logging button on the Trace Logger window to stop your file from growing.
Open the file with a text editor... windows notepad is great for these .log files. Then,
in windows notepad, to find the first time
down and
a were pressed: click Edit>Find... and then type "A:41 X:08" and press enter. The abetudlr helps me to quickly find button presses...
A:41 X:08 finds
aA:41 X:07 finds
bA:41 X:06 finds
selectA:41 X:05 finds
startA:41 X:04 finds
upA:41 X:03 finds
downA:41 X:02 finds
leftA:41 X:01 finds
right being pressed
(Holding
down and then pressing
a many times makes 100% chance that the first instance of "A:41 X:08" found will be
at a time when both
a and
down were pressed.
Well, maybe not a 100% chance cause you could have something like Code:
lda #$41
ldx #$08
in the very beginning... but once you are certain your controller routine works and you have memorized the address of your "look at controller" routine, there is 100% chance that the first stop at the address you memorized will be a time when both a and down are pressed )
edit. additioncolored buttonsedit2