summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2014-10-15 13:16:51 -0400
committerJesse Luehrs <doy@tozt.net>2014-10-15 13:16:51 -0400
commitcbaac4361c9d1082a64e9ecd6b8dc1da9a58f4e6 (patch)
treec75547a01d144ce4c69fd18fed7f57a0aa1a6f91
parentb31a08ec1e9fab6bf3f99c4d09ea93e46a9d78b7 (diff)
downloadblog.tozt.net-cbaac4361c9d1082a64e9ecd6b8dc1da9a58f4e6.tar.gz
blog.tozt.net-cbaac4361c9d1082a64e9ecd6b8dc1da9a58f4e6.zip
new post
-rw-r--r--media/input.s135
-rw-r--r--posts/writing-an-nes-game-part-3.md95
2 files changed, 230 insertions, 0 deletions
diff --git a/media/input.s b/media/input.s
new file mode 100644
index 0000000..56ccee5
--- /dev/null
+++ b/media/input.s
@@ -0,0 +1,135 @@
+.ROMBANKMAP
+BANKSTOTAL 2
+BANKSIZE $4000
+BANKS 1
+BANKSIZE $2000
+BANKS 1
+.ENDRO
+
+.MEMORYMAP
+DEFAULTSLOT 0
+SLOTSIZE $4000
+SLOT 0 $C000
+SLOTSIZE $2000
+SLOT 1 $0000
+.ENDME
+
+.ENUM $00
+sleeping DB
+color DB
+frame_count DB
+.ENDE
+
+ .bank 0 slot 0
+ .org $0000
+RESET:
+ SEI
+ CLD
+ LDX #$FF
+ TXS
+ INX
+ STX $2000.w
+ STX $2001.w
+ STX $4010.w
+ LDX #$40
+ STX $4017.w
+
+vblankwait1:
+ BIT $2002
+ BPL vblankwait1
+
+clrmem:
+ LDA #$00
+ STA $0000, x
+ STA $0100, x
+ STA $0300, x
+ STA $0400, x
+ STA $0500, x
+ STA $0600, x
+ STA $0700, x
+ LDA #$FE
+ STA $0200, x
+ INX
+ BNE clrmem
+
+ LDA #%10000001
+ STA color
+
+vblankwait2:
+ BIT $2002
+ BPL vblankwait2
+
+ LDA #%10000000
+ STA $2000
+
+loop:
+ INC sleeping
+wait_for_vblank_end:
+ LDA sleeping
+ BNE wait_for_vblank_end
+
+ ; controller 1 latch
+ LDA #$01
+ STA $4016
+ LDA #$00
+ STA $4016
+
+ ; controller 1 clock, reading the state of the A button
+ LDA $4016
+ AND #%00000001
+ BNE change_color
+ JMP loop_end
+ ; reading the rest of the buttons is unnecessary, so we don't do it
+
+change_color:
+ LDA #$00
+ STA frame_count
+ LDX color
+ CPX #%10000001
+ BEQ turn_green
+ CPX #%01000001
+ BEQ turn_red
+
+turn_blue:
+ LDA #%10000001
+ STA color
+ JMP loop_end
+turn_green:
+ LDA #%01000001
+ STA color
+ JMP loop_end
+turn_red:
+ LDA #%00100001
+ STA color
+
+loop_end:
+ JMP loop
+
+NMI:
+ PHA
+ TXA
+ PHA
+ TYA
+ PHA
+
+ LDA color
+ STA $2001
+
+ LDA #$00
+ STA sleeping
+ PLA
+ TAY
+ PLA
+ TAX
+ PLA
+
+ RTI
+
+ .orga $FFFA
+ .dw NMI
+ .dw RESET
+ .dw 0
+
+ .bank 1 slot 1
+ .org $0000
+ .incbin "sprites.chr"
diff --git a/posts/writing-an-nes-game-part-3.md b/posts/writing-an-nes-game-part-3.md
new file mode 100644
index 0000000..998ae50
--- /dev/null
+++ b/posts/writing-an-nes-game-part-3.md
@@ -0,0 +1,95 @@
+---
+title: writing an nes game, part 3
+date: "2014-10-15 15:30"
+tags: [hacker school, nes]
+---
+
+# Writing an NES game - input handling
+
+Now that we have a general idea of how a program for the NES is structured,
+it'd be useful to get a bit further into the specific capabilities of the NES.
+One of the pretty basic (and pretty important) ones is reading input from the
+controllers.
+
+The basic structure of an NES controller is a [shift
+register](https://en.wikipedia.org/wiki/Shift_register). To read the
+controller inputs, you send a signal to the latch pin, which stores the state
+of the input buttons in the register, and then you send a sequence of signals
+to the clock pin to put the state of each button on the output in sequentially
+(A, B, Select, Start, Up, Down, Left, Right). As it turns out, the code you
+have to write to read from the controller maps pretty exactly to these
+operations. This is what it looks like:
+
+```asm
+read_controller1:
+ ; memory address $4016 corresponds to the shift register inside the
+ ; controller plugged into the first controller port. writing to it sets the
+ ; state of the latch pin, and so we set the latch pin high and then low in
+ ; order to store the controller state in the shift register.
+ LDA #$01
+ STA $4016
+ LDA #$00
+ STA $4016
+
+ ; reading from $4016 reads the output value of the data pin and also sends a
+ ; signal to the clock pin in order to put the next bit of data on the output
+ ; for the next read. the value that is read has the state of the button in
+ ; the low bit, and the upper bits contain various other pieces of
+ ; information (such as whether a controller is plugged in at all, etc), so
+ ; if we only care about the state of the button we have to mask out
+ ; everything else.
+read_a:
+ LDA $4016
+ AND #%00000001
+ BEQ read_b
+ ; code for if the a button is pressed
+read_b:
+ LDA $4016
+ AND #%00000001
+ BEQ read_select
+ ; code for if the b button is pressed
+read_select:
+ LDA $4016
+ AND #%00000001
+ BEQ read_start
+ ; code for if the select button is pressed
+read_start:
+ LDA $4016
+ AND #%00000001
+ BEQ read_up
+ ; code for if the start button is pressed
+read_up:
+ LDA $4016
+ AND #%00000001
+ BEQ read_down
+ ; code for if the up button is pressed
+read_down:
+ LDA $4016
+ AND #%00000001
+ BEQ read_left
+ ; code for if the down button is pressed
+read_left:
+ LDA $4016
+ AND #%00000001
+ BEQ read_right
+ ; code for if the left button is pressed
+read_right:
+ LDA $4016
+ AND #%00000001
+ BEQ end_read_controller1
+ ; code for if the right button is pressed
+
+end_read_controller1:
+ RTS
+```
+
+Obviously this could be simplified by putting the reads in a loop (the [snake
+game](https://github.com/doy/nes-snake) handles it by shifting and packing all
+of the button states into a single byte which the code can query later on),
+but that does require more CPU cycles, especially if not all of the buttons
+are important.
+
+In the spirit of continuing with real working code,
+[here]({{urls.media}}/input.nes) is a sample program which changes the
+background color every time you press A, rather than every second like last
+time. Tomorrow, we'll work on drawing sprites!