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
@@ -0,0 +1,96 @@
|
|
1
|
+
#include "event.h"
|
2
|
+
|
3
|
+
namespace Midi {
|
4
|
+
|
5
|
+
////
|
6
|
+
// Default constructor
|
7
|
+
Event::Event(void) :
|
8
|
+
m_delta(0), m_size(0),
|
9
|
+
m_data_size(0), m_status(0),
|
10
|
+
m_meta_type(0), m_parameter1(0),
|
11
|
+
m_parameter2(0), m_data(NULL) { }
|
12
|
+
|
13
|
+
|
14
|
+
////
|
15
|
+
// Constructor to set specific fields
|
16
|
+
Event::Event(int delta, int status, int parameter1, int parameter2) :
|
17
|
+
m_delta(delta), m_size(4),
|
18
|
+
m_data_size(0), m_status(status),
|
19
|
+
m_meta_type(0), m_parameter1(parameter1),
|
20
|
+
m_parameter2(parameter2), m_data(NULL) { }
|
21
|
+
|
22
|
+
|
23
|
+
////
|
24
|
+
// Use a file pointer to initialize
|
25
|
+
void Event::init_from_file(FILE *fp, unsigned char last_status){
|
26
|
+
fpos_t saved_position;
|
27
|
+
unsigned int value_size;
|
28
|
+
// All types of events are preceeded by a uintvar delta time
|
29
|
+
m_delta = read_variable_length(fp, &value_size);
|
30
|
+
m_size += value_size;
|
31
|
+
|
32
|
+
// All types then have a status byte, unless they are reusing
|
33
|
+
// the previous status, get ready to rewind if this is the case.
|
34
|
+
fgetpos(fp, &saved_position);
|
35
|
+
fread(&m_status, sizeof(unsigned char), 1, fp);
|
36
|
+
m_size++;
|
37
|
+
|
38
|
+
if(!is_status_byte(m_status)){
|
39
|
+
// This is not a status byte, so it must be reusing the previous one, Rewind
|
40
|
+
m_status = last_status;
|
41
|
+
fsetpos(fp, &saved_position);
|
42
|
+
m_size--;
|
43
|
+
}
|
44
|
+
|
45
|
+
switch(m_status){
|
46
|
+
case 0xFF: // Meta
|
47
|
+
// This will have a meta sub type
|
48
|
+
fread(&m_meta_type, sizeof(unsigned char), 1, fp);
|
49
|
+
m_size++;
|
50
|
+
// Now a variable size for data
|
51
|
+
m_data_size = read_variable_length(fp, &value_size);
|
52
|
+
m_size += value_size;
|
53
|
+
// Finally, read the meta data
|
54
|
+
m_data = malloc(sizeof(unsigned char) * m_data_size);
|
55
|
+
fread(m_data, sizeof(unsigned char), m_data_size, fp);
|
56
|
+
m_size += m_data_size;
|
57
|
+
break;
|
58
|
+
case 0xF0: // Sysex
|
59
|
+
case 0xF7: // Sysex
|
60
|
+
// Sysex data runs until the next 0xF0 or 0xF7, count how many byte to allocate, and rewind
|
61
|
+
fgetpos(fp, &saved_position);
|
62
|
+
while(m_status != fgetc(fp)){
|
63
|
+
m_data_size++;
|
64
|
+
}
|
65
|
+
fsetpos(fp, &saved_position);
|
66
|
+
|
67
|
+
m_data = malloc(sizeof(unsigned char) * m_data_size);
|
68
|
+
fread(m_data, sizeof(unsigned char), m_data_size, fp);
|
69
|
+
m_size += (m_data_size + 1);
|
70
|
+
fgetc(fp); // Throw away the end byte, although it counts for size
|
71
|
+
break;
|
72
|
+
default: // Midi
|
73
|
+
fread(&m_parameter1, sizeof(unsigned char), 1, fp);
|
74
|
+
fread(&m_parameter2, sizeof(unsigned char), 1, fp);
|
75
|
+
m_size += 2;
|
76
|
+
break;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
void Event::init_midi(int delta, int status, int parameter1, int parameter2){
|
82
|
+
m_delta = delta;
|
83
|
+
m_status = status;
|
84
|
+
m_parameter1 = parameter1;
|
85
|
+
m_parameter2 = parameter2;
|
86
|
+
m_size = 4;
|
87
|
+
}
|
88
|
+
|
89
|
+
|
90
|
+
Event::~Event(void){
|
91
|
+
if(m_data){
|
92
|
+
free(m_data);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#include "file.h"
|
2
|
+
|
3
|
+
namespace Midi {
|
4
|
+
|
5
|
+
File::File(void){
|
6
|
+
m_header.cookie[0] = m_header.cookie[1] = m_header.cookie[2] = m_header.cookie[3] = 0;
|
7
|
+
m_header.size = 0;
|
8
|
+
}
|
9
|
+
|
10
|
+
|
11
|
+
void File::init_from_file(const char *filename){
|
12
|
+
FILE *fp = fopen(filename, "rb");
|
13
|
+
fread(&m_header, sizeof(midi_header_t), 1, fp);
|
14
|
+
// Fix up this Big Endian stuff
|
15
|
+
m_header.size = swap_endian_32(m_header.size);
|
16
|
+
m_header.format = swap_endian_16(m_header.format);
|
17
|
+
m_header.track_count = swap_endian_16(m_header.track_count);
|
18
|
+
m_header.ticks_per_quarter_note = swap_endian_16(m_header.ticks_per_quarter_note);
|
19
|
+
|
20
|
+
// Read each track
|
21
|
+
Track *track;
|
22
|
+
for(int i = 0; i < m_header.track_count; i++){
|
23
|
+
track = new Track();
|
24
|
+
track->init_from_file(fp);
|
25
|
+
m_tracks.push_back(track);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
File::~File(void){
|
31
|
+
// Free all the midi tracks
|
32
|
+
for(unsigned int i = 0; i < m_tracks.size(); i++){
|
33
|
+
delete m_tracks[i];
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#include "helpers.h"
|
2
|
+
|
3
|
+
|
4
|
+
namespace Midi {
|
5
|
+
|
6
|
+
////
|
7
|
+
// I have eliminated any doubt that this function works on variable
|
8
|
+
// length uintvars up to 4 bytes.
|
9
|
+
unsigned int read_variable_length(FILE *fp, unsigned int *value_size) {
|
10
|
+
unsigned int value;
|
11
|
+
*value_size = 1;
|
12
|
+
|
13
|
+
if((value = fgetc(fp)) & 0x80){
|
14
|
+
unsigned char c;
|
15
|
+
value &= 0x7F;
|
16
|
+
do{
|
17
|
+
(*value_size)++;
|
18
|
+
value = (value << 7) + ((c = fgetc(fp)) & 0x7F);
|
19
|
+
} while (c & 0x80);
|
20
|
+
}
|
21
|
+
return(value);
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
////
|
26
|
+
// Swap 4 bytes
|
27
|
+
int swap_endian_32(int big_endian){
|
28
|
+
register int little_endian;
|
29
|
+
little_endian = (big_endian & 0x000000FF);
|
30
|
+
little_endian = ((big_endian & 0x0000FF00) >> 0x08) | (little_endian << 0x08);
|
31
|
+
little_endian = ((big_endian & 0x00FF0000) >> 0x10) | (little_endian << 0x08);
|
32
|
+
little_endian = ((big_endian & 0xFF000000) >> 0x18) | (little_endian << 0x08);
|
33
|
+
return(little_endian);
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
////
|
38
|
+
// Swap 2 bytes
|
39
|
+
short swap_endian_16(short big_endian){
|
40
|
+
register short little_endian;
|
41
|
+
little_endian = (big_endian & 0x00FF);
|
42
|
+
little_endian = ((big_endian & 0xFF00) >> 0x08) | (little_endian << 0x08);
|
43
|
+
return(little_endian);
|
44
|
+
}
|
45
|
+
|
46
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#include "track.h"
|
2
|
+
|
3
|
+
namespace Midi {
|
4
|
+
|
5
|
+
Track::Track(void){
|
6
|
+
m_header.cookie[0] = m_header.cookie[1] = m_header.cookie[2] = m_header.cookie[3] = 0;
|
7
|
+
m_header.size = 0;
|
8
|
+
m_total_size = 0;
|
9
|
+
}
|
10
|
+
|
11
|
+
|
12
|
+
void Track::init_from_file(FILE *fp){
|
13
|
+
Event *track_event;
|
14
|
+
unsigned int bytes_read = 0;
|
15
|
+
unsigned char last_status = 0x0;
|
16
|
+
|
17
|
+
fread(&m_header, sizeof(midi_track_header_t), 1, fp);
|
18
|
+
m_header.size = swap_endian_32(m_header.size);
|
19
|
+
if(m_header.size == 0) return;
|
20
|
+
|
21
|
+
while(bytes_read < m_header.size){
|
22
|
+
track_event = new Event();
|
23
|
+
track_event->init_from_file(fp, last_status);
|
24
|
+
m_events.push_back(track_event);
|
25
|
+
last_status = track_event->status();
|
26
|
+
bytes_read += track_event->bytes_read();
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
Track::~Track(void){
|
31
|
+
// Free all the track events
|
32
|
+
for(unsigned int i = 0; i < m_events.size(); i++){
|
33
|
+
delete m_events[i];
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
}
|
@@ -0,0 +1,91 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
###############################################################################
|
4
|
+
## http://www.6502.org/tutorials/6502opcodes.html
|
5
|
+
## This web page has information about each and every 6502 instruction
|
6
|
+
## Specifically:
|
7
|
+
##
|
8
|
+
## - Description of what each of the instructions do
|
9
|
+
## - Which modes are supported by which instructions, immediate, zero page
|
10
|
+
## zero page x, and y, absolute, indirect, relative etc.
|
11
|
+
## - The hex codes each instruction assembles to, in each mode.
|
12
|
+
## - The lengths in bytes of each instruction, by mode
|
13
|
+
## - The possibly variable number of cycles each instruction takes.
|
14
|
+
##
|
15
|
+
## There are 56 of them, and in my programmer laziness I just wrote this
|
16
|
+
## script to parse the page into the data structure that you see in
|
17
|
+
## opcodes.yaml. This really helped in creating the assembler, and
|
18
|
+
## it had basically everything I needed to know, and sped up writing
|
19
|
+
## this by huge factor. So, yay to this page, and this script!
|
20
|
+
|
21
|
+
require 'yaml'
|
22
|
+
|
23
|
+
## Instruction name, and output structure to fill in.
|
24
|
+
name = :adc
|
25
|
+
output = {name => {}}
|
26
|
+
|
27
|
+
## Copy paste the tables from that website into this heredoc:
|
28
|
+
text =<<-TEXT
|
29
|
+
Immediate ADC #$44 $69 2 2
|
30
|
+
Zero Page ADC $44 $65 2 3
|
31
|
+
Zero Page,X ADC $44,X $75 2 4
|
32
|
+
Absolute ADC $4400 $6D 3 4
|
33
|
+
Absolute,X ADC $4400,X $7D 3 4+
|
34
|
+
Absolute,Y ADC $4400,Y $79 3 4+
|
35
|
+
Indirect,X ADC ($44,X) $61 2 6
|
36
|
+
Indirect,Y ADC ($44),Y $71 2 5+
|
37
|
+
TEXT
|
38
|
+
|
39
|
+
|
40
|
+
## And now iterate over each line to extract the info
|
41
|
+
lines = text.split(/\n/)
|
42
|
+
lines.each do |line|
|
43
|
+
|
44
|
+
## Grab out the values we care about
|
45
|
+
parts = line.split
|
46
|
+
cycles, len, hex = parts[-1], parts[-2], parts[-3]
|
47
|
+
hex = "0x%X" % hex.gsub('$', '').to_i(16)
|
48
|
+
|
49
|
+
match_data = cycles.match(/([0-9]+)(\+?)/)
|
50
|
+
cycles = match_data[1]
|
51
|
+
boundary = match_data[2]
|
52
|
+
hash = {:hex => hex, :len => len, :cycles => cycles, :boundry_add => boundary != ""}
|
53
|
+
|
54
|
+
## And now decide which mode the line belongs to, collecting each listed mode
|
55
|
+
hash = case line
|
56
|
+
when /^Accumulator/
|
57
|
+
{:accumulator => hash}
|
58
|
+
when /^Immediate/
|
59
|
+
{:immediate => hash}
|
60
|
+
when /^Zero Page,X/
|
61
|
+
{:zero_page_x => hash}
|
62
|
+
when /^Zero Page,Y/
|
63
|
+
{:zero_page_y => hash}
|
64
|
+
when /^Zero Page/
|
65
|
+
{:zero_page => hash}
|
66
|
+
when /^Absolute,X/
|
67
|
+
{:absolute_x => hash}
|
68
|
+
when /^Absolute,Y/
|
69
|
+
{:absolute_y => hash}
|
70
|
+
when /^Absolute/
|
71
|
+
{:absolute => hash}
|
72
|
+
when /^Indirect,X/
|
73
|
+
{:indirect_x => hash}
|
74
|
+
when /^Indirect,Y/
|
75
|
+
{:indirect_y => hash}
|
76
|
+
when /^Indirect/
|
77
|
+
{:indirect => hash}
|
78
|
+
when /^Implied/
|
79
|
+
{:implied => hash}
|
80
|
+
else
|
81
|
+
{}
|
82
|
+
end
|
83
|
+
output[name].merge!(hash)
|
84
|
+
end
|
85
|
+
|
86
|
+
## Now output some yaml, and I only had to do this about 45 times
|
87
|
+
## instead of laboriously and mistak-pronely doing it by hand.
|
88
|
+
puts YAML.dump(output).gsub("'", '')
|
89
|
+
|
90
|
+
## See opcodes.yaml
|
91
|
+
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: n65
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Safiire
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: An NES assembler for the 6502 microprocessor written in Ruby
|
42
|
+
email:
|
43
|
+
- safiire@irkenkitties.com
|
44
|
+
executables:
|
45
|
+
- n65
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- bin/n65
|
55
|
+
- data/opcodes.yaml
|
56
|
+
- examples/beep.asm
|
57
|
+
- examples/demo.asm
|
58
|
+
- examples/mario2.asm
|
59
|
+
- examples/mario2.char
|
60
|
+
- examples/music_driver.asm
|
61
|
+
- examples/noise.asm
|
62
|
+
- examples/pulse_chord.asm
|
63
|
+
- images/assembler_demo.png
|
64
|
+
- lib/n65.rb
|
65
|
+
- lib/n65/directives/ascii.rb
|
66
|
+
- lib/n65/directives/bytes.rb
|
67
|
+
- lib/n65/directives/dw.rb
|
68
|
+
- lib/n65/directives/enter_scope.rb
|
69
|
+
- lib/n65/directives/exit_scope.rb
|
70
|
+
- lib/n65/directives/inc.rb
|
71
|
+
- lib/n65/directives/incbin.rb
|
72
|
+
- lib/n65/directives/ines_header.rb
|
73
|
+
- lib/n65/directives/label.rb
|
74
|
+
- lib/n65/directives/org.rb
|
75
|
+
- lib/n65/directives/segment.rb
|
76
|
+
- lib/n65/directives/space.rb
|
77
|
+
- lib/n65/front_end.rb
|
78
|
+
- lib/n65/instruction.rb
|
79
|
+
- lib/n65/instruction_base.rb
|
80
|
+
- lib/n65/memory_space.rb
|
81
|
+
- lib/n65/opcodes.rb
|
82
|
+
- lib/n65/parser.rb
|
83
|
+
- lib/n65/regexes.rb
|
84
|
+
- lib/n65/symbol_table.rb
|
85
|
+
- lib/n65/version.rb
|
86
|
+
- n65.gemspec
|
87
|
+
- nes_lib/nes.sym
|
88
|
+
- test/test_memory_space.rb
|
89
|
+
- test/test_symbol_table.rb
|
90
|
+
- utils/midi/Makefile
|
91
|
+
- utils/midi/c_scale.mid
|
92
|
+
- utils/midi/convert
|
93
|
+
- utils/midi/guitar.mid
|
94
|
+
- utils/midi/include/event.h
|
95
|
+
- utils/midi/include/file.h
|
96
|
+
- utils/midi/include/helpers.h
|
97
|
+
- utils/midi/include/track.h
|
98
|
+
- utils/midi/lil_melody.mid
|
99
|
+
- utils/midi/mi_feabhra.mid
|
100
|
+
- utils/midi/midi_to_nes.rb
|
101
|
+
- utils/midi/source/convert.cpp
|
102
|
+
- utils/midi/source/event.cpp
|
103
|
+
- utils/midi/source/file.cpp
|
104
|
+
- utils/midi/source/helpers.cpp
|
105
|
+
- utils/midi/source/track.cpp
|
106
|
+
- utils/opcode_table_to_yaml.rb
|
107
|
+
homepage: http://github.com/safiire/n65
|
108
|
+
licenses:
|
109
|
+
- GPL2
|
110
|
+
metadata: {}
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.4.5
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: An NES assembler for the 6502 microprocessor written in Ruby
|
131
|
+
test_files:
|
132
|
+
- test/test_memory_space.rb
|
133
|
+
- test/test_symbol_table.rb
|