r6502 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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