Class: MicroCisc::Vm::Device

Inherits:
Object
  • Object
show all
Defined in:
lib/micro_cisc/vm/device.rb

Overview

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

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

  • 0x0 - Device ID, read only. Unique system wide.

  • 0x1 - Bank address (MSB) | Device type (LSB)

  • 0x2 - Bus access device ID

  • 0x3 - Interrupt code (MSB) | Device status (LSB)

  • 0x4 - Local register with interrupt handler address (read/write)

  • 0x5 to 0xF - Device type specific

Direct Known Subclasses

EmptyDevice, Processor, TermDevice

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id, type, local_blocks, rom_blocks = []) ⇒ Device

Returns a new instance of Device.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/micro_cisc/vm/device.rb', line 16

def initialize(id, type, local_blocks, rom_blocks = [])
  @id = id
  @external_read = 0x001F
  @privileged_read = 0x001F
  @privileged_write = 0x0010
  @internal_write = 0x000C

  @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

  @devices = [self]
end

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.



14
15
16
# File 'lib/micro_cisc/vm/device.rb', line 14

def id
  @id
end

Instance Method Details

#bank_index=(index) ⇒ Object



38
39
40
# File 'lib/micro_cisc/vm/device.rb', line 38

def bank_index=(index)
  @control_mem[1] = ((index & 0xFF) << 8) | (@control_mem[1] & 0xFF)
end

#banked?(address) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
90
91
92
93
# File 'lib/micro_cisc/vm/device.rb', line 85

def banked?(address)
  banked = ((address & 0xF000) >> 12)
  if banked == 0
    banked = 1
  else
    banked = 1 << banked
  end
  (banked & @control) != 0
end

#devices=(devices) ⇒ Object



42
43
44
45
# File 'lib/micro_cisc/vm/device.rb', line 42

def devices=(devices)
  @devices = [self] + devices
  @devices.each_with_index { |device, index| device.bank_index = index }
end

#handle_control_read(address) ⇒ Object



77
78
79
# File 'lib/micro_cisc/vm/device.rb', line 77

def handle_control_read(address)
  # Does nothing by default, override in subclass
end

#handle_control_update(address, value) ⇒ Object



81
82
83
# File 'lib/micro_cisc/vm/device.rb', line 81

def handle_control_update(address, value)
  # Does nothing by default, override in subclass
end

#read_control(source_device_id, address) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/micro_cisc/vm/device.rb', line 64

def read_control(source_device_id, address)
  return @control_mem[0] if address == 0
  if 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) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/micro_cisc/vm/device.rb', line 117

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

#source_halted(source_device_id) ⇒ Object



47
48
49
# File 'lib/micro_cisc/vm/device.rb', line 47

def source_halted(source_device_id)
  @control_mem[2] = 0 if source_device_id == @control_mem[2]
end

#write_control(source_device_id, address, value) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/micro_cisc/vm/device.rb', line 51

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 source_device_id == @control_mem[2]
    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) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/micro_cisc/vm/device.rb', line 95

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
    page = (address & 0xFF00) >> 8
    @local_mem[page][address & 0xFF] = value
  elsif source_device_id == @control_mem[2]
    page = (@control_mem[3] & 0xFF00) >> 8
    @local_mem[page][address & 0xFF] = value
  end
end