class Lignite::SystemCommands

The commands that cannot appear in a .rbf program, used mostly for program manipulation

Public Class Methods

new(conn = Connection.create) click to toggle source

@param conn [Connection]

# File lib/lignite/system_commands.rb, line 16
def initialize(conn = Connection.create)
  @conn = conn
  load_yml
end
run(conn = Connection.create, &block) click to toggle source
# File lib/lignite/system_commands.rb, line 9
def self.run(conn = Connection.create, &block)
  sc = new(conn)
  sc.instance_exec(&block)
  sc.close
end

Public Instance Methods

close() click to toggle source
# File lib/lignite/system_commands.rb, line 21
def close
  @conn.close
end

Private Instance Methods

assert_match(actual, expected, description) click to toggle source
# File lib/lignite/system_commands.rb, line 125
def assert_match(actual, expected, description)
  return if actual == expected
  raise "#{description} does not match, expected #{expected}, actual #{actual}"
end
handlers(odata) click to toggle source
# File lib/lignite/system_commands.rb, line 74
def handlers(odata)
  oparams = odata["params"]
  param_handlers = []
  return_handlers = []
  oparams.each do |p|
    if p["dir"] == "in"
      param_handlers << param_handler(p)
    else
      return_handlers << return_handler(p)
    end
  end
  [param_handlers, return_handlers]
end
load_op(oname, odata) click to toggle source

oname LIST_FILES

# File lib/lignite/system_commands.rb, line 36
def load_op(oname, odata)
  ovalue = odata["value"]

  param_handlers, return_handlers = handlers(odata)

  osym = oname.downcase.to_sym
  self.class.send(:define_method, osym) do |*args|
    logger.debug "called #{osym} with #{args.inspect}"
    if args.size != param_handlers.size
      raise ArgumentError, "expected #{param_handlers.size} arguments, got #{args.size}"
    end

    bytes = u8(ovalue)
    bytes += param_handlers.zip(args).map do |h, a|
      # h.call(a) would have self = Op instead of #<Op>
      instance_exec(a, &h)
    end.join("")
    logger.debug "sysop to execute: #{bytes.inspect}"

    reply = system_command_with_reply(bytes)

    parse_reply(reply, return_handlers)
  end
end
load_yml() click to toggle source
# File lib/lignite/system_commands.rb, line 27
def load_yml
  fname = File.expand_path("../../../data/sysops.yml", __FILE__)
  op_hash = YAML.load_file(fname)["sysops"]
  op_hash.each do |oname, odata|
    load_op(oname, odata)
  end
end
param_handler(oparam) click to toggle source
# File lib/lignite/system_commands.rb, line 88
def param_handler(oparam)
  case oparam["type"]
  when "U8" then ->(x) { u8(x) }
  when "U16" then ->(x) { u16(x) }
  when "U32" then ->(x) { u32(x) }
  when "BYTES" then ->(x) { x }
  when "ZBYTES" then ->(x) { x + u8(0) }
  else
    raise
  end
end
parse_reply(reply, return_handlers) click to toggle source

@param reply [ByteString] @param return_handlers [Array<Proc>] @return [Object,Array<Object>]

# File lib/lignite/system_commands.rb, line 64
def parse_reply(reply, return_handlers)
  replies = return_handlers.map do |h|
    parsed, reply = h.call(reply)
    parsed
  end
  raise "Unparsed reply #{reply.inspect}" unless reply.empty?
  # A single reply is returned as a scalar, not an array
  replies.size == 1 ? replies.first : replies
end
return_handler(oparam) click to toggle source

the handler is a lambda returning a pair: a parsed value and the rest of the input

# File lib/lignite/system_commands.rb, line 102
def return_handler(oparam)
  case oparam["type"]
  when "U8" then ->(i) { [unpack_u8(i[0, 1]), i[1..-1]] }
  when "U16" then ->(i) { [unpack_u16(i[0, 2]), i[2..-1]] }
  when "U32" then ->(i) { [unpack_u32(i[0, 4]), i[4..-1]] }
  when "BYTES" then ->(i) { [i, ""] }
  else
    raise
  end
end
system_command_with_reply(instr_bytes) click to toggle source
# File lib/lignite/system_commands.rb, line 113
def system_command_with_reply(instr_bytes)
  cmd = Message.system_command_with_reply(instr_bytes)
  @conn.send(cmd.bytes)

  reply = Message.reply_from_bytes(@conn.receive)
  assert_match(reply.msgid, cmd.msgid, "Reply id")
  assert_match(reply.command, unpack_u8(instr_bytes[0]), "Command num")
  raise VMError, format("Error: %u", reply.status) if reply.error?

  reply.data
end