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 - Local bank block (MSB) | Device type (LSB) - read only

  • 0x2 - Init device ID - read only if set, writable if 0 or by init device

  • 0x3 - Accessed device block address (LSB) - writable by init device

  • 0x4 - <Reserved> (MSB) | Device status (LSB)

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

  • 0x6 to 0xF - Device type specific (see below)

Direct Known Subclasses

ColorLcdDisplay, EmptyDevice, Processor, TermDevice

Constant Summary collapse

TYPE_INVALID =
0
TYPE_PROCESSOR =
1
TYPE_BLOCK_MEMORY =
2
TYPE_BLOCK_IO =
3
TYPE_SERIAL =
4
TYPE_HID =
5
TYPE_TERMINAL =
6

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of Device.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 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

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.



23
24
25
# File 'lib/micro_cisc/vm/device.rb', line 23

def id
  @id
end

Instance Method Details

#bank_index=(index) ⇒ Object



48
49
50
# 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) ⇒ Boolean

Returns:

  • (Boolean)


96
97
98
99
100
101
102
103
104
# 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) ⇒ Object



52
53
54
55
# 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) ⇒ Object



88
89
90
# 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) ⇒ Object



92
93
94
# 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) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
# 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) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 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) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 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) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# 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