class MicroCisc::Vm::Device

This is a generic device base class providing memory and control access

From the docs, the control word layout is as follows:

Constants

TYPE_BLOCK_IO
TYPE_BLOCK_MEMORY
TYPE_HID
TYPE_INVALID
TYPE_PROCESSOR
TYPE_SERIAL
TYPE_TERMINAL

Attributes

id[R]

Public Class Methods

new(id, type, local_blocks, rom_blocks = []) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 25
def initialize(id, type, local_blocks, rom_blocks = [])
  @id = id
  @external_read = 0x003F
  @privileged_read = 0x003F
  @privileged_write = 0x002C
  @internal_write = 0x0010

  @local_blocks = local_blocks
  rom_blocks.each { |block| block.freeze }
  ram_blocks = Array.new(local_blocks - rom_blocks.size).map do
    Array.new(256).map { 0 }
  end
  @local_mem = rom_blocks + ram_blocks

  @control = 0
  @control_mem = Array.new(16).map { 0 }
  @control_mem[0] = id
  @control_mem[1] = type & 0xFF
  @control_mem[2] = 0

  @devices = [self]
end

Public Instance Methods

bank_index=(index) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 48
def bank_index=(index)
  @control_mem[1] = ((index & 0xFF) << 8) | (@control_mem[1] & 0xFF)
end
banked?(address) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 96
def banked?(address)
  banked = ((address & 0xF000) >> 12)
  if banked == 0
    banked = 1
  else
    banked = 1 << banked
  end
  (banked & @control) != 0
end
devices=(devices) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 52
def devices=(devices)
  @devices = [self] + devices
  @devices.each_with_index { |device, index| device.bank_index = index }
end
handle_control_read(address) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 88
def handle_control_read(address)
  # Does nothing by default, override in subclass
end
handle_control_update(address, value) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 92
def handle_control_update(address, value)
  # Does nothing by default, override in subclass
end
read_control(source_device_id, address) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 73
def read_control(source_device_id, address)
  if address == 0
    handle_control_read(address)
    @control_mem[0]
  elsif source_device_id == @id || source_device_id == @control_mem[2]
    return 0 if (1 << (address - 1)) & @privileged_read == 0
    handle_control_read(address)
    @control_mem[address]
  else
    return 0 if (1 << (address - 1)) & @external_read == 0
    handle_control_read(address)
    @control_mem[address]
  end
end
read_mem(source_device_id, address, force_local = false) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 128
def read_mem(source_device_id, address, force_local = false)
  banked = banked?(address) && !force_local
  device = (address >> 4)
  if banked && source_device_id == @id && device < @devices.size
    @devices[device].read_control(source_device_id, address & 0xF)
  elsif banked && source_device_id == @id && device >= 256
    device = (address >> 8)
    if device < @devices.size
      @devices[device].read_mem(source_device_id, address & 0xFF)
    else
      page = (address & 0xFF00) >> 8
      @local_mem[page][address & 0xFF]
    end
  elsif !banked && source_device_id == @id
    page = (address & 0xFF00) >> 8
    @local_mem[page][address & 0xFF]
  elsif source_device_id == @control_mem[2]
    page = @control_mem[3] & 0xFF00
    @local_mem[page][address * 0xFF]
  end
end
write_control(source_device_id, address, value) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 57
def write_control(source_device_id, address, value)
  address = address & 0xF
  return if address == 0
  if source_device_id == @id
    return if (1 << (address - 1)) & @internal_write == 0
    @control_mem[address] = value
  elsif @control_mem[2] == 0 || source_device_id == @control_mem[2]
    # Special case where a processor can "claim" the device
    @control_mem[2] = source_device_id

    return if (1 << (address - 1)) & @privileged_write == 0
    @control_mem[address] = value
    handle_control_update(address, value)
  end
end
write_mem(source_device_id, address, value) click to toggle source
# File lib/micro_cisc/vm/device.rb, line 106
def write_mem(source_device_id, address, value)
  banked = banked?(address)
  device = (address >> 4)
  if banked && source_device_id == @id && device < @devices.size
    @devices[device].write_control(source_device_id, address & 0xF, value)
  elsif banked && source_device_id == @id && device >= 256
    device = (address >> 8)
    if device < @devices.size
      @devices[device].write_mem(source_device_id, address & 0xFF, value)
    else
      page = (address & 0xFF00) >> 8
      @local_mem[page][address & 0xFF] = value
    end
  elsif !banked && source_device_id == @id
    block = (address & 0xFF00) >> 8
    @local_mem[block][address & 0xFF] = value
  elsif source_device_id == @control_mem[2]
    block = @control_mem[3]
    @local_mem[block][address & 0xFF] = value if @local_mem[block]
  end
end