r6502 0.0.1

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MGMyOTkxOTE5YjI1MTI5MTZmODQwMzI2OGJiMDUzZWRhYzcyZDVkNg==
5
+ data.tar.gz: !binary |-
6
+ NDVhZmEwZjc0NmY4MWU3OTYxZmY2YmY2NmQ2YjllMjBkMDAwZDVkMQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NThlMDg4ODY5MDgxMjU1NDVmMzVhODgyM2I5OTQ5MGI1OTc2MDZhMzY5NDYx
10
+ NTYzYmE3NTFmYjQ2NWJmNjQxZTVkY2EyMDkyMzNmYjg5NWI0NmQ1M2M1ZDY3
11
+ MzM1NDNhNGVjMmI3Y2I3YzNiNDBlNjZjZDFlYmY1NWE3ZDZkOTg=
12
+ data.tar.gz: !binary |-
13
+ OGQyMjFhN2FjNTRiODgwMjY2NmQxMzE5MjEwMDdmYWM4YjcxODkxYzU5ZWU1
14
+ OGQ5ZDkxMTYxOTE0NDJhZjQxY2QyNGQ1M2ZkN2FmZjg3MThlZmIwN2QwNmY1
15
+ Y2IxY2MyOTA2NDcwZjc0ZWM5ZmFlYjUyZWUxMTJhYTUyMDkyNmE=
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Joe Landers
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Update: 14 November 2012
2
+ It works. [See my blog post](http://0xfffc.tumblr.com/post/35751220092/6502-assembler-and-simulator-in-ruby)
3
+
4
+ It probably bears no resemblance to the JS code
5
+ which inspired the project, so don't get confused
6
+ by that.
7
+
8
+ Also, the code (and especially the specs) need
9
+ cleanup and refactoring, so don't judge. :)
10
+
11
+ # Original intro:
12
+ Work-in-progress assembler and simulator of the 6502,
13
+ written in Ruby.
14
+
15
+ Originally, I was going to fork Nick Morgan's JS simulation
16
+ (github.com/skilldrick/6502js), but I decided to do a
17
+ rewrite in Ruby. (He's got a very nice interactive "book"
18
+ on 6502 assembly programming.)
19
+
20
+ Nick's version is a modification of the excellent original,
21
+ by Stian Soreng (6502asm.com), so probably the most thanks
22
+ for the code goes to him.
23
+
24
+ I'm using the JS code as reference, but probably most of
25
+ the internal structure will be quite different.
26
+
27
+ MIT Licensed
data/bin/r6502 ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib',__FILE__)
3
+ require 'r6502'
4
+
5
+ @mem = R6502::Memory.new
6
+ @asm = R6502::Assembler.new(@mem)
7
+ @cpu = R6502::Cpu.new(@mem)
8
+ @cpu.pc = 0x0600 #start of execution, for now
9
+
10
+ file = []
11
+ puts "Hello! Enter some 6502 assembly, and an empty line when done."
12
+ until (line = gets.chomp).empty?
13
+ file << line
14
+ end
15
+
16
+ @asm.process_file( file.join("\n") )
17
+
18
+ puts "Now give me the first and last address of the memory"
19
+ puts "that you want me to print out."
20
+ inpt = gets
21
+ first = inpt.split[0].to_i(16)
22
+ second = inpt.split[1].to_i(16)
23
+ @mem.get_range(first, second).each {|b| print ("%02x " % b)}
24
+ print "\n\n"
25
+
26
+ puts "Now I'll run that code"
27
+
28
+ while( @mem.get(@cpu.pc) != 0 ) do
29
+ @cpu.step
30
+ end
31
+
32
+ puts "Now give me the first and last address of the memory"
33
+ puts "that you want me to print out."
34
+ inpt = gets
35
+ first = inpt.split[0].to_i(16)
36
+ second = inpt.split[1].to_i(16)
37
+ @mem.get_range(first, second).each {|b| print ("%02x " % b)}
38
+ print "\n\n"
39
+
40
+ puts "That looks excellent. Have a good day."
41
+
data/lib/r6502.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'r6502/memory'
2
+ require 'r6502/assembler'
3
+ require 'r6502/opcode_table'
4
+ require 'r6502/instr_table'
5
+ require 'r6502/cpu_instructions.rb'
6
+ require 'r6502/cpu_execution.rb'
@@ -0,0 +1,204 @@
1
+ module R6502
2
+ class Assembler
3
+
4
+ def initialize(memory)
5
+ @memory = memory
6
+ @pc = 0x600
7
+ # @labels holds 'label' => 0xabcd mappings
8
+ # where the address is what gets subbed for the label
9
+ @labels = {}
10
+ # @deferred holds 0x0601 => 'main' mappings
11
+ # where the addr. marks memory we need to fill in 2nd pass
12
+ # (after we know all the label => addr. mappings)
13
+ @deferred = {}
14
+ end
15
+
16
+ def pc
17
+ @pc
18
+ end
19
+
20
+ def labels
21
+ @labels
22
+ end
23
+
24
+ def deferred
25
+ @deferred
26
+ end
27
+
28
+ def label_get(label)
29
+ @labels[label]
30
+ end
31
+
32
+ def uncomment(line)
33
+ line.sub(/\s*;.*/, '')
34
+ end
35
+
36
+ def extract_command(instr)
37
+ instr = instr.sub(/^\s*/, '')
38
+ instr.sub(/\s.*/, '').to_sym
39
+ end
40
+
41
+ def extract_param(instr)
42
+ instr.sub(/[a-zA-Z]+\s*/, '').downcase
43
+ end
44
+
45
+ def addr_mode(param)
46
+ case param
47
+ when /#\$[0-9a-f]{1,2}$/
48
+ :imm
49
+ when /\$[0-9a-f]{1,2}$/
50
+ :zp
51
+ when /\$[0-9a-f]{1,2},x$/
52
+ :zpx
53
+ when /\$[0-9a-f]{1,2},y$/
54
+ :zpy
55
+ when /\$[0-9a-f]{3,4}$/
56
+ :abs
57
+ when /^[a-z]+/
58
+ :abs
59
+ when /\$[0-9a-f]{3,4},x$/
60
+ :absx
61
+ when /\$[0-9a-f]{3,4},y$/
62
+ :absy
63
+ when /\(\$[0-9a-f]{3,4}\)$/
64
+ :ind
65
+ when /\(\$[0-9a-f]{1,2},x\)$/
66
+ :indx
67
+ when /\(\$[0-9a-f]{1,2}\),y$/
68
+ :indy
69
+ when //
70
+ :imp
71
+ end
72
+ end
73
+
74
+ # This method got nasty.
75
+ def asm_instr(instr)
76
+ command = extract_command(instr)
77
+ param = extract_param(instr)
78
+
79
+ # Branch instructions always in relative
80
+ # mode. No other instructions use this mode.
81
+ # Relative mode and zero-page mode look the
82
+ # same to addr_mode(), so we need to handle
83
+ # this here.
84
+ if [:bpl, :bmi, :bvc, :bvs,
85
+ :bcc, :bcs, :bne, :beq].
86
+ include?(command)
87
+ mode = :rel
88
+ else
89
+ mode = addr_mode(param)
90
+ end
91
+
92
+ bytes = []
93
+ bytes << opcode(command, mode)
94
+
95
+ # If implied mode, it's a 1-byte instruction.
96
+ if [:imp].include?(mode)
97
+ return bytes
98
+ end
99
+
100
+ # Handle label or address / immediate value
101
+ if param =~ /\$/ # non-labels always have a $
102
+ # Extract hex number from param string.
103
+ number = /[0-9a-f]{1,4}/.match(param)[0].to_i(16)
104
+ else
105
+ # Store a dummy value and record this location
106
+ # to be updated in 2nd pass.
107
+ defer_value(@pc + 1, param)
108
+ number = mode == :rel ? 0xff : 0xffff
109
+ end
110
+
111
+ # These instructions take 1 byte.
112
+ if [:imm, :zp, :zpx, :zpy,
113
+ :indx, :indy, :rel].include?(mode)
114
+ (number <= 0xff) || (raise "#{command}'s number too big")
115
+ return bytes << number
116
+ # These instructions take 2 bytes.
117
+ elsif [:abs, :absx, :absy, :ind].include?(mode)
118
+ (number <= 0xffff) || (raise 'number too big')
119
+ bytes << (number & 0xff) # least-sig. byte
120
+ bytes << (number >> 8) # most-sig. byte
121
+ end
122
+ end
123
+
124
+ def write_byte(byte)
125
+ @memory.set(@pc, byte)
126
+ @pc += 1
127
+ end
128
+
129
+ def process_line(line) # can have label, cmd, param, cmt
130
+ line.downcase!
131
+ instr = uncomment(line) # strips comment
132
+ instr = delabel(instr) # strips label
133
+ if instr == '' then return end
134
+ bytes = asm_instr(instr) # convert remainder to machine code
135
+ bytes.each { |b| write_byte(b) } # pop bytes into memory
136
+ end
137
+
138
+ def delabel(instr)
139
+ instr = instr.sub(/^\s+/, '') #strip leading whitespace
140
+ first = instr.split(/\s+/)[0] #get first non-ws substr.
141
+ # if first word is an instruction, then there's
142
+ # no label in this instr, so we're done.
143
+ if ['adc', 'and', 'asl', 'bit', 'bpl', 'bmi',
144
+ 'bvc', 'bvs', 'bcc', 'bcs', 'bne', 'beq',
145
+ 'brk', 'cmp', 'cpx', 'cpy', 'dec', 'eor',
146
+ 'clc', 'sec', 'cli', 'sei', 'clv', 'cld',
147
+ 'sed', 'inc', 'jmp', 'jsr', 'lda', 'ldx',
148
+ 'ldy', 'lsr', 'nop', 'ora', 'tax', 'txa',
149
+ 'dex', 'inx', 'tay', 'tya', 'dey', 'iny',
150
+ 'ror', 'rol', 'rti', 'rts', 'sbc', 'sta',
151
+ 'txs', 'tsx', 'pha', 'pla', 'php', 'plp',
152
+ 'stx', 'sty', 'nil'].include?(first) || first == nil
153
+ return instr
154
+ end
155
+
156
+ # otherwise, it is a label.
157
+ first = /\w+/.match(first)[0]
158
+ new_label(first)
159
+
160
+ return instr.sub(/^\S+\s*/, '') # rm label from instr
161
+ end
162
+
163
+ def new_label(name)
164
+ @labels.member?(name) && (raise 'label already defined')
165
+ @labels[name] = @pc
166
+ end
167
+
168
+ def defer_value(addr, label)
169
+ @deferred[addr] = label
170
+ end
171
+
172
+ def process_file(lines)
173
+ # First pass places machine code and records the locations
174
+ # of referred-to labels, the values of which we'll need
175
+ # to write in the second pass.
176
+ lines.split("\n").each do |line|
177
+ process_line( line )
178
+ end
179
+ # Goes through the saved locations (filled with dummy
180
+ # values) and writes the real values.
181
+ @deferred.keys.each do |addr|
182
+ instr = @memory.get( addr - 1 )
183
+ # hopefully temporary quickfix
184
+ if [0x10, 0x30, 0x50, 0x70,
185
+ 0x90, 0xb0, 0xd0, 0xf0].include?(instr)
186
+ to_addr = label_get( @deferred[addr] )
187
+ delta = to_addr - addr - 1
188
+ raise 'bad rel address' unless (-128..127).include? delta
189
+ value = delta < 0 ? 256 + delta : delta
190
+ @memory.set( addr, value )
191
+ else
192
+ both_bytes = label_get( @deferred[addr] )
193
+ low_byte = both_bytes & 0x00ff
194
+ high_byte = both_bytes >> 8
195
+ @memory.set( addr, low_byte )
196
+ @memory.set( addr+1, high_byte )
197
+ end
198
+ end
199
+
200
+ end
201
+
202
+ end
203
+ end
204
+
@@ -0,0 +1,95 @@
1
+ module R6502
2
+ class Cpu
3
+ attr_accessor :mem, :pc, :s, :x, :y, :a
4
+ attr_accessor :c, :z, :i, :d, :b, :v, :n
5
+ def initialize(mem)
6
+ @mem = mem
7
+ @pc = @mem.get_word(0xfffc)
8
+ @s = 0xff
9
+ @x = 0x00
10
+ @y = 0x00
11
+ @a = 0x00
12
+ @c = 0
13
+ @z = 0
14
+ @i = 0
15
+ @d = 0
16
+ @b = 0
17
+ @v = 0
18
+ @n = 0
19
+ end
20
+ def step
21
+ instr, mode = instr_mode( mem.get(pc) )
22
+ arg = decode_arg( mode, mem.get(pc+1), mem.get(pc+2) )
23
+
24
+ puts "instr: #{instr} at pc 0x#{pc.to_s(16)} with arg #{arg.to_i.to_s(16)}"
25
+ method( instr ).call( arg, mode )
26
+ puts " a: #{a.to_s(16)} x: #{x.to_s(16)} y: #{y.to_s(16)} z: #{z.to_s(16)} n: #{n.to_s(16)} c: #{c.to_s(16)}"
27
+ puts "==="
28
+ end
29
+ def decode_arg(mode, sec_word, thd_word)
30
+ case mode
31
+ when :imp
32
+ nil
33
+ when :imm
34
+ sec_word
35
+ when :zp
36
+ sec_word
37
+ when :zpx
38
+ sec_word + @x
39
+ when :zpy
40
+ sec_word + @y
41
+ when :rel
42
+ sec_word <= 127 ? sec_word : sec_word - 256
43
+ when :abs
44
+ (thd_word<<8) + sec_word
45
+ when :absx
46
+ (thd_word<<8) + sec_word + @x
47
+ when :absy
48
+ (thd_word<<8) + sec_word + @y
49
+ when :ind
50
+ lb = @mem.get( (thd_word<<8) + sec_word )
51
+ hb = @mem.get( (thd_word<<8) + sec_word + 1 )
52
+ (hb<<8) + lb
53
+ when :indx
54
+ lb = @mem.get( 0xff & (@x + sec_word) )
55
+ hb = @mem.get( 0xff & (@x + sec_word) + 1 )
56
+ @mem.get( (hb<<8) + lb )
57
+ when :indy
58
+ lb = @mem.get( 0xff & sec_word )
59
+ hb = @mem.get( 0xff & sec_word + 1)
60
+ addr = (hb<<8) + lb
61
+ @mem.get( addr + @y )
62
+ end
63
+ end
64
+ def inc_pc_by_mode(mode)
65
+ case mode
66
+ when :imp
67
+ @pc += 1
68
+ when :acc
69
+ @pc += 1
70
+ when :imm
71
+ @pc += 2
72
+ when :zp
73
+ @pc += 2
74
+ when :zpx
75
+ @pc += 2
76
+ when :zpy
77
+ @pc += 2
78
+ when :abs
79
+ @pc += 3
80
+ when :absx
81
+ @pc += 3
82
+ when :absy
83
+ @pc += 3
84
+ when :indx
85
+ @pc += 2
86
+ when :indy
87
+ @pc += 2
88
+ when :rel
89
+ @pc += 2
90
+ when :ind #only used by jmp, which explicitly sets the pc,
91
+ @pc += 3 #so hopefully this is never used here.
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,517 @@
1
+ module R6502
2
+ class Cpu
3
+ # add with carry
4
+ def adc(arg, mode)
5
+ x = @a
6
+ y = mode == :imm ? arg : @mem.get(arg)
7
+ if @d == 0 #normal binary mode
8
+ r = x + y + @c
9
+ @a = 0xff & r
10
+ @v = (((0x7f&x) + (0x7f&y) + @c)>>7) ^ ((x + y + @c)>>8)
11
+ @z = (r&0xff).zero? ? 1 : 0
12
+ @c = r > 255 ? 1 : 0
13
+ @n = (0x80&r)>>7
14
+ else #BCD mode
15
+ ones = (0xf&x) + (0xf&y)
16
+ tens = ((0xf0&x)>>4) + ((0xf0&y)>>4)
17
+ r0 = ones + 10*tens + @c
18
+ @c = r0 > 99 ? 1 : 0
19
+ r = r0 % 100
20
+ @z = r.zero? ? 1 : 0
21
+ @a = r + 6*((r/10).floor)
22
+ @n = (0x80&@a)>>7
23
+ end
24
+ inc_pc_by_mode(mode)
25
+ end
26
+ # subtract with carry
27
+ def sbc(arg, mode)
28
+ x = @a
29
+ y = mode == :imm ? arg : @mem.get(arg)
30
+ if @d == 0 #normal binary mode
31
+ y = (y^0xff)
32
+ r = x + y + @c
33
+ @a = 0xff & r
34
+ @v = (((0x7f&x) + (0x7f&y) + @c)>>7) ^ ((x + y + @c)>>8)
35
+ @z = (0xff&r).zero? ? 1 : 0
36
+ @c = r > 255 ? 1 : 0
37
+ @n = (0x80&r)>>7
38
+ else #BCD mode
39
+ ones = (0xf&x) - (0xf&y)
40
+ tens = ((0xf0&x)>>4) - ((0xf0&y)>>4)
41
+ r0 = ones + 10*tens - (1 - @c)
42
+ @c = r0 >= 0 ? 1 : 0
43
+ r = r0 % 100
44
+ @z = r.zero? ? 1 : 0
45
+ @a = r + 6*((r/10).floor)
46
+ @n = (0x80&@a)>>7
47
+ end
48
+ inc_pc_by_mode(mode)
49
+ end
50
+ # logical and
51
+ def and(arg, mode)
52
+ case mode
53
+ when :imm
54
+ @a = @a & arg
55
+ else
56
+ @a = @a & @mem.get(arg)
57
+ end
58
+ @z = @a.zero? ? 1 : 0
59
+ @n = @a>>7
60
+ inc_pc_by_mode(mode)
61
+ end
62
+ # shift left
63
+ def asl(arg, mode)
64
+ case mode
65
+ when :acc
66
+ r = @a<<1
67
+ @a = r&0xff
68
+ @z = @a.zero? ? 1 : 0
69
+ @n = @a>>7
70
+ @c = r > 0xff ? 1 : 0
71
+ else
72
+ r = @mem.get(arg)<<1
73
+ @mem.set( arg, r&0xff )
74
+ @z = r.zero? ? 1 : 0
75
+ @n = r>>7
76
+ @c = r > 0xff ? 1 : 0
77
+ end
78
+ inc_pc_by_mode(mode)
79
+ end
80
+ # logical and (result discarded)
81
+ def bit(arg, mode)
82
+ m = @mem.get( arg )
83
+ a = @a
84
+ result = a & m
85
+ @z = result.zero? ? 1 : 0
86
+ @v = (m & 0x40)>>6
87
+ @n = (m & 0x80)>>7
88
+ inc_pc_by_mode(mode)
89
+ end
90
+ # decrement (memory)
91
+ def dec(arg, mode)
92
+ r = @mem.get(arg) - 1
93
+ @mem.set( arg, r&0xff )
94
+ @z = r.zero? ? 1 : 0
95
+ @n = (r&0x80)>>7
96
+ inc_pc_by_mode(mode)
97
+ end
98
+ # decrement (x)
99
+ def dex(arg, mode)
100
+ r = @x - 1
101
+ @x = r&0xff
102
+ @z = @x.zero? ? 1 : 0
103
+ @n = (@x&0x80)>>7
104
+ inc_pc_by_mode(mode)
105
+ end
106
+ # decrement (y)
107
+ def dey(arg, mode)
108
+ r = @y - 1
109
+ @y = r&0xff
110
+ @z = @y.zero? ? 1 : 0
111
+ @n = (@y&0x80)>>7
112
+ inc_pc_by_mode(mode)
113
+ end
114
+ # exclusive or
115
+ def eor(arg, mode)
116
+ case mode
117
+ when :imm
118
+ @a = @a ^ arg
119
+ else
120
+ @a = @a ^ @mem.get(arg)
121
+ end
122
+ @z = @a.zero? ? 1 : 0
123
+ @n = (@a&0x80)>>7
124
+ inc_pc_by_mode(mode)
125
+ end
126
+ # increment (memory)
127
+ def inc(arg, mode)
128
+ r = (@mem.get(arg) + 1)&0xff
129
+ @mem.set( arg, r )
130
+ @z = r.zero? ? 1 : 0
131
+ @n = (r&0x80)>>7
132
+ inc_pc_by_mode(mode)
133
+ end
134
+ # increment (x)
135
+ def inx(arg, mode)
136
+ r = (@x + 1)&0xff
137
+ @x = r
138
+ @z = r.zero? ? 1 : 0
139
+ @n = (r&0x80)>>7
140
+ inc_pc_by_mode(mode)
141
+ end
142
+ # increment (y)
143
+ def iny(arg, mode)
144
+ r = (@y + 1)&0xff
145
+ @y = r
146
+ @z = r.zero? ? 1 : 0
147
+ @n = (r&0x80)>>7
148
+ inc_pc_by_mode(mode)
149
+ end
150
+ # shift right
151
+ def lsr(arg, mode)
152
+ case mode
153
+ when :acc
154
+ @c = 0x01&@a
155
+ @a = @a>>1
156
+ @z = @a.zero? ? 1 : 0
157
+ @n = (@a&0x80)>>7 #bit 7 will always be zero, though
158
+ else
159
+ v = @mem.get(arg)
160
+ @c = 0x01&v
161
+ r = v>>1
162
+ @z = r.zero? ? 1 : 0
163
+ @n = (r&0x80)>>7
164
+ @mem.set( arg, r )
165
+ end
166
+ inc_pc_by_mode(mode)
167
+ end
168
+ # inclusive or
169
+ def ora(arg, mode)
170
+ case mode
171
+ when :imm
172
+ @a = @a | arg
173
+ else
174
+ @a = @a | @mem.get( arg )
175
+ end
176
+ @z = @a.zero? ? 1 : 0
177
+ @n = (@a&0x80)>>7
178
+ inc_pc_by_mode(mode)
179
+ end
180
+ # rotate left
181
+ def rol(arg, mode)
182
+ case mode
183
+ when :acc
184
+ c = @c
185
+ @c = (@a&0x80)>>7
186
+ @a = 0xff & (@a<<1) | c
187
+ @z = @a.zero? ? 1 : 0
188
+ @n = (@a&0x80)>>7
189
+ else
190
+ val = @mem.get(arg)
191
+ c = @c
192
+ @c = (val&0x80)>>7
193
+ r = 0xff & (val<<1) | c
194
+ @z = r.zero? ? 1 : 0
195
+ @n = (r&0x80)>>7
196
+ @mem.set(arg, r)
197
+ end
198
+ inc_pc_by_mode(mode)
199
+ end
200
+ # rotate right
201
+ def ror(arg, mode)
202
+ case mode
203
+ when :acc
204
+ c = @c
205
+ @c = @a&0x01
206
+ @a = (@a>>1) | (c<<7)
207
+ @z = @a.zero? ? 1 : 0
208
+ @n = (@a&0x80)>>7
209
+ else
210
+ val = @mem.get(arg)
211
+ c = @c
212
+ @c = val&0x01
213
+ r = (val>>1) | (c<<7)
214
+ @mem.set(arg, r)
215
+ @z = r.zero? ? 1 : 0
216
+ @n = (r&0x80)>>7
217
+ end
218
+ inc_pc_by_mode(mode)
219
+ end
220
+ # no operation
221
+ def nop(arg, mode)
222
+ inc_pc_by_mode(mode)
223
+ end
224
+ def sec(arg, mode)
225
+ @c = 1
226
+ inc_pc_by_mode(mode)
227
+ end
228
+ def sed(arg, mode)
229
+ @d = 1
230
+ inc_pc_by_mode(mode)
231
+ end
232
+ def sei(arg, mode)
233
+ @i = 1
234
+ inc_pc_by_mode(mode)
235
+ end
236
+ def bcc(arg, mode)
237
+ inc_pc_by_mode(:rel)
238
+ @pc += arg if @c == 0
239
+ end
240
+ def bcs(arg, mode)
241
+ inc_pc_by_mode(:rel)
242
+ @pc += arg if @c == 1
243
+ end
244
+ def beq(arg, mode)
245
+ inc_pc_by_mode(:rel)
246
+ @pc += arg if @z == 1
247
+ end
248
+ def bmi(arg, mode)
249
+ inc_pc_by_mode(:rel)
250
+ @pc += arg if @n == 1
251
+ end
252
+ def bne(arg, mode)
253
+ inc_pc_by_mode(:rel)
254
+ @pc += arg if @z == 0
255
+ end
256
+ def bpl(arg, mode)
257
+ inc_pc_by_mode(:rel)
258
+ @pc += arg if @n == 0
259
+ end
260
+ def bvc(arg, mode)
261
+ inc_pc_by_mode(:rel)
262
+ @pc += arg if @v == 0
263
+ end
264
+ def bvs(arg, mode)
265
+ inc_pc_by_mode(:rel)
266
+ @pc += arg if @v == 1
267
+ end
268
+ def clc(arg, mode)
269
+ @c = 0
270
+ inc_pc_by_mode(mode)
271
+ end
272
+ def cld(arg, mode)
273
+ @d = 0
274
+ inc_pc_by_mode(mode)
275
+ end
276
+ def cli(arg, mode)
277
+ @i = 0
278
+ inc_pc_by_mode(mode)
279
+ end
280
+ def clv(arg, mode)
281
+ @v = 0
282
+ inc_pc_by_mode(mode)
283
+ end
284
+ def cmp(arg, mode)
285
+ case mode
286
+ when :imm
287
+ result = @a - arg
288
+ @c = result >= 0 ? 1 : 0
289
+ @z = result == 0 ? 1 : 0
290
+ @n = (0xff&result)>>7
291
+ else
292
+ m = @mem.get( arg )
293
+ result = @a - m
294
+ @c = result >= 0 ? 1 : 0
295
+ @z = result == 0 ? 1 : 0
296
+ @n = (0xff&result)>>7
297
+ end
298
+ inc_pc_by_mode(mode)
299
+ end
300
+ def cpx(arg, mode)
301
+ case mode
302
+ when :imm
303
+ result = @x - arg
304
+ @c = result >= 0 ? 1 : 0
305
+ @z = result == 0 ? 1 : 0
306
+ @n = (0xff&result)>>7
307
+ else
308
+ m = @mem.get( arg )
309
+ result = @x - m
310
+ @c = result >= 0 ? 1 : 0
311
+ @z = result == 0 ? 1 : 0
312
+ @n = (0xff&result)>>7
313
+ end
314
+ inc_pc_by_mode(mode)
315
+ end
316
+ def cpy(arg, mode)
317
+ case mode
318
+ when :imm
319
+ result = @y - arg
320
+ @c = result >= 0 ? 1 : 0
321
+ @z = result == 0 ? 1 : 0
322
+ @n = (0xff&result)>>7
323
+ else
324
+ m = @mem.get( arg )
325
+ result = @y - m
326
+ @c = result >= 0 ? 1 : 0
327
+ @z = result == 0 ? 1 : 0
328
+ @n = (0xff&result)>>7
329
+ end
330
+ inc_pc_by_mode(mode)
331
+ end
332
+ def jmp(arg, mode)
333
+ case mode
334
+ when :abs
335
+ @pc = arg
336
+ else #indirect
337
+ lsb = @mem.get( arg )
338
+ msb = @mem.get( arg + 1)
339
+ @pc = lsb + msb<<8
340
+ end
341
+ end
342
+ def lda(arg, mode)
343
+ case mode
344
+ when :imm
345
+ @a = arg
346
+ else
347
+ @a = @mem.get( arg )
348
+ end
349
+ @z = @a.zero? ? 1 : 0
350
+ @n = (0x80&@a)>>7
351
+ inc_pc_by_mode(mode)
352
+ end
353
+ def ldx(arg, mode)
354
+ case mode
355
+ when :imm
356
+ @x = arg
357
+ else
358
+ @x = @mem.get( arg )
359
+ end
360
+ @z = @x.zero? ? 1 : 0
361
+ @n = (0x80&@x)>>7
362
+ inc_pc_by_mode(mode)
363
+ end
364
+ def ldy(arg, mode)
365
+ case mode
366
+ when :imm
367
+ @y = arg
368
+ else
369
+ @y = @mem.get( arg )
370
+ end
371
+ @z = @y.zero? ? 1 : 0
372
+ @n = (0x80&@y)>>7
373
+ inc_pc_by_mode(mode)
374
+ end
375
+ def pha(arg, mode)
376
+ addr = 0x0100 + (0xff & @s)
377
+ @mem.set( addr, @a )
378
+ @s -= 1
379
+ inc_pc_by_mode(mode)
380
+ end
381
+ def pla(arg, mode)
382
+ addr = 0x0100 + (0xff & (@s + 1))
383
+ @a = @mem.get( addr )
384
+ @s += 1
385
+ @z = @a.zero? ? 1 : 0
386
+ @n = (0x80&@a)>>7
387
+ inc_pc_by_mode(mode)
388
+ end
389
+ def php(arg, mode)
390
+ addr = 0x0100 + (0xff & @s)
391
+ val = @n #bit 7
392
+ val = (val<<1) + @v #bit 6
393
+ val = (val<<1) + 1 #bit 5
394
+ val = (val<<1) + @b #bit 4
395
+ val = (val<<1) + @d #bit 3
396
+ val = (val<<1) + @i #bit 2
397
+ val = (val<<1) + @z #bit 1
398
+ val = (val<<1) + @c #bit 0
399
+
400
+ @mem.set( addr, val )
401
+ @s -= 1
402
+ inc_pc_by_mode(mode)
403
+ end
404
+ def plp(arg, mode)
405
+ addr = 0x0100 + (0xff & (@s + 1))
406
+ val = @mem.get( addr )
407
+ @c = 0x1 & val
408
+ @z = 0x1 & (val>>1)
409
+ @i = 0x1 & (val>>2)
410
+ @d = 0x1 & (val>>3)
411
+ @b = 0x1 & (val>>4)
412
+ # bit 5
413
+ @v = 0x1 & (val>>6)
414
+ @n = 0x1 & (val>>7)
415
+ inc_pc_by_mode(mode)
416
+ end
417
+ def sta(arg, mode)
418
+ @mem.set( arg, @a )
419
+ inc_pc_by_mode(mode)
420
+ end
421
+ def stx(arg, mode)
422
+ @mem.set( arg, @x )
423
+ inc_pc_by_mode(mode)
424
+ end
425
+ def sty(arg, mode)
426
+ @mem.set( arg, @y )
427
+ inc_pc_by_mode(mode)
428
+ end
429
+ def tax(arg, mode)
430
+ @x = @a
431
+ @z = @x.zero? ? 1 : 0
432
+ @n = (0x80&@x)>>7
433
+ inc_pc_by_mode(mode)
434
+ end
435
+ def tay(arg, mode)
436
+ @y = @a
437
+ @z = @y.zero? ? 1 : 0
438
+ @n = (0x80&@y)>>7
439
+ inc_pc_by_mode(mode)
440
+ end
441
+ def tsx(arg, mode)
442
+ @x = @s
443
+ @z = @x.zero? ? 1 : 0
444
+ @n = (0x80&@x)>>7
445
+ inc_pc_by_mode(mode)
446
+ end
447
+ def txa(arg, mode)
448
+ @a = @x
449
+ @z = @a.zero? ? 1 : 0
450
+ @n = (0x80&@a)>>7
451
+ inc_pc_by_mode(mode)
452
+ end
453
+ def txs(arg, mode)
454
+ @s = @x
455
+ inc_pc_by_mode(mode)
456
+ end
457
+ def tya(arg, mode)
458
+ @a = @y
459
+ @z = @a.zero? ? 1 : 0
460
+ @n = (0x80&@a)>>7
461
+ inc_pc_by_mode(mode)
462
+ end
463
+ def brk(arg, mode)
464
+ @mem.set(0x0100 + @s, @pc>>8)
465
+ @s -= 1
466
+ @mem.set(0x0100 + @s, (0xff&@pc))
467
+ @s -= 1
468
+ val = @n #bit 7
469
+ val = (val<<1) + @v #bit 6
470
+ val = (val<<1) + 1 #bit 5
471
+ val = (val<<1) + 1 #bit 4 break flag
472
+ val = (val<<1) + @d #bit 3
473
+ val = (val<<1) + @i #bit 2
474
+ val = (val<<1) + @z #bit 1
475
+ val = (val<<1) + @c #bit 0
476
+ @mem.set(0x0100 + @s, val)
477
+ @s -= 1
478
+
479
+ lo = @mem.get(0xfffe)
480
+ hi = @mem.get(0xffff)
481
+ @pc = (hi<<8) + lo
482
+ end
483
+ def rti( arg, mode )
484
+ flags = @mem.get(0x0100 + @s + 1)
485
+ @s += 1
486
+
487
+ @c = 0x1 & flags
488
+ @z = 0x1 & (flags>>1)
489
+ @i = 0x1 & (flags>>2)
490
+ @d = 0x1 & (flags>>3)
491
+ @b = 0x1 & (flags>>4)
492
+ # bit 5
493
+ @v = 0x1 & (flags>>6)
494
+ @n = 0x1 & (flags>>7)
495
+
496
+ hi = @mem.get(0x0100 + @s + 1)
497
+ @s += 1
498
+ lo = @mem.get(0x0100 + @s + 1)
499
+ @s += 1
500
+ @pc = 0xffff&((hi<<8) + lo)
501
+ end
502
+ def jsr( arg, mode )
503
+ @mem.set(0x0100 + @s, @pc>>8)
504
+ @s -= 1
505
+ @mem.set(0x0100 + @s, (0xff&@pc) + 2)
506
+ @s -= 1
507
+ @pc = arg
508
+ end
509
+ def rts( arg, mode )
510
+ hi = @mem.get(0x0100 + @s + 1)
511
+ @s += 1
512
+ lo = @mem.get(0x0100 + @s + 1)
513
+ @s += 1
514
+ @pc = 0xffff&((hi<<8) + lo + 1)
515
+ end
516
+ end
517
+ end