From cbaac4361c9d1082a64e9ecd6b8dc1da9a58f4e6 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 15 Oct 2014 13:16:51 -0400 Subject: new post --- media/input.s | 135 ++++++++++++++++++++++++++++++++++++ posts/writing-an-nes-game-part-3.md | 95 +++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 media/input.s create mode 100644 posts/writing-an-nes-game-part-3.md 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! -- cgit v1.2.3-54-g00ecf