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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/Gemfile +4 -0
- data/LICENSE +340 -0
- data/README.md +126 -0
- data/Rakefile +2 -0
- data/bin/n65 +11 -0
- data/data/opcodes.yaml +1030 -0
- data/examples/beep.asm +24 -0
- data/examples/mario2.asm +260 -0
- data/examples/mario2.char +0 -0
- data/examples/music_driver.asm +202 -0
- data/examples/noise.asm +93 -0
- data/examples/pulse_chord.asm +213 -0
- data/images/assembler_demo.png +0 -0
- data/lib/n65.rb +243 -0
- data/lib/n65/directives/ascii.rb +42 -0
- data/lib/n65/directives/bytes.rb +102 -0
- data/lib/n65/directives/dw.rb +86 -0
- data/lib/n65/directives/enter_scope.rb +55 -0
- data/lib/n65/directives/exit_scope.rb +35 -0
- data/lib/n65/directives/inc.rb +67 -0
- data/lib/n65/directives/incbin.rb +51 -0
- data/lib/n65/directives/ines_header.rb +53 -0
- data/lib/n65/directives/label.rb +46 -0
- data/lib/n65/directives/org.rb +47 -0
- data/lib/n65/directives/segment.rb +45 -0
- data/lib/n65/directives/space.rb +46 -0
- data/lib/n65/front_end.rb +90 -0
- data/lib/n65/instruction.rb +308 -0
- data/lib/n65/instruction_base.rb +29 -0
- data/lib/n65/memory_space.rb +150 -0
- data/lib/n65/opcodes.rb +9 -0
- data/lib/n65/parser.rb +85 -0
- data/lib/n65/regexes.rb +33 -0
- data/lib/n65/symbol_table.rb +198 -0
- data/lib/n65/version.rb +3 -0
- data/n65.gemspec +23 -0
- data/nes_lib/nes.sym +105 -0
- data/test/test_memory_space.rb +82 -0
- data/test/test_symbol_table.rb +238 -0
- data/utils/midi/Makefile +3 -0
- data/utils/midi/c_scale.mid +0 -0
- data/utils/midi/convert +0 -0
- data/utils/midi/guitar.mid +0 -0
- data/utils/midi/include/event.h +93 -0
- data/utils/midi/include/file.h +57 -0
- data/utils/midi/include/helpers.h +14 -0
- data/utils/midi/include/track.h +45 -0
- data/utils/midi/lil_melody.mid +0 -0
- data/utils/midi/mi_feabhra.mid +0 -0
- data/utils/midi/midi_to_nes.rb +204 -0
- data/utils/midi/source/convert.cpp +16 -0
- data/utils/midi/source/event.cpp +96 -0
- data/utils/midi/source/file.cpp +37 -0
- data/utils/midi/source/helpers.cpp +46 -0
- data/utils/midi/source/track.cpp +37 -0
- data/utils/opcode_table_to_yaml.rb +91 -0
- metadata +133 -0
data/examples/beep.asm
ADDED
@@ -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
|
data/examples/mario2.asm
ADDED
@@ -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"
|