n65 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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"