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
@@ -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
|