r6502 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE +20 -0
- data/README.md +27 -0
- data/bin/r6502 +41 -0
- data/lib/r6502.rb +6 -0
- data/lib/r6502/assembler.rb +204 -0
- data/lib/r6502/cpu_execution.rb +95 -0
- data/lib/r6502/cpu_instructions.rb +517 -0
- data/lib/r6502/instr_table.rb +213 -0
- data/lib/r6502/memory.rb +21 -0
- data/lib/r6502/opcode_table.rb +64 -0
- data/r6502.gemspec +13 -0
- data/spec/r6502/assembler_spec.rb +226 -0
- data/spec/r6502/cpu_execution_spec.rb +119 -0
- data/spec/r6502/cpu_instructions_spec.rb +1284 -0
- data/spec/r6502/memory_spec.rb +37 -0
- data/spec/spec_helper.rb +1 -0
- metadata +60 -0
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,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
|