Class: C64Asm::Operand

Inherits:
Object
  • Object
show all
Defined in:
lib/c64asm/asm.rb

Overview

Operand, the most important building block

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(op, arg = false, mod = false) ⇒ Operand

Setup and validate an operand

Raises:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/c64asm/asm.rb', line 44

def initialize(op, arg = false, mod = false)
  raise OperandError, 'No such operand' unless OP_CODES.has_key? op

  @op = op
  @mode = false
  @arg = arg
  @mod = mod
  @label = false
  @ready = false

  opcode = OP_CODES[op]

  # mode resolution
  if opcode.has_key? :n
    # all immediates
    @mode = :n
    @ready = true
  elsif not arg and opcode.has_key? :e
    @mode = :e
    @ready = true
  elsif opcode.keys.length == 1
    # branching and jsr
    @mode = opcode.keys.first
  elsif arg and arg.instance_of? Fixnum
    # for the rest, let's try figure out mode by checking argument
    # we treat addressing modes as of higher priority, eg. :z over :d
    if arg >= 0 and arg <= 255
      if opcode.has_key? :z
        @mode = :z
      elsif opcode.has_key? :d
        @mode = :d
      else
        raise OperandError, 'No mode handling byte'
      end
    elsif arg >= 256 and arg <= 65535
      if opcode.has_key? :a
        @mode = :a
      else
        raise OperandError, 'Argument out of range'
      end
    end
  end

  # future reference handling, aka labels
  if arg and arg.instance_of? Symbol
    # labels can point only to absolute addresses
    unless (has_a = opcode.has_key? :a) or opcode.has_key? :r
      raise OperandError, 'Used with label but no :a or :r modes'
    end

    @mode = has_a ? :a : :r
    @label = arg
  end

  # argument checking
  if @mode and not @label
    raise OperandError, 'Invalid argument' unless validate

    @ready = true
  end

  # modifier checking
  check_mod if mod
end

Instance Attribute Details

#argObject (readonly)

Returns the value of attribute arg.



41
42
43
# File 'lib/c64asm/asm.rb', line 41

def arg
  @arg
end

#labelObject (readonly)

Returns the value of attribute label.



41
42
43
# File 'lib/c64asm/asm.rb', line 41

def label
  @label
end

#modeObject (readonly)

Returns the value of attribute mode.



41
42
43
# File 'lib/c64asm/asm.rb', line 41

def mode
  @mode
end

#opObject (readonly)

Returns the value of attribute op.



41
42
43
# File 'lib/c64asm/asm.rb', line 41

def op
  @op
end

Instance Method Details

#check_modObject (private)

Validate modifier

Raises:



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/c64asm/asm.rb', line 225

def check_mod
  raise OperandError, 'Modifier used with non-label argument' unless @label

  if @mod.instance_of? Fixnum
    @mod = [:+, @mod]
  elsif @mod.instance_of? Array and @mod.length == 2 and [:/, :*, :<<, :>>, :& , :|].member? @mod.first
    raise OperandError, 'Arithmetic argument has to be a fixnum' unless @mod[1].instance_of? Fixnum
  elsif [:<, :>].member? @mod
    # this two modifiers make sense only for :d addressing mode
    if not @mode or (@mode and @mode != :d)
      unless OP_CODES[@op].has_key? :d
        raise OperandError, 'Byte modifier used with non-direct addressing mode'
      end

      @mode = :d
    end

    @mod = [@mod == :< ? :ls_byte : :ms_byte]
  else
    raise OperandError, 'Unknown modifier'
  end
end

#check_mode(mode, arg, mod) ⇒ Object (private)

Validate addressing mode

Raises:



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/c64asm/asm.rb', line 195

def check_mode(mode, arg, mod)
  raise OperandError, 'Operand was ready' if @ready
  raise OperandError, 'No such mode' unless OP_CODES[@op].has_key? mode

  @mode = mode
  @arg = arg
  @mod = mod

  case arg
  when Fixnum
    raise OperandError, 'Invalid argument' unless validate
    @ready = true
  when Symbol
    modec = @mode.to_s[0]
    if @mod
      raise OperandError, 'Label used with wrong mode' unless (modec == 'a') or (modec == 'd')
    else
      raise OperandError, 'Label used with wrong mode' unless modec == 'a'
    end
    @label = arg
  else
    raise OperandError, 'Invalid argument type'
  end

  check_mod if mod

  self
end

#lengthObject

Return the length of the additional operand in machine code bytes, or false



164
# File 'lib/c64asm/asm.rb', line 164

def length; @mode ? 1 + ADDR_MODES[@mode][:len] : false; end

#ready?Boolean

Do we have all data in raw form

Returns:

  • (Boolean)


115
# File 'lib/c64asm/asm.rb', line 115

def ready?; @ready; end

#resolve(arg) ⇒ Object

Resolve addresses, if needed

Raises:



118
119
120
121
122
123
124
125
# File 'lib/c64asm/asm.rb', line 118

def resolve(arg)
  return true unless @label

  @mod ? @arg = arg.send(*@mod) : @arg = arg
  raise OperandError, 'Invalid argument' unless validate

  @ready = true
end

#to_aObject

Turn the operand into a byte array Won’t work if we haven’t got all the necessary data yet.



171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/c64asm/asm.rb', line 171

def to_a
  return [] unless @ready

  bytes = [OP_CODES[@op][@mode][:byte]]

  if @arg and @arg > 255
    bytes += [@arg.ls_byte, @arg.ms_byte]
  elsif @arg
    bytes += [@arg]
  else
    bytes
  end
end

#to_binaryObject

Turn the operand into a byte string Won’t work if we haven’t got all the necessary data yet.



187
188
189
190
191
# File 'lib/c64asm/asm.rb', line 187

def to_binary
  return '' unless @ready

  @mode == :r ? to_a.pack('Cc') : to_a.pack('C*')
end

#to_sObject

Return pretty string representation of the operand



167
# File 'lib/c64asm/asm.rb', line 167

def to_s; "<Operand: #{to_source}>"; end

#to_sourceObject

Turn the operand into source code string



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/c64asm/asm.rb', line 128

def to_source
  source = @op.to_s

  if @label
    if @mod
      case @mod.first
      when :+
        label = @label.to_s + ('%+d' % @mod[1])
      when :ls_byte
        label = '<' + @label.to_s
      when :ms_byte
        label = '>' + @label.to_s
      else
        label = @label.to_s + @mod.join
      end
    else
      label = @label.to_s
    end
  end

  unless @mode == :n or @mode == :e
    if @label
      source += ADDR_MODES[@mode][:src] % label
    else
      if @mode == :r
        source += ' *%+d' % @arg.to_s
      else
        source += ADDR_MODES[@mode][:src] % ('$' + @arg.to_s(16))
      end
    end
  end

  source
end

#validateObject (private)

Low-level validation



249
250
251
252
253
254
255
256
257
258
# File 'lib/c64asm/asm.rb', line 249

def validate
  if (@mode == :n and @arg) or \
    (@mode == :r and not (@arg >= -128 and @arg <= 127)) or \
    ([:a, :ax, :ay, :ar].member? @mode and not (@arg >= 0 and @arg <= 65535)) or \
    ([:d, :z, :zx, :zy, :zxr, :zyr].member? @mode and not (@arg >= 0 and @arg <= 255))
    false
  else
    true
  end
end