ruby6502 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ #ifndef FAKE6502
2
+ #define FAKE6502
3
+
4
+ void reset6502();
5
+ void step6502();
6
+ void exec6502(uint32_t tickcount);
7
+ void nmi6502();
8
+ void irq6502();
9
+ void hookexternal(void *funcptr);
10
+
11
+ uint16_t getPC();
12
+ uint8_t getSP();
13
+ uint8_t getA();
14
+ uint8_t getX();
15
+ uint8_t getY();
16
+ uint8_t getStatus();
17
+ uint32_t getInstructions();
18
+ uint64_t getTicks();
19
+
20
+ #endif
@@ -0,0 +1,253 @@
1
+ #include <stdlib.h>
2
+ #include <time.h>
3
+ #include <stdint.h>
4
+ #include <ruby.h>
5
+ #include "fake6502.h"
6
+
7
+ static VALUE mRuby6502;
8
+ uint8_t has_instruction_hooks = 0;
9
+ uint8_t has_read_write_hooks = 0;
10
+
11
+ uint16_t rng_hook_address = 0;
12
+
13
+ #define MEMSIZE 0x10000
14
+ uint8_t MEMORY[MEMSIZE] = {0};
15
+
16
+ static VALUE program_counter(VALUE self)
17
+ {
18
+ return UINT2NUM(getPC());
19
+ }
20
+
21
+ static VALUE stack_pointer(VALUE self)
22
+ {
23
+ return UINT2NUM(getSP());
24
+ }
25
+
26
+ static VALUE a_register(VALUE self)
27
+ {
28
+ return UINT2NUM(getA());
29
+ }
30
+
31
+ static VALUE x_register(VALUE self)
32
+ {
33
+ return UINT2NUM(getX());
34
+ }
35
+
36
+ static VALUE y_register(VALUE self)
37
+ {
38
+ return UINT2NUM(getY());
39
+ }
40
+
41
+ static VALUE status_flags(VALUE self)
42
+ {
43
+ return UINT2NUM(getStatus());
44
+ }
45
+
46
+ static VALUE instruction_count(VALUE self)
47
+ {
48
+ return ULONG2NUM(getInstructions());
49
+ }
50
+
51
+ static VALUE tick_count(VALUE self)
52
+ {
53
+ return ULL2NUM(getTicks());
54
+ }
55
+
56
+ static VALUE reset(VALUE self)
57
+ {
58
+ reset6502();
59
+ return Qtrue;
60
+ }
61
+
62
+ static VALUE interrupt_request(VALUE self)
63
+ {
64
+ irq6502();
65
+ return Qtrue;
66
+ }
67
+
68
+ static VALUE non_maskable_interrupt(VALUE self)
69
+ {
70
+ nmi6502();
71
+ return Qtrue;
72
+ }
73
+
74
+ static VALUE step(VALUE self)
75
+ {
76
+ step6502();
77
+ return instruction_count(self);
78
+ }
79
+
80
+ static VALUE step_times(VALUE self, VALUE stepCount)
81
+ {
82
+ int steps = NUM2INT(stepCount);
83
+ for(int i = 0; i < steps; ++i) {
84
+ step6502();
85
+ }
86
+
87
+ return instruction_count(self);
88
+ }
89
+
90
+ static VALUE exec(VALUE self, VALUE tickCount)
91
+ {
92
+ exec6502((uint32_t) NUM2ULONG(tickCount));
93
+ return instruction_count(self);
94
+ }
95
+
96
+ static VALUE memory_size(VALUE self)
97
+ {
98
+ return UINT2NUM(MEMSIZE);
99
+ }
100
+
101
+ static uint8_t read_address(uint16_t address)
102
+ {
103
+ if ( address >= 0 && address < MEMSIZE ) {
104
+ return MEMORY[address];
105
+ } else return 0;
106
+ }
107
+
108
+ uint8_t read6502(uint16_t address)
109
+ {
110
+
111
+ if ( has_read_write_hooks ) {
112
+ rb_funcall(mRuby6502, rb_intern("execute_read_write_hook"), 2, UINT2NUM(address), ID2SYM(rb_intern("read")));
113
+ }
114
+
115
+ return read_address(address);
116
+ }
117
+
118
+ static VALUE read_byte(VALUE self, VALUE location)
119
+ {
120
+ uint16_t address;
121
+
122
+ address = (uint16_t) NUM2UINT(location);
123
+
124
+ return UINT2NUM(read_address(address));
125
+ }
126
+
127
+ static void write_address(uint16_t address, uint8_t value)
128
+ {
129
+ if ( address >= 0 && address < MEMSIZE ) {
130
+ MEMORY[address] = value;
131
+ }
132
+ }
133
+
134
+ void write6502(uint16_t address, uint8_t value)
135
+ {
136
+ if ( has_read_write_hooks ) {
137
+ rb_funcall(mRuby6502, rb_intern("execute_read_write_hook"), 2, INT2NUM(address), ID2SYM(rb_intern("write")));
138
+ }
139
+
140
+ write_address(address, value);
141
+ }
142
+
143
+ static VALUE load_byte(VALUE self, VALUE location, VALUE r_value)
144
+ {
145
+ uint16_t address;
146
+ uint8_t value;
147
+
148
+ address = (uint16_t) NUM2UINT(location);
149
+ value = (uint8_t) NUM2UINT(r_value);
150
+
151
+ write_address(address, value);
152
+
153
+ return location;
154
+ }
155
+
156
+ static VALUE set_instruction_hooks(VALUE self)
157
+ {
158
+ has_instruction_hooks = 1;
159
+ return Qtrue;
160
+ }
161
+
162
+ static VALUE unset_instruction_hooks(VALUE self)
163
+ {
164
+ has_instruction_hooks = 0;
165
+ return Qtrue;
166
+ }
167
+
168
+ static VALUE get_has_instruction_hooks(VALUE self)
169
+ {
170
+ if ( has_instruction_hooks ) {
171
+ return Qtrue;
172
+ } else {
173
+ return Qfalse;
174
+ }
175
+ }
176
+
177
+ static VALUE set_read_write_hooks(VALUE self)
178
+ {
179
+ has_read_write_hooks = 1;
180
+ return Qtrue;
181
+ }
182
+
183
+ static VALUE unset_read_write_hooks(VALUE self)
184
+ {
185
+ has_read_write_hooks = 0;
186
+ return Qtrue;
187
+ }
188
+
189
+ static VALUE get_has_read_write_hooks(VALUE self)
190
+ {
191
+ if ( has_read_write_hooks ) {
192
+ return Qtrue;
193
+ } else {
194
+ return Qfalse;
195
+ }
196
+ }
197
+
198
+ static VALUE configure_rng(VALUE self, VALUE location) {
199
+ uint16_t address;
200
+
201
+ address = (uint16_t) NUM2UINT(location);
202
+
203
+ if ( address > 0 && address < MEMSIZE ) {
204
+ rng_hook_address = address;
205
+ }
206
+ }
207
+
208
+ void execute_instruction_hooks() {
209
+ if ( rng_hook_address ) {
210
+ write_address(rng_hook_address, (uint8_t) rand());
211
+ }
212
+
213
+ if ( has_instruction_hooks ) {
214
+ rb_funcall(mRuby6502, rb_intern("execute_instruction_hooks"), 0);
215
+ }
216
+ }
217
+
218
+ void Init_ruby6502()
219
+ {
220
+ mRuby6502 = rb_define_module("Ruby6502");
221
+ rb_define_singleton_method(mRuby6502, "memory_size", memory_size, 0);
222
+ rb_define_singleton_method(mRuby6502, "read_byte", read_byte, 1);
223
+ rb_define_singleton_method(mRuby6502, "load_byte", load_byte, 2);
224
+
225
+ rb_define_singleton_method(mRuby6502, "program_counter", program_counter, 0);
226
+ rb_define_singleton_method(mRuby6502, "stack_pointer", stack_pointer, 0);
227
+ rb_define_singleton_method(mRuby6502, "a_register", a_register, 0);
228
+ rb_define_singleton_method(mRuby6502, "x_register", x_register, 0);
229
+ rb_define_singleton_method(mRuby6502, "y_register", y_register, 0);
230
+ rb_define_singleton_method(mRuby6502, "status_flags", status_flags, 0);
231
+ rb_define_singleton_method(mRuby6502, "instruction_count", instruction_count, 0);
232
+ rb_define_singleton_method(mRuby6502, "tick_count", tick_count, 0);
233
+
234
+ rb_define_singleton_method(mRuby6502, "set_instruction_hooks", set_instruction_hooks, 0);
235
+ rb_define_singleton_method(mRuby6502, "unset_instruction_hooks", unset_instruction_hooks, 0);
236
+ rb_define_singleton_method(mRuby6502, "instruction_hooks?", get_has_instruction_hooks, 0);
237
+ rb_define_singleton_method(mRuby6502, "configure_rng", configure_rng, 1);
238
+
239
+ rb_define_singleton_method(mRuby6502, "set_read_write_hooks", set_read_write_hooks, 0);
240
+ rb_define_singleton_method(mRuby6502, "unset_read_write_hooks", unset_read_write_hooks, 0);
241
+ rb_define_singleton_method(mRuby6502, "read_write_hooks?", get_has_read_write_hooks, 0);
242
+
243
+ rb_define_singleton_method(mRuby6502, "reset", reset, 0);
244
+ rb_define_singleton_method(mRuby6502, "interrupt_request", interrupt_request, 0);
245
+ rb_define_singleton_method(mRuby6502, "non_maskable_interrupt", non_maskable_interrupt, 0);
246
+ rb_define_singleton_method(mRuby6502, "step", step, 0);
247
+ rb_define_singleton_method(mRuby6502, "step_times", step_times, 1);
248
+ rb_define_singleton_method(mRuby6502, "exec", exec, 1);
249
+
250
+ hookexternal(execute_instruction_hooks);
251
+
252
+ srand ((unsigned int) time (NULL));
253
+ }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruby6502
4
+ VERSION = "0.1.0"
5
+ end
data/lib/ruby6502.rb ADDED
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby6502/ruby6502"
4
+
5
+ module Ruby6502
6
+ INSTRUCTION_HOOKS = []
7
+ READ_WRITE_HOOKS = {}
8
+
9
+ class << self
10
+ def load(bytearray, location: 0)
11
+ byte_size = bytearray.size
12
+ location = location.to_i
13
+
14
+ if location < 0
15
+ raise "Cannot load to a negative memory location"
16
+ end
17
+
18
+ if memory_size < location + byte_size
19
+ raise "Loading #{byte_size} bytes to #{format("%04x", location)} would overflow memory"
20
+ end
21
+
22
+ bytearray.each do |byte|
23
+ byte_to_load = byte.to_i & 0xff
24
+ load_byte(location, byte_to_load)
25
+ location += 1
26
+ end
27
+ end
28
+
29
+ def read(location:, bytes:)
30
+ location = location.to_i
31
+ bytes = bytes.to_i
32
+
33
+ unless location >= 0 && location < memory_size
34
+ raise "#{location} is outside memory bounds"
35
+ end
36
+
37
+ unless bytes >= 0
38
+ raise "Must read a positive number of bytes"
39
+ end
40
+
41
+ raise "#{format("%04x", location + bytes)} is outside bounds" if location + bytes > memory_size
42
+
43
+ bytes.times.map do |byte|
44
+ read_byte(location + byte)
45
+ end
46
+ end
47
+
48
+ def register_instruction_hook(&hook)
49
+ set_instruction_hooks unless instruction_hooks?
50
+ INSTRUCTION_HOOKS << hook
51
+ end
52
+
53
+ def clear_instruction_hooks
54
+ unset_instruction_hooks
55
+ INSTRUCTION_HOOKS.clear
56
+ end
57
+
58
+ def register_read_write_hook(location, read_or_write, &hook)
59
+ read_or_write = read_or_write.to_sym
60
+ unless [:read, :write, :read_write].include?(read_or_write)
61
+ raise "#{read_or_write} must be one of :read, :write, :read_write"
62
+ end
63
+
64
+ set_read_write_hooks unless read_write_hooks?
65
+
66
+ if read_or_write == :read_write
67
+ READ_WRITE_HOOKS[[location, :read]] = hook
68
+ READ_WRITE_HOOKS[[location, :write]] = hook
69
+ else
70
+ READ_WRITE_HOOKS[[location, read_or_write]] = hook
71
+ end
72
+ end
73
+
74
+ def deregister_read_write_hook(location, read_or_write)
75
+ if read_or_write == :read_write
76
+ READ_WRITE_HOOKS.delete([location, :read])
77
+ READ_WRITE_HOOKS.delete([location, :write])
78
+ else
79
+ READ_WRITE_HOOKS.delete([location, read_or_write])
80
+ end
81
+
82
+ unset_read_write_hooks if READ_WRITE_HOOKS.empty?
83
+ end
84
+
85
+ private :read_byte, :load_byte, :set_instruction_hooks, :unset_instruction_hooks, :instruction_hooks?,
86
+ :set_read_write_hooks, :unset_read_write_hooks, :read_write_hooks?
87
+
88
+ def execute_instruction_hooks
89
+ INSTRUCTION_HOOKS.each(&:call)
90
+ end
91
+
92
+ def execute_read_write_hook(location, read_or_write)
93
+ READ_WRITE_HOOKS[[location, read_or_write]]&.call(read_or_write)
94
+ end
95
+ end
96
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby6502
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kyle Tate
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.31'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.31'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-shopify
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.8'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.8'
83
+ description: A Ruby emulator for the 6502. The 6502 is powered by http://rubbermallet.org/fake6502.c
84
+ email: kbt.tate@gmail.com
85
+ executables: []
86
+ extensions:
87
+ - ext/ruby6502/extconf.rb
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ext/ruby6502/extconf.rb
91
+ - ext/ruby6502/fake6502.c
92
+ - ext/ruby6502/fake6502.h
93
+ - ext/ruby6502/ruby6502.c
94
+ - lib/ruby6502.rb
95
+ - lib/ruby6502/version.rb
96
+ homepage: https://github.com/infiton/ruby6502
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '2.7'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.3.7
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Ruby 6502 emulator
119
+ test_files: []