n65 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +340 -0
  5. data/README.md +126 -0
  6. data/Rakefile +2 -0
  7. data/bin/n65 +11 -0
  8. data/data/opcodes.yaml +1030 -0
  9. data/examples/beep.asm +24 -0
  10. data/examples/mario2.asm +260 -0
  11. data/examples/mario2.char +0 -0
  12. data/examples/music_driver.asm +202 -0
  13. data/examples/noise.asm +93 -0
  14. data/examples/pulse_chord.asm +213 -0
  15. data/images/assembler_demo.png +0 -0
  16. data/lib/n65.rb +243 -0
  17. data/lib/n65/directives/ascii.rb +42 -0
  18. data/lib/n65/directives/bytes.rb +102 -0
  19. data/lib/n65/directives/dw.rb +86 -0
  20. data/lib/n65/directives/enter_scope.rb +55 -0
  21. data/lib/n65/directives/exit_scope.rb +35 -0
  22. data/lib/n65/directives/inc.rb +67 -0
  23. data/lib/n65/directives/incbin.rb +51 -0
  24. data/lib/n65/directives/ines_header.rb +53 -0
  25. data/lib/n65/directives/label.rb +46 -0
  26. data/lib/n65/directives/org.rb +47 -0
  27. data/lib/n65/directives/segment.rb +45 -0
  28. data/lib/n65/directives/space.rb +46 -0
  29. data/lib/n65/front_end.rb +90 -0
  30. data/lib/n65/instruction.rb +308 -0
  31. data/lib/n65/instruction_base.rb +29 -0
  32. data/lib/n65/memory_space.rb +150 -0
  33. data/lib/n65/opcodes.rb +9 -0
  34. data/lib/n65/parser.rb +85 -0
  35. data/lib/n65/regexes.rb +33 -0
  36. data/lib/n65/symbol_table.rb +198 -0
  37. data/lib/n65/version.rb +3 -0
  38. data/n65.gemspec +23 -0
  39. data/nes_lib/nes.sym +105 -0
  40. data/test/test_memory_space.rb +82 -0
  41. data/test/test_symbol_table.rb +238 -0
  42. data/utils/midi/Makefile +3 -0
  43. data/utils/midi/c_scale.mid +0 -0
  44. data/utils/midi/convert +0 -0
  45. data/utils/midi/guitar.mid +0 -0
  46. data/utils/midi/include/event.h +93 -0
  47. data/utils/midi/include/file.h +57 -0
  48. data/utils/midi/include/helpers.h +14 -0
  49. data/utils/midi/include/track.h +45 -0
  50. data/utils/midi/lil_melody.mid +0 -0
  51. data/utils/midi/mi_feabhra.mid +0 -0
  52. data/utils/midi/midi_to_nes.rb +204 -0
  53. data/utils/midi/source/convert.cpp +16 -0
  54. data/utils/midi/source/event.cpp +96 -0
  55. data/utils/midi/source/file.cpp +37 -0
  56. data/utils/midi/source/helpers.cpp +46 -0
  57. data/utils/midi/source/track.cpp +37 -0
  58. data/utils/opcode_table_to_yaml.rb +91 -0
  59. 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