n65 0.5.0 → 1.0.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +28 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +125 -0
- data/Gemfile +3 -1
- data/README.md +3 -20
- data/Rakefile +15 -1
- data/bin/n65 +2 -0
- data/data/opcodes.yaml +39 -39
- data/everdrive_transfer/everdrive.rb +175 -0
- data/examples/pulse_chord.asm +1 -1
- data/examples/scales.asm +182 -0
- data/lib/n65/directives/ascii.rb +4 -19
- data/lib/n65/directives/bytes.rb +20 -35
- data/lib/n65/directives/dw.rb +22 -36
- data/lib/n65/directives/enter_scope.rb +14 -30
- data/lib/n65/directives/exit_scope.rb +7 -17
- data/lib/n65/directives/inc.rb +14 -30
- data/lib/n65/directives/incbin.rb +6 -24
- data/lib/n65/directives/ines_header.rb +66 -27
- data/lib/n65/directives/label.rb +8 -21
- data/lib/n65/directives/org.rb +9 -19
- data/lib/n65/directives/segment.rb +5 -19
- data/lib/n65/directives/space.rb +6 -18
- data/lib/n65/front_end.rb +36 -39
- data/lib/n65/instruction.rb +123 -159
- data/lib/n65/instruction_base.rb +6 -18
- data/lib/n65/memory_space.rb +51 -71
- data/lib/n65/opcodes.rb +3 -5
- data/lib/n65/parser.rb +20 -38
- data/lib/n65/regexes.rb +20 -21
- data/lib/n65/symbol_table.rb +58 -89
- data/lib/n65/version.rb +3 -1
- data/lib/n65.rb +120 -121
- data/n65.gemspec +17 -12
- data/nes_lib/nes.sym +2 -2
- data/spec/.rubocop.yml +4 -0
- data/spec/assembler_spec.rb +84 -0
- data/spec/lib/n65/memory_space_spec.rb +147 -0
- data/spec/lib/n65/symbol_table_spec.rb +291 -0
- data/utils/opcode_table_to_yaml.rb +65 -67
- data.tar.gz.sig +0 -0
- metadata +84 -41
- metadata.gz.sig +0 -0
- data/examples/music_driver.asm +0 -202
- data/test/test_memory_space.rb +0 -82
- data/test/test_symbol_table.rb +0 -238
- data/utils/midi/Makefile +0 -3
- 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 +0 -93
- data/utils/midi/include/file.h +0 -57
- data/utils/midi/include/helpers.h +0 -14
- data/utils/midi/include/track.h +0 -45
- data/utils/midi/lil_melody.mid +0 -0
- data/utils/midi/mi_feabhra.mid +0 -0
- data/utils/midi/midi_to_nes.rb +0 -204
- data/utils/midi/source/convert.cpp +0 -16
- data/utils/midi/source/event.cpp +0 -96
- data/utils/midi/source/file.cpp +0 -37
- data/utils/midi/source/helpers.cpp +0 -46
- data/utils/midi/source/track.cpp +0 -37
data/examples/music_driver.asm
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
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"
|
data/test/test_memory_space.rb
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
gem 'minitest'
|
|
2
|
-
require 'minitest/autorun'
|
|
3
|
-
require 'minitest/unit'
|
|
4
|
-
|
|
5
|
-
require_relative '../lib/n65/memory_space.rb'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestMemorySpace < MiniTest::Test
|
|
9
|
-
include N65
|
|
10
|
-
|
|
11
|
-
def _test_create_prog_rom
|
|
12
|
-
## First just try to read alll of it
|
|
13
|
-
space = MemorySpace.create_prog_rom
|
|
14
|
-
contents = space.read(0x8000, 0x4000)
|
|
15
|
-
assert_equal(contents.size, 0x4000)
|
|
16
|
-
assert(contents.all?{|byte| byte.zero?})
|
|
17
|
-
|
|
18
|
-
## It is mirrored so this should also work
|
|
19
|
-
space = MemorySpace.create_prog_rom
|
|
20
|
-
contents = space.read(0xC000, 0x4000)
|
|
21
|
-
assert_equal(contents.size, 0x4000)
|
|
22
|
-
assert(contents.all?{|byte| byte.zero?})
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _test_writing
|
|
27
|
-
## Write some bytes into prog 2 area
|
|
28
|
-
space = MemorySpace.create_prog_rom
|
|
29
|
-
space.write(0xC000, "hi there".bytes)
|
|
30
|
-
|
|
31
|
-
## Read them back..
|
|
32
|
-
contents = space.read(0xC000, 8)
|
|
33
|
-
assert_equal('hi there', contents.pack('C*'))
|
|
34
|
-
|
|
35
|
-
## Should be mirrored in prog 1
|
|
36
|
-
contents = space.read(0x8000, 8)
|
|
37
|
-
assert_equal('hi there', contents.pack('C*'))
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _test_reading_out_of_bounds
|
|
42
|
-
space = MemorySpace.create_prog_rom
|
|
43
|
-
assert_raises(MemorySpace::AccessOutsideProgRom) do
|
|
44
|
-
space.read(0x200, 10)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
## But that is valid char rom area, so no explody
|
|
48
|
-
space = MemorySpace.create_char_rom
|
|
49
|
-
space.read(0x200, 10)
|
|
50
|
-
|
|
51
|
-
## But something like this should explode
|
|
52
|
-
space = MemorySpace.create_char_rom
|
|
53
|
-
assert_raises(MemorySpace::AccessOutsideCharRom) do
|
|
54
|
-
space.read(0x8001, 10)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
####
|
|
60
|
-
## There seem to be problems writing bytes right to
|
|
61
|
-
## the end of the memory map, specifically where the
|
|
62
|
-
## vector table is in prog rom, so let's test that.
|
|
63
|
-
def test_writing_to_end
|
|
64
|
-
space = MemorySpace.create_prog_rom
|
|
65
|
-
bytes = [0xDE, 0xAD]
|
|
66
|
-
|
|
67
|
-
## Write the NMI address to FFFA
|
|
68
|
-
space.write(0xFFFA, bytes)
|
|
69
|
-
|
|
70
|
-
## Write the entry point to FFFC
|
|
71
|
-
space.write(0xFFFC, bytes)
|
|
72
|
-
|
|
73
|
-
## Write the irq to FFFE, and this fails, saying
|
|
74
|
-
## I'm trying to write to $10000 for some reason.
|
|
75
|
-
space.write(0xFFFE, bytes)
|
|
76
|
-
|
|
77
|
-
## Write to the very first
|
|
78
|
-
space.write(0x8000, bytes)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
end
|
|
82
|
-
|
data/test/test_symbol_table.rb
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
gem 'minitest'
|
|
2
|
-
require 'minitest/autorun'
|
|
3
|
-
require 'minitest/unit'
|
|
4
|
-
|
|
5
|
-
require_relative '../lib/n65/symbol_table.rb'
|
|
6
|
-
require_relative '../lib/n65.rb'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestSymbolTable < MiniTest::Test
|
|
10
|
-
include N65
|
|
11
|
-
|
|
12
|
-
####
|
|
13
|
-
## Test that we can make simple global symbols
|
|
14
|
-
def test_define_global_symbols
|
|
15
|
-
st = SymbolTable.new
|
|
16
|
-
st.define_symbol('dog', 'woof')
|
|
17
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
####
|
|
22
|
-
## Test entering into a sub scope, and setting and retrieving values
|
|
23
|
-
def test_enter_scope
|
|
24
|
-
st = SymbolTable.new
|
|
25
|
-
st.enter_scope('animals')
|
|
26
|
-
st.define_symbol('dog', 'woof')
|
|
27
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
####
|
|
32
|
-
## Access something from an outer scope without dot syntax
|
|
33
|
-
def test_outer_scope
|
|
34
|
-
st = SymbolTable.new
|
|
35
|
-
st.enter_scope('outer')
|
|
36
|
-
st.define_symbol('dog', 'woof')
|
|
37
|
-
st.enter_scope('inner')
|
|
38
|
-
st.define_symbol('pig', 'oink')
|
|
39
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
####
|
|
44
|
-
## Access something from an outer scope without dot syntax
|
|
45
|
-
def test_shadow
|
|
46
|
-
st = SymbolTable.new
|
|
47
|
-
st.enter_scope('outer')
|
|
48
|
-
st.define_symbol('dog', 'woof')
|
|
49
|
-
st.enter_scope('inner')
|
|
50
|
-
st.define_symbol('dog', 'bark')
|
|
51
|
-
assert_equal('bark', st.resolve_symbol('dog'))
|
|
52
|
-
assert_equal('woof', st.resolve_symbol('outer.dog'))
|
|
53
|
-
st.exit_scope
|
|
54
|
-
st.exit_scope
|
|
55
|
-
assert_equal('bark', st.resolve_symbol('outer.inner.dog'))
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
####
|
|
60
|
-
## Test exiting a sub scope, and seeing that the variable is unavailable by simple name
|
|
61
|
-
def test_exit_scope
|
|
62
|
-
st = SymbolTable.new
|
|
63
|
-
st.enter_scope('animals')
|
|
64
|
-
st.define_symbol('dog', 'woof')
|
|
65
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
66
|
-
|
|
67
|
-
st.exit_scope
|
|
68
|
-
|
|
69
|
-
assert_raises(SymbolTable::UndefinedSymbol) do
|
|
70
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
####
|
|
76
|
-
## Test exiting a sub scope, and being able to access a symbol through a full path
|
|
77
|
-
def test_exit_scope
|
|
78
|
-
st = SymbolTable.new
|
|
79
|
-
st.enter_scope('animals')
|
|
80
|
-
st.define_symbol('dog', 'woof')
|
|
81
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
82
|
-
|
|
83
|
-
st.exit_scope
|
|
84
|
-
|
|
85
|
-
assert_equal('woof', st.resolve_symbol('animals.dog'))
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
####
|
|
90
|
-
## Have two symbols that are the same but are in different scopes
|
|
91
|
-
def test_two_scopes_same_symbol
|
|
92
|
-
st = SymbolTable.new
|
|
93
|
-
st.define_symbol('dog', 'woof')
|
|
94
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
95
|
-
|
|
96
|
-
st.enter_scope('animals')
|
|
97
|
-
|
|
98
|
-
st.define_symbol('dog', 'woofwoof')
|
|
99
|
-
assert_equal('woofwoof', st.resolve_symbol('dog'))
|
|
100
|
-
|
|
101
|
-
st.exit_scope
|
|
102
|
-
|
|
103
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
104
|
-
assert_equal('woofwoof', st.resolve_symbol('animals.dog'))
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
####
|
|
109
|
-
## How do you get stuff out of the global scope when you are in
|
|
110
|
-
## a sub scope?
|
|
111
|
-
def test_access_global_scope
|
|
112
|
-
st = SymbolTable.new
|
|
113
|
-
st.define_symbol('dog', 'woof')
|
|
114
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
115
|
-
|
|
116
|
-
st.enter_scope('animals')
|
|
117
|
-
st.define_symbol('pig', 'oink')
|
|
118
|
-
assert_equal('oink', st.resolve_symbol('pig'))
|
|
119
|
-
|
|
120
|
-
## Ok, now I want to access global.dog basically from the previous scope
|
|
121
|
-
assert_equal('woof', st.resolve_symbol('global.dog'))
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
####
|
|
126
|
-
## Now I want to just test making an anonymous scope
|
|
127
|
-
def test_anonymous_scope
|
|
128
|
-
st = SymbolTable.new
|
|
129
|
-
st.define_symbol('dog', 'woof')
|
|
130
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
131
|
-
|
|
132
|
-
st.enter_scope
|
|
133
|
-
st.define_symbol('pig', 'oink')
|
|
134
|
-
assert_equal('oink', st.resolve_symbol('pig'))
|
|
135
|
-
|
|
136
|
-
## Ok, now I want to access global.dog basically from the previous scope
|
|
137
|
-
assert_equal('woof', st.resolve_symbol('global.dog'))
|
|
138
|
-
|
|
139
|
-
## Now exit the anonymous scope and get dog
|
|
140
|
-
st.exit_scope
|
|
141
|
-
assert_equal('woof', st.resolve_symbol('global.dog'))
|
|
142
|
-
assert_equal('woof', st.resolve_symbol('dog'))
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
####
|
|
147
|
-
## Now I want to test that I cannot exist the outer-most
|
|
148
|
-
## global scope by mistake
|
|
149
|
-
def test_cant_exit_global
|
|
150
|
-
st = SymbolTable.new
|
|
151
|
-
assert_raises(SymbolTable::CantExitScope) do
|
|
152
|
-
st.exit_scope
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
####
|
|
158
|
-
## I would like the name of the scope to take on the
|
|
159
|
-
## value of the program counter at that location.
|
|
160
|
-
def test_scope_as_symbol
|
|
161
|
-
program = <<-ASM
|
|
162
|
-
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
|
|
163
|
-
.org $8000
|
|
164
|
-
.segment prog 0
|
|
165
|
-
.scope main
|
|
166
|
-
sei
|
|
167
|
-
cld
|
|
168
|
-
lda \#$00
|
|
169
|
-
jmp main
|
|
170
|
-
.
|
|
171
|
-
jmp main
|
|
172
|
-
jmp global.main
|
|
173
|
-
ASM
|
|
174
|
-
|
|
175
|
-
#### There really should be an evaluate string method
|
|
176
|
-
assembler = Assembler.new
|
|
177
|
-
program.split(/\n/).each do |line|
|
|
178
|
-
assembler.assemble_one_line(line)
|
|
179
|
-
end
|
|
180
|
-
assembler.fulfill_promises
|
|
181
|
-
assert_equal(0x8000, assembler.symbol_table.resolve_symbol('global.main'))
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
####
|
|
186
|
-
## Fix a bug where we can't see a forward declared symbol in a scope
|
|
187
|
-
def test_foward_declaration_in_scope
|
|
188
|
-
program = <<-ASM
|
|
189
|
-
;;;;
|
|
190
|
-
; Create an iNES header
|
|
191
|
-
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
|
|
192
|
-
|
|
193
|
-
;;;;
|
|
194
|
-
; Try to expose a problem we have with scopes
|
|
195
|
-
; We don't seem to be able to branch to a forward
|
|
196
|
-
; declared symbol within a scope
|
|
197
|
-
.org $8000
|
|
198
|
-
.scope main
|
|
199
|
-
sei
|
|
200
|
-
cld
|
|
201
|
-
lda #\$00
|
|
202
|
-
bne forward_symbol
|
|
203
|
-
nop
|
|
204
|
-
nop
|
|
205
|
-
nop
|
|
206
|
-
forward_symbol:
|
|
207
|
-
rts
|
|
208
|
-
.
|
|
209
|
-
ASM
|
|
210
|
-
|
|
211
|
-
#### There really should be an evaluate string method
|
|
212
|
-
assembler = Assembler.new
|
|
213
|
-
program.split(/\n/).each do |line|
|
|
214
|
-
assembler.assemble_one_line(line)
|
|
215
|
-
end
|
|
216
|
-
puts YAML.dump(assembler.symbol_table)
|
|
217
|
-
assembler.fulfill_promises
|
|
218
|
-
|
|
219
|
-
#### The forward symbol should have been resolved to +3, and the ROM should look like this:
|
|
220
|
-
correct_rom = [0x4e, 0x45, 0x53, 0x1a, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
221
|
-
0x78, # SEI
|
|
222
|
-
0xd8, # CLD
|
|
223
|
-
0xa9, 0x0, # LDA immediate 0
|
|
224
|
-
0xd0, 0x3, # BNE +3
|
|
225
|
-
0xea, # NOP
|
|
226
|
-
0xea, # NOP
|
|
227
|
-
0xea, # NOP
|
|
228
|
-
0x60 # RTS forward_symbol
|
|
229
|
-
]
|
|
230
|
-
|
|
231
|
-
#### Grab the first 26 bytes of the rom and make sure they assemble to the above
|
|
232
|
-
emitted_rom = assembler.emit_binary_rom.bytes[0...26]
|
|
233
|
-
assert_equal(correct_rom, emitted_rom)
|
|
234
|
-
#### Yup it is fixed now.
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
end
|
|
238
|
-
|
data/utils/midi/Makefile
DELETED
data/utils/midi/c_scale.mid
DELETED
|
Binary file
|
data/utils/midi/convert
DELETED
|
Binary file
|
data/utils/midi/guitar.mid
DELETED
|
Binary file
|
data/utils/midi/include/event.h
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
#ifndef MIDI_EVENT_H
|
|
2
|
-
#define MIDI_EVENT_H
|
|
3
|
-
|
|
4
|
-
#include <stdio.h>
|
|
5
|
-
#include <stdlib.h>
|
|
6
|
-
#include "helpers.h"
|
|
7
|
-
|
|
8
|
-
namespace Midi {
|
|
9
|
-
|
|
10
|
-
enum MidiMessageType {Midi = 0, MidiNoteOff = 0x8, MidiNoteOn, MidiPolyAftertouch,
|
|
11
|
-
MidiControlChange, MidiProgramChange, MidiChannelAftertouch,
|
|
12
|
-
MidiPitchWheel, MidiMetaEvent = 0xFF, MidiSysex1 = 0xF0, MidiSysex2 = 0xF7};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
enum MidiMetaType {MetaSequenceNumber = 0x0, MetaTextEvent = 0x1, MetaCopyright = 0x2,
|
|
16
|
-
MetaTrackName = 0x3, MetaInstrumentName = 0x4, MetaLyricText = 0x5,
|
|
17
|
-
MetaMarkerText = 0x6, MetaCuePoint = 0x7, MetaChannelPrefixAssignment = 0x20,
|
|
18
|
-
MetaEndOfTrack = 0x2F, MetaTempoSetting = 0x51, MetaSMPTEOffset = 0x54,
|
|
19
|
-
MetaTimeSignature = 0x58, MetaKeySignature = 0x59, MetaSequenceSpecific = 0x7F};
|
|
20
|
-
|
|
21
|
-
/***
|
|
22
|
-
* Spec:
|
|
23
|
-
* MidiTrackEvent = <delta_time (Variable)> + <MidiEvent> | <MetaEvent> | <SysexEvent>
|
|
24
|
-
* MidiEvent = <MidiMessageType (4 bits)> + <channel (4 bits)> + <parameter1 (1 byte)> <parameter2 (1 byte)>
|
|
25
|
-
* MetaEvent = 0xFF + <MidiMetaType> + <size (Variable)> + <data>
|
|
26
|
-
* SysexEvent = 0xF0 + <data> + 0xF7 -or-
|
|
27
|
-
* SysexEvent = 0xF7 + <data> 0xF7
|
|
28
|
-
***/
|
|
29
|
-
|
|
30
|
-
class Event {
|
|
31
|
-
private:
|
|
32
|
-
unsigned int m_delta;
|
|
33
|
-
unsigned int m_size;
|
|
34
|
-
unsigned int m_data_size;
|
|
35
|
-
unsigned char m_status;
|
|
36
|
-
unsigned char m_meta_type;
|
|
37
|
-
unsigned char m_parameter1;
|
|
38
|
-
unsigned char m_parameter2;
|
|
39
|
-
void *m_data;
|
|
40
|
-
|
|
41
|
-
public:
|
|
42
|
-
Event();
|
|
43
|
-
Event(int delta, int status, int parameter1, int parameter2);
|
|
44
|
-
void init_midi(int delta, int status, int parameter1, int parameter2);
|
|
45
|
-
void init_from_file(FILE *fp, unsigned char last_status);
|
|
46
|
-
~Event();
|
|
47
|
-
|
|
48
|
-
// Some easy inline functions
|
|
49
|
-
static bool is_status_byte(unsigned char byte) { return ((byte & 0x80) >> 7) == 1; }
|
|
50
|
-
unsigned int delta() const { return m_delta; }
|
|
51
|
-
unsigned char status() const { return m_status; }
|
|
52
|
-
unsigned int bytes_read() const { return m_size; }
|
|
53
|
-
unsigned char parameter1() const { return m_parameter1; }
|
|
54
|
-
unsigned char parameter2() const { return m_parameter2; }
|
|
55
|
-
int message_type() const { return (m_status &0xF0) >> 4; }
|
|
56
|
-
int channel() const { return (m_status &0xF); }
|
|
57
|
-
void *sysex_data() const { return m_data; }
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
void print_yaml(){
|
|
61
|
-
printf(" - :delta: 0x%X\n", m_delta);
|
|
62
|
-
printf(" :status: 0x%X\n", m_status);
|
|
63
|
-
|
|
64
|
-
switch(m_status){
|
|
65
|
-
case 0xFF:
|
|
66
|
-
printf(" :meta_type: 0x%X\n", m_meta_type);
|
|
67
|
-
printf(" :meta_data_size: 0x%X\n", m_data_size);
|
|
68
|
-
break;
|
|
69
|
-
case 0xF0:
|
|
70
|
-
case 0xF7:
|
|
71
|
-
printf(" :sysex_data_size: %d\n", m_data_size);
|
|
72
|
-
break;
|
|
73
|
-
default:
|
|
74
|
-
printf(" :parameter1: 0x%X\n", m_parameter1);
|
|
75
|
-
printf(" :parameter2: 0x%X\n", m_parameter2);
|
|
76
|
-
}
|
|
77
|
-
/* Printing out the actual data as a string confuses the YAML parser
|
|
78
|
-
* So let's just not do that.
|
|
79
|
-
if(m_data){
|
|
80
|
-
printf(" :data: \"");
|
|
81
|
-
|
|
82
|
-
for(int i = 0; i < m_data_size; i++){
|
|
83
|
-
printf("%c", ((unsigned char*)m_data)[i]);
|
|
84
|
-
}
|
|
85
|
-
printf("\"\n");
|
|
86
|
-
}
|
|
87
|
-
*/
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
#endif
|
data/utils/midi/include/file.h
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
#ifndef MIDI_FILE_H
|
|
2
|
-
#define MIDI_FILE_H
|
|
3
|
-
|
|
4
|
-
#include <vector>
|
|
5
|
-
#include "track.h"
|
|
6
|
-
|
|
7
|
-
namespace Midi {
|
|
8
|
-
|
|
9
|
-
/***
|
|
10
|
-
* Spec:
|
|
11
|
-
* MidiFile = <midi_header_t> + <MidiTrack> [+ <MidiTrack> ...]
|
|
12
|
-
*
|
|
13
|
-
* midi_header_t = "MThd" + <size (4 bytes)> + <format (2 bytes)> + <track_count (2 bytes)> + <ticks_per_quarter_note (2 bytes)>
|
|
14
|
-
*
|
|
15
|
-
***/
|
|
16
|
-
|
|
17
|
-
typedef struct midi_header_t {
|
|
18
|
-
char cookie[4];
|
|
19
|
-
unsigned int size;
|
|
20
|
-
unsigned short format;
|
|
21
|
-
unsigned short track_count;
|
|
22
|
-
unsigned short ticks_per_quarter_note;
|
|
23
|
-
} __attribute__((packed)) midi_header_t;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class File {
|
|
27
|
-
private:
|
|
28
|
-
midi_header_t m_header;
|
|
29
|
-
|
|
30
|
-
public:
|
|
31
|
-
File(void);
|
|
32
|
-
~File(void);
|
|
33
|
-
std::vector<Track *> m_tracks;
|
|
34
|
-
void init_from_file(const char *filename);
|
|
35
|
-
|
|
36
|
-
void print_yaml(){
|
|
37
|
-
printf("---\n");
|
|
38
|
-
printf(":midi_file:\n");
|
|
39
|
-
printf(" :header: Mthd\n");
|
|
40
|
-
printf(" :size: %u\n", m_header.size);
|
|
41
|
-
printf(" :format: %u\n", m_header.format);
|
|
42
|
-
printf(" :track_count: %u\n", m_header.track_count);
|
|
43
|
-
printf(" :ticks_per_quarter_note: %u\n", m_header.ticks_per_quarter_note);
|
|
44
|
-
printf(" :tracks:\n");
|
|
45
|
-
|
|
46
|
-
for(int i = 0; i < m_tracks.size(); i++){
|
|
47
|
-
if(i == 0){
|
|
48
|
-
m_tracks[i]->print_yaml();
|
|
49
|
-
}else{
|
|
50
|
-
m_tracks[i]->print_yaml();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
#endif
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#ifndef MIDI_HELPERS_H
|
|
2
|
-
#define MIDI_HELPERS_H
|
|
3
|
-
|
|
4
|
-
#include <stdio.h>
|
|
5
|
-
#include <stdlib.h>
|
|
6
|
-
|
|
7
|
-
namespace Midi {
|
|
8
|
-
|
|
9
|
-
unsigned int read_variable_length(FILE *fp, unsigned int *value_size);
|
|
10
|
-
short swap_endian_16(short big_endian);
|
|
11
|
-
int swap_endian_32(int big_endian);
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
#endif
|