n65 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +340 -0
  5. data/README.md +126 -0
  6. data/Rakefile +2 -0
  7. data/bin/n65 +11 -0
  8. data/data/opcodes.yaml +1030 -0
  9. data/examples/beep.asm +24 -0
  10. data/examples/mario2.asm +260 -0
  11. data/examples/mario2.char +0 -0
  12. data/examples/music_driver.asm +202 -0
  13. data/examples/noise.asm +93 -0
  14. data/examples/pulse_chord.asm +213 -0
  15. data/images/assembler_demo.png +0 -0
  16. data/lib/n65.rb +243 -0
  17. data/lib/n65/directives/ascii.rb +42 -0
  18. data/lib/n65/directives/bytes.rb +102 -0
  19. data/lib/n65/directives/dw.rb +86 -0
  20. data/lib/n65/directives/enter_scope.rb +55 -0
  21. data/lib/n65/directives/exit_scope.rb +35 -0
  22. data/lib/n65/directives/inc.rb +67 -0
  23. data/lib/n65/directives/incbin.rb +51 -0
  24. data/lib/n65/directives/ines_header.rb +53 -0
  25. data/lib/n65/directives/label.rb +46 -0
  26. data/lib/n65/directives/org.rb +47 -0
  27. data/lib/n65/directives/segment.rb +45 -0
  28. data/lib/n65/directives/space.rb +46 -0
  29. data/lib/n65/front_end.rb +90 -0
  30. data/lib/n65/instruction.rb +308 -0
  31. data/lib/n65/instruction_base.rb +29 -0
  32. data/lib/n65/memory_space.rb +150 -0
  33. data/lib/n65/opcodes.rb +9 -0
  34. data/lib/n65/parser.rb +85 -0
  35. data/lib/n65/regexes.rb +33 -0
  36. data/lib/n65/symbol_table.rb +198 -0
  37. data/lib/n65/version.rb +3 -0
  38. data/n65.gemspec +23 -0
  39. data/nes_lib/nes.sym +105 -0
  40. data/test/test_memory_space.rb +82 -0
  41. data/test/test_symbol_table.rb +238 -0
  42. data/utils/midi/Makefile +3 -0
  43. data/utils/midi/c_scale.mid +0 -0
  44. data/utils/midi/convert +0 -0
  45. data/utils/midi/guitar.mid +0 -0
  46. data/utils/midi/include/event.h +93 -0
  47. data/utils/midi/include/file.h +57 -0
  48. data/utils/midi/include/helpers.h +14 -0
  49. data/utils/midi/include/track.h +45 -0
  50. data/utils/midi/lil_melody.mid +0 -0
  51. data/utils/midi/mi_feabhra.mid +0 -0
  52. data/utils/midi/midi_to_nes.rb +204 -0
  53. data/utils/midi/source/convert.cpp +16 -0
  54. data/utils/midi/source/event.cpp +96 -0
  55. data/utils/midi/source/file.cpp +37 -0
  56. data/utils/midi/source/helpers.cpp +46 -0
  57. data/utils/midi/source/track.cpp +37 -0
  58. data/utils/opcode_table_to_yaml.rb +91 -0
  59. metadata +133 -0
@@ -0,0 +1,24 @@
1
+ ; Create an iNES header
2
+ .ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 1}
3
+
4
+ ; Here is the start of our code
5
+ .org $C000
6
+ .scope main
7
+ LDA #$01 ; square 1
8
+ STA $4015
9
+ LDA #$F8 ; period low
10
+ STA $4002
11
+ LDA #$02 ; period high
12
+ STA $4003
13
+ LDA #$BF ; volume
14
+ STA $4000
15
+ forever:
16
+ JMP forever
17
+
18
+ nothing:
19
+ RTI
20
+
21
+ .org $FFFA ; Here are the three interrupt vectors
22
+ .dw nothing ; VBlank non-maskable interrupt
23
+ .dw main ; When the processor is reset or powers on
24
+ .dw nothing ; External interrupt IRQ
@@ -0,0 +1,260 @@
1
+ ;------------------------------------------------------------------------------
2
+ ; Let's try to recreate a bit of a level from Mario 2
3
+ ;
4
+ ;;;;
5
+ ; Create an iNES header
6
+ .ines {"prog": 1, "char": 1, "mapper": 0, "mirror": 0}
7
+
8
+
9
+ ;;;;
10
+ ; Include all the symbols in the nes library
11
+ .inc <nes.sym>
12
+
13
+
14
+ ;;;;
15
+ ; Open the prog section bank 0
16
+ .segment prog 0
17
+
18
+ ;;;;
19
+ ; Current horizontal scroll value
20
+ .org $0000
21
+ .space horizontal_scroll 1
22
+
23
+
24
+ ;;;;
25
+ ; Setup the interrupt vectors
26
+ .org $FFFA
27
+ .dw vblank
28
+ .dw main
29
+ .dw irq
30
+
31
+
32
+ ;;;;
33
+ ; Here is our code entry point, which we'll call main.
34
+ .org $C000
35
+ .scope main
36
+ ; Disable interrupts and decimal flag
37
+ sei
38
+ cld
39
+
40
+ ; Wait for 2 vblanks
41
+ wait_vb1:
42
+ lda nes.ppu.status
43
+ bpl wait_vb1
44
+ wait_vb2:
45
+ lda nes.ppu.status
46
+ bpl wait_vb2
47
+
48
+ ; Now we want to initialize the hardware to a known state
49
+ lda #$00
50
+ ldx #$00
51
+ clear_segments:
52
+ sta $00, x
53
+ sta $0100, x
54
+ sta $0200, x
55
+ sta $0300, x
56
+ sta $0400, x
57
+ sta $0500, x
58
+ sta $0600, x
59
+ sta $0700, x
60
+ inx
61
+ bne clear_segments
62
+
63
+ ; Reset the stack pointer
64
+ ldx #$FF
65
+ txs
66
+
67
+ ; Disable all graphics and vblank nmi
68
+ lda #$00
69
+ sta nes.ppu.control
70
+ sta nes.ppu.mask
71
+
72
+ ; Call subroutines to initialize the graphics
73
+ jsr load_palette
74
+ jsr load_name_tables
75
+ jsr init_scrolling
76
+ jsr init_ppu
77
+
78
+ ; Resume interrupts and loop here forever
79
+ cli
80
+ forever:
81
+ jmp forever
82
+ .
83
+
84
+
85
+ ;;;;
86
+ ; nes.ppu.control: bitpattern is VPHB SINN
87
+ ; V: NMI enable
88
+ ; P: PPU master/slave (this does nothing on the NES)
89
+ ; H: Sprite height 0 = 8x8, 1 = 8x16
90
+ ; B: Background pattern table address (0: $0000; 1: $1000)
91
+ ; S: Sprite pattern table address for 8x8 sprites (0: $0000; 1: $1000; ignored in 8x16 mode)
92
+ ; I: VRAM address increment per CPU read/write of nes.vram.io (0: add 1, going across; 1: add 32, going down)
93
+ ; NN: Base nametable address (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
94
+ ;
95
+ ; Equivalently, bits 0 and 1 are the most significant bit of the scrolling coordinates
96
+ ;
97
+ ; nes.ppu.mask: bitpattern is BGRs bMmG
98
+ ; BGR: Color emphasis bits
99
+ ; s: Sprite enable
100
+ ; b: Background enable
101
+ ; M: Background left column enable
102
+ ; m: Sprite left column enable
103
+ ; G: Greyscale
104
+ ;
105
+ .scope init_ppu
106
+ lda #%10001000 ; NMI enable, 8x8 tile, Background: $0000, Sprites: $1000, Address increment: 1, Nametable: $2000
107
+ sta nes.ppu.control
108
+ lda #%00011110 ; No color emphasis, Enable sprites, Enable Background, Enable sprite and bg left column, no greyscale
109
+ sta nes.ppu.mask
110
+ rts
111
+ .
112
+
113
+
114
+ ;;;;
115
+ ; This initializes the scrolling value in the zero page
116
+ ; So that we begin offscreen and can scroll down
117
+ .scope init_scrolling
118
+ lda #$00
119
+ sta horizontal_scroll zp
120
+ rts
121
+ .
122
+
123
+
124
+ ;;;;
125
+ ; Load palette into $3F00
126
+ .scope load_palette
127
+ lda #$3F
128
+ ldx #$00
129
+ sta nes.vram.address
130
+ stx nes.vram.address
131
+ loop:
132
+ lda palette, x
133
+ sta nes.vram.io
134
+ inx
135
+ cpx #$20
136
+ bne loop
137
+ rts
138
+ .
139
+
140
+
141
+ ;;;;
142
+ ; Load the background tiles into the name table
143
+ .scope load_name_tables
144
+ ldy #$00
145
+ ldx #$04
146
+ lda #<background
147
+ sta $10
148
+ lda #>background
149
+ sta $11
150
+ lda #$24
151
+ sta nes.vram.address
152
+ lda #$00
153
+ sta nes.vram.address
154
+ loop:
155
+ lda ($10), y
156
+ sta nes.vram.io
157
+ iny
158
+ bne loop
159
+ inc $11
160
+ dex
161
+ bne loop
162
+ ; Now clear the second nametable
163
+ ldy #$00
164
+ ldx #$04
165
+ lda #$00
166
+ .scope
167
+ loop:
168
+ sta nes.vram.io
169
+ iny
170
+ bne loop
171
+ dex
172
+ bne loop
173
+ .
174
+ rts
175
+ .
176
+
177
+
178
+ ;;;;
179
+ ; Scroll the screen if we have to
180
+ .scope scroll_screen
181
+ ldx #$00 ; Reset VRAM Address to $0000
182
+ stx nes.vram.address
183
+ stx nes.vram.address
184
+
185
+ ldx horizontal_scroll zp
186
+ inx
187
+ stx horizontal_scroll zp
188
+
189
+
190
+ lda #$00
191
+ stx nes.ppu.scroll ; Write the new horizontal scroll value
192
+ sta nes.ppu.scroll ; Write 0 for vertical scroll
193
+ rts
194
+ .
195
+
196
+
197
+ ;;;;
198
+ ; VBlank routine
199
+ .scope vblank
200
+ jsr scroll_screen
201
+ rti
202
+ .
203
+
204
+
205
+ .scope irq
206
+ rti
207
+ .
208
+
209
+ ;;;;
210
+ ; Here is a bg palette which I ripped from Mario 2, but, I just put black for sprite palette
211
+ palette:
212
+ .bytes $21, $30, $12, $0F, $21, $30, $16, $0F, $21, $27, $17, $0F, $21, $29, $1A, $0F
213
+ .bytes $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F
214
+
215
+
216
+ ;;;;
217
+ ; This is the background tile map, or "Name Table" for a bit of a Mario 2 Level.
218
+ ; Here is how I generated this from a string of hex
219
+ ; ary.map{|byte| "$%.2X" % byte.to_i(16) }.each_slice(32){|group| puts ".bytes #{group.join(', ')}" }
220
+ background:
221
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BA, $BC, $FA, $FA, $FA, $FA
222
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BB, $BD, $FA, $FA, $FA, $FA
223
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BA, $BC, $FA, $FA, $FA, $FA
224
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BB, $BD, $FA, $FA, $FA, $FA
225
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BA, $BC, $FA, $FA, $FA, $FA
226
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BB, $BD, $FA, $FA, $FA, $FA
227
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BA, $BC, $FA, $FA, $FA, $FA
228
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BB, $BD, $FA, $FA, $FA, $FA
229
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BA, $BC, $FA, $FA, $FA, $FA
230
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BB, $BD, $FA, $FA, $FA, $FA
231
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $BA, $BC, $FA, $FA, $FA, $FA
232
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $B0, $B1, $B0, $B1, $90, $91, $FA, $FA, $FA, $FA
233
+ .bytes $B4, $B6, $B8, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $60, $62, $64, $62, $64, $62, $64, $62, $64, $66, $FA, $FA
234
+ .bytes $B5, $B7, $B9, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $46, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
235
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
236
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
237
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $DE, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
238
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
239
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
240
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
241
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
242
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
243
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
244
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
245
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
246
+ .bytes $FA, $FA, $FA, $FA, $FA, $FA, $B0, $B1, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $FA, $61, $63, $65, $63, $65, $63, $65, $63, $65, $67, $FA, $FA
247
+ .bytes $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72, $70, $72
248
+ .bytes $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73, $71, $73
249
+ .bytes $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF
250
+ .bytes $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AE, $AE, $AE, $AE, $AE, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF, $AE, $AF
251
+ .bytes $55, $55, $55, $55, $33, $44, $DD, $DD, $57, $55, $55, $55, $33, $44, $DD, $DD, $55, $55, $55, $55, $33, $44, $DD, $DD, $50, $55, $55, $55, $CC, $FF, $FF, $FF
252
+ .bytes $55, $55, $55, $54, $FF, $FF, $FF, $FF, $55, $55, $55, $55, $33, $FF, $FF, $FF, $F5, $F5, $F5, $F5, $F3, $FF, $FF, $FF, $5A, $5A, $5A, $5A, $0A, $0A, $0A, $0A
253
+
254
+
255
+ ;;;;
256
+ ; Here we include an entire binary char rom from Mario 2, which contains 4KB of tile data
257
+ ; No spirte data is included.
258
+ .segment char 0
259
+ .org $0000
260
+ .incbin "mario2.char"
Binary file
@@ -0,0 +1,202 @@
1
+ ;------------------------------------------------------------------------------
2
+ ; An NES music engine that understands the binary stream outputted from my
3
+ ; MIDI converter :)
4
+ ;;;;
5
+ ; Create an iNES header
6
+ .ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
7
+
8
+
9
+ ;;;;
10
+ ; Include all the symbols in the nes library
11
+ .inc <nes.sym>
12
+
13
+
14
+ ;;;;
15
+ ; Let's put a data structure to control the sound engine in the zero page
16
+ .org $0000
17
+ .scope sound_engine
18
+ ; Where we are reading from ROM
19
+ .space stream_read_ptr_lo 1
20
+ .space stream_read_ptr_hi 1
21
+
22
+ ; Where we are writing in the APU
23
+ .space stream_write_ptr_lo 1
24
+ .space stream_write_ptr_hi 1
25
+ .space delta 1
26
+ .
27
+
28
+
29
+ ;;;;
30
+ ; Open the prog section bank 0
31
+ .segment prog 0
32
+
33
+
34
+ ;;;;
35
+ ; Setup the interrupt vectors
36
+ .org $FFFA
37
+ .dw vblank
38
+ .dw main
39
+ .dw irq
40
+
41
+
42
+ ;;;;
43
+ ; Here is our code entry point, which we'll call main.
44
+ .org $C000
45
+ .scope main
46
+ ; Disable interrupts and decimal flag
47
+ sei
48
+ cld
49
+
50
+ ; Wait for 2 vblanks
51
+ wait_vb1:
52
+ lda nes.ppu.status
53
+ bpl wait_vb1
54
+ wait_vb2:
55
+ lda nes.ppu.status
56
+ bpl wait_vb2
57
+
58
+ ; Now we want to initialize the hardware to a known state
59
+ lda #%00
60
+ ldx #$00
61
+ clear_segments:
62
+ sta $0, x
63
+ sta $100, x
64
+ sta $200, x
65
+ sta $300, x
66
+ sta $400, x
67
+ sta $500, x
68
+ sta $600, x
69
+ sta $700, x
70
+ inx
71
+ bne clear_segments
72
+
73
+ ; Reset the stack pointer
74
+ ldx #$FF
75
+ txs
76
+
77
+ ; Disable all graphics and vblank nmi
78
+ lda #$00
79
+ sta nes.ppu.control
80
+ sta nes.ppu.mask
81
+
82
+ jsr init_sound
83
+
84
+ ; Resume interrupts and NMI and loop here forever
85
+ lda #%10000000
86
+ sta nes.ppu.control
87
+ cli
88
+ forever:
89
+ jmp forever
90
+ .
91
+
92
+
93
+ ;;;;
94
+ ; Initialize the APU to enable Pulse1
95
+ .scope init_sound
96
+ lda #$00
97
+ ldy #$00
98
+ clear_apu:
99
+ sta nes.apu, y
100
+ iny
101
+ cpy #$10
102
+ bne clear_apu
103
+
104
+ lda #>music_buffer
105
+ ldx #<music_buffer
106
+ sta sound_engine.stream_read_ptr_hi
107
+ stx sound_engine.stream_read_ptr_lo
108
+
109
+ lda #$40
110
+ ldx #$00
111
+ sta sound_engine.stream_write_ptr_hi
112
+ stx sound_engine.stream_write_ptr_lo
113
+
114
+ lda #$01
115
+ sta sound_engine.delta
116
+
117
+ lda #$01
118
+ sta nes.apu.channel_enable
119
+ rts
120
+ .
121
+
122
+
123
+ ;;;;
124
+ ; VBlank reads our music buffer
125
+ .scope vblank
126
+ ; Backup our registers
127
+ pha
128
+ txa
129
+ pha
130
+ tya
131
+ pha
132
+
133
+ jsr sound_engine_driver
134
+
135
+ ; Restore the registers
136
+ pla
137
+ tay
138
+ pla
139
+ tax
140
+ pla
141
+ rti
142
+ .
143
+
144
+ ;;;;
145
+ ; Sound driver that updates the APU registers
146
+ ; via a stream of APU write commands
147
+ .scope sound_engine_driver
148
+ dec sound_engine.delta
149
+ bne done
150
+
151
+ read_event:
152
+ ; Load the new delta from the stream
153
+ ldy #$00
154
+ lda (sound_engine.stream_read_ptr_lo), Y
155
+ sta sound_engine.delta
156
+
157
+ ; Read pulse1 control register value
158
+ ldy #$01
159
+ lda (sound_engine.stream_read_ptr_lo), Y
160
+ sta nes.apu.pulse1.control
161
+
162
+ ; Read the value for pulse1.ft
163
+ ldy #$02
164
+ lda (sound_engine.stream_read_ptr_lo), Y
165
+ sta nes.apu.pulse1.ft
166
+
167
+
168
+ ; Read the value for pulse1.ct
169
+ ldy #$03
170
+ lda (sound_engine.stream_read_ptr_lo), Y
171
+ sta nes.apu.pulse1.ct
172
+
173
+ ; Advance the 16-bit stream pointer by number of bytes read
174
+ lda sound_engine.stream_read_ptr_lo
175
+ clc
176
+ adc #$04
177
+ sta sound_engine.stream_read_ptr_lo
178
+ bcc done
179
+ inc sound_engine.stream_read_ptr_hi
180
+
181
+ ; If the very next event is 0 delta away, do it now too
182
+ ; Read the value for pulse1.ct
183
+ ldy #$04
184
+ lda (sound_engine.stream_read_ptr_lo), Y
185
+ beq read_event
186
+
187
+ done:
188
+ rts
189
+ .
190
+
191
+
192
+ ;;;;
193
+ ; IRQ Does nothing
194
+ .scope irq
195
+ rti
196
+ .
197
+
198
+ ;;;;
199
+ ; Include the music buffer stream
200
+ .org $D000
201
+ music_buffer:
202
+ .incbin "data.mus"