n65 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|