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