~charles/misc

77b03ecdc660f846ad528d5b624fb3908c0114ab — Charles Daniels 1 year, 4 months ago 2e87d45
check in pickle8
M README.md => README.md +9 -0
@@ 76,3 76,12 @@ frequency to be useful.

Consequentially, the project has been abandoned, as it would not seem to be a
fruitful line of research.

### pickle8

[go to project](./pickle8)

My attempt as a freshmen to write a
[CHIP-8](https://en.wikipedia.org/wiki/CHIP-8) emulator in Python. It was never
finished, as I got bored with the project. The name "pickle8" was a reference
to an inside joke.

A pickle8/ByteUtil.py => pickle8/ByteUtil.py +16 -0
@@ 0,0 1,16 @@

def fromInt(val, length):
  # creates length-bit-long binary string which evaluates to val
  val = int(val)
  bits = bin(val)[2:]
  while len(bits) < length:
    bits = '0' + bits

  assert int(bits, 2) == val
  assert len(bits) == length
  return bits


def fromHex(val, length):
  val = int(val, 16)
  return fromInt(val, length)

A pickle8/CPU.py => pickle8/CPU.py +147 -0
@@ 0,0 1,147 @@
import logging
import Memory
import ByteUtil


class Processor:
  __initial_PC = 513

  def __init__(this):
    this.__registers = {}
    for c in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']:
      this.__registers[c] = Memory.Byte()
    this.__registers['I'] = 0
    this.__registers['delay'] = Memory.Byte()
    this.__registers['sound'] = Memory.Byte()
    this.__registers['PC'] = this.__initial_PC
    this.__registers['SP'] = Memory.Byte()
    this.__registers['VF'] = Memory.Byte()

    this.memory = Memory.MainMemory()

  def getreg(this, reg):
    reg = this.__registers[reg]
    if type(reg) is Memory.Byte:
      return reg.get(int)
    else:
      return reg

  def setreg(this, reg, val):
    if reg in ['PC', 'I']:
      if val < 0 or val > 65535:
        raise ValueError
      this.__registers[reg] = val
    else:
      this.__registers[reg].set(val)

  def dump(this, target):
    with open(target + '.memory', 'w') as f:
      f.write(this.memory.dump())
    with open(target + '.registers', 'w') as f:
      for key in this.__registers:
        f.write("{}:{}\n".format(key, this.getreg(key)))

  def load(this, target):
    with open(target + '.memory', 'r') as f:
      lines = ''
      for line in f:
        lines += line + '\n'
      this.memory.load(lines)
    with open(target + '.registers', 'r') as f:
      for line in f:
        if len(line) > 0 and ':' in line:
          this.setreg(line.split(':')[0], int(line.split(':')[1]))

  def tick(this):
    PC = this.getreg('PC')
    nextaddr = PC + 2
    opcode = [this.memory.get(PC).getl(int),
              this.memory.get(PC).geth(int),
              this.memory.get(PC + 1).getl(int),
              this.memory.get(PC + 1).geth(int)]
    opcode_raw = opcode

    if PC >= this.memory.size() - 2:
      logging.info("PC exceeded address space, halting")
      return False

    # http://devernay.free.fr/hacks/chip8/C8TECH10.HTM

    opcode = ''.join([ByteUtil.fromInt(x, 4) for x in opcode])
    assert len(opcode) == 16

    logging.debug("PC={}; opcode = {} = {} {} {} {}"
                  .format(PC, opcode_raw, opcode[0:4], opcode[4:8],
                          opcode[8:12], opcode[12:16]))

    instruction = int(opcode[0:4], 2)
    register = hex(int(opcode[4:8], 2))[2:]  # assuming register is specified
    # at [4:8] (nibble 2)
    # 2nd register at [8:12] (nibble 3)
    register2 = hex(int(opcode[8:12], 2))[2:]
    nibble2 = int(opcode[8:16], 2)  # int value of the last 2 nibbles
    nibble3 = int(opcode[4:16], 2)  # int value of the last 3 nibbles

    if instruction == 6:
      # set
      logging.debug("set {} {}".format(register, nibble2))
      this.setreg(register, nibble2)

    elif opcode[8:16] == '11100000':  # 14 00
      # cls
      logging.debug("cls")
      addr = 3840
      while addr < this.memory.size():
        this.memory.set(addr, 0)
        addr += 1

    elif opcode[8:16] == '11101110':  # 14 14
      # ret
      logging.debug('ret')
      logging.warning("call to ret (0xe), which is not implemented")

    elif instruction == 1:
      # JP
      logging.debug("jp {}".format(nibble3))
      nextaddr = nibble3

    elif instruction == 2:
      # call
      logging.debug("call {}".format(nibble3))
      logging.warning("call to call (0x02), which is not implemented")

    elif instruction == 3:
      # se
      logging.debug("se {} {}".format(register, nibble2))
      if this.getreg(register) == nibble2:
        nextaddr += 2
        logging.debug("because register {} is equal to {}, jumping ahead 2"
                      .format(register, nibble2))

    elif instruction == 4:
      # sne
      logging.debug("sne {} {}".format(register, nibble2))
      if this.getreg(register) != nibble2:
        nextaddr += 2
        logging.debug("because register {} != {}, jumping ahead 2"
                      .format(register, nibble2))

    elif instruction == 5:
      # se (register-register)
      logging.debug("se {} {}".format(register, register2))
      if this.getreg(register) == this.getreg(register2):
        nextaddr += 2
        logging.debug("because register {} == register {}, jumping ahead 2"
                      .format(register, register2))

    elif instruction == 7:
      # add
      logging.debug("add {} {}".format(register, nibble2))
      existing = this.getreg(register)
      this.setreg(register, existing + nibble2)

    else:
      nextaddr -= 1

    this.setreg('PC', nextaddr)
    return True

A pickle8/CPU_test.py => pickle8/CPU_test.py +30 -0
@@ 0,0 1,30 @@
import unittest
import Memory
import CPU
import random


class ProcessorTest(unittest.TestCase):

  def test_registers(this):
    for run_n in range(10000):
      p = CPU.Processor()
      # test 16 bit registers
      test_val = random.randint(-1000, 655350)
      if test_val < 0 or test_val > 65535:
        with this.assertRaises(ValueError):
          p.setreg('I', test_val)
        with this.assertRaises(ValueError):
          p.setreg('PC', test_val)
      else:
        p.setreg('I', test_val)
        p.setreg('PC', test_val)
        this.assertTrue(p.getreg('I') == test_val)
        this.assertTrue(p.getreg('PC') == test_val)

      # test 8 bit registers
      for reg in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
                  'c', 'd', 'e', 'f', 'delay', 'sound', 'SP', 'VF']:
        test_val = random.randint(0, 255)
        p.setreg(reg, test_val)
        this.assertTrue(p.getreg(reg) == test_val)

A pickle8/Memory.py => pickle8/Memory.py +246 -0
@@ 0,0 1,246 @@
import binascii
import logging
import sys
import pickle


class Byte:
  __default_format = int
  __bin_format = ["bin", "binary", "2", 2]
  __hex_format = ["hex", "hexadecimal", "16", 16]
  __int_format = ["int", "integer", int]
  __oct_format = ["oct", "octal", "8", 8]
  __list_format = ["list", list]

  def __repr__(this):
    return this.get(2)

  def __init__(this, val=0):
    this.__contents = [False for x in range(8)]
    this.set(val)

  def __format_bits(this, fmt, bits):
    # bits is a list like __contents
    assert type(bits) is list

    if fmt in this.__bin_format:
      return ''.join(['1' if x else '0' for x in bits])

    if fmt in this.__hex_format:
      s = hex(this.__format_bits(int, bits))
      if len(s) < 4:
        s = "0x0" + s[2:]
      return s

    if fmt in this.__oct_format:
      return oct(this.__format_bits(int, bits))

    if fmt in this.__int_format:
      return int(this.__format_bits(2, bits), 2)

    if fmt in this.__list_format:
      return bits

  def __to_bits(this, val, minlength=8, maxlength=8):
    # generate a valid list-of-bools from the value
    ret = []
    if type(val) is int:
      if val < 0:
        raise ValueError
      for c in bin(val)[2:]:
        if c == '1':
          ret.append(True)
        else:
          ret.append(False)

    elif type(val) is str:
      if val[0:2] == "0b":
        ret = this.__to_bits(int(val[2:], 2), minlength, maxlength)
      elif val[0:2] == "0o":
        ret = this.__to_bits(int(val[2:], 8), minlength, maxlength)
      elif val[0:2] == "0x":
        ret = this.__to_bits(int(val[2:], 16), minlength, maxlength)
      else:
        # assume we just have 1s and 0s
        ret = this.__to_bits(int(val, 2), minlength, maxlength)

    else:
      # give up, if it's not valid it will fail validation
      ret = val

    while len(ret) < minlength:
      ret = [False] + ret

    if len(ret) > maxlength:
      raise ValueError

    for elem in ret:
      if elem not in [True, False]:
        raise ValueError

    return ret

  def get(this, fmt=__default_format):
    return this.__format_bits(fmt, this.__contents)

  # get bit by index
  def getb(this, idx, fmt=__default_format):
    try:
      assert idx >= 0
      assert idx < 8
    except Exception:
      logging.warning("cannot get index '{}' from 1-byte block".format(idx))
      raise IndexError

    bit = int(this.get(2)[idx])
    return this.__format_bits(fmt, [bit])

  # get upper nibble
  def getl(this, fmt=__default_format):
    return this.__format_bits(fmt, this.__contents[4:])

  def geth(this, fmt=__default_format):
    return this.__format_bits(fmt, this.__contents[:4])

  def setb(this, val, idx):
    try:
      val = int(val)
      assert val in [0, 1]
      assert idx >= 0
      assert idx < 8
    except Exception:
      logging.warning("cannot set bit at index '{}' to '{}' in 1-byte block"
                      .format(idx, val))
      raise ValueError

    this.__contents[idx] = val

  def setl(this, val):
    this.__to_bits(val, 4, 4)
    low = this.getl(list)
    new_contents = low + this.__to_bits(val, 4, 4)
    assert len(new_contents) == 8
    this.__contents = new_contents

  def seth(this, val):
    val = this.__to_bits(val, 4, 4)
    high = this.geth(list)
    new_contents = this.__to_bits(val, 4, 4) + high
    assert len(new_contents) == 8
    this.__contents = new_contents

  def set(this, val):
    try:
      this.__contents = this.__to_bits(val, 8, 8)
    except Exception as e:
      logging.warning("failed to blit byte '{}'".format(val))
      raise ValueError


class MainMemory:
  __size = 4096

  def __init__(this):
    this.__contents = []
    for i in range(this.__size):
      this.__contents.append(Byte(0))

    this.clear()

  def clear(this):
    for i in range(this.__size):
      this.get(i).set(0)

  def size(this):
    return this.__size

  def get(this, addr):
    if addr < 0 or addr >= this.__size:
      logging.warning("attempt to access out of bounds address '{}'"
                      .format(addr))
      raise IndexError

    return this.__contents[addr]

  def set(this, addr, val):
    if addr < 0 or addr >= this.__size:
      logging.warning("attempt to access out of bounds address '{}'"
                      .format(addr))
      raise IndexError

    this.__contents[addr].set(val)

  def getRange(this, lower, upper):
    upper += 1  # make the upper end of the range inclusive
    if lower < 0 or lower > upper:
      logging.warning("memory range lower bound {} is out of bounds"
                      .format(lower))
      raise IndexError
    if upper < lower or upper >= this.size():
      logging.warning("memory range upper bound {} is out of bounds"
                      .format(upper))
      raise IndexError

    ret = []
    for addr in range(lower, upper):
      ret.append(this.get(addr))

    return ret

  def prettyPrint(this, lower=0, upper=__size-1):
    print(this.prettyFormat(lower, upper))

  def prettyFormat(this, lower=0, upper=__size-1):
    s = ''
    if lower < 0 or lower > upper:
      logging.warning("range lower bound {} is out of bounds"
                      .format(lower))
      raise IndexError
    if upper < lower or upper >= this.size():
      logging.warning("range upper bound {} is out of bounds"
                      .format(upper))
      raise IndexError

    addr = lower

    while addr < upper:
      left = hex(addr)
      if len(left) < 4:
        left = "0x0" + left[2:]
      s += (left + " | ")
      for col in range(4):
        if addr >= this.size():
          sys.stdout.write("\n")
          return
        s += (this.get(addr).get("hex") + " ")
        addr += 1
      s += " "
      for col in range(4):
        if addr >= this.size():
          s += "\n"
          return
        s += (this.get(addr).get("hex") + " ")
        addr += 1
      s += "\n"

    return s

  def dump(this):
    s = ''
    for addr in range(0, this.size()):
      if this.get(addr).get(int) != 0:
        s += '{}:{}'.format(addr, this.get(addr).get(int))
        s += "\n"

    return s

  def load(this, source):
    for line in source.split('\n'):
      if len(line.split(':')) != 2:
        continue
      try:
        addr = int(line.split(':')[0])
        val = int(line.split(':')[1])
        this.set(addr, val)
      except Exception:
        logging.warning("failed to load line '{}'".format(line))

A pickle8/Memory_test.py => pickle8/Memory_test.py +128 -0
@@ 0,0 1,128 @@
import unittest
import Memory
import random


class ByteTest(unittest.TestCase):

  def test_initilization(this):
    for run_n in range(10000):
      test_val = random.randint(0, 255)
      b = Memory.Byte(test_val)
      this.assertTrue(b.get(int) == test_val)
      b = Memory.Byte()
      this.assertTrue(b.get(int) == 0)

  def test_getb(this):
    for run_n in range(10000):
      test_val = random.randint(136, 255)
      expected = bin(test_val)[2:]
      b = Memory.Byte(test_val)
      this.assertTrue(b.getb(0, int) == int(expected[0]))
      this.assertTrue(b.getb(1, int) == int(expected[1]))
      this.assertTrue(b.getb(2, int) == int(expected[2]))
      this.assertTrue(b.getb(3, int) == int(expected[3]))
      this.assertTrue(b.getb(4, int) == int(expected[4]))
      this.assertTrue(b.getb(5, int) == int(expected[5]))
      this.assertTrue(b.getb(6, int) == int(expected[6]))
      this.assertTrue(b.getb(7, int) == int(expected[7]))

  def test_nibble_access(this):
    for run_n in range(10000):
      test_h = bin(random.randint(8, 15))[2:]
      test_l = bin(random.randint(8, 15))[2:]
      b = Memory.Byte(test_h + test_l)
      high = b.geth(int)
      this.assertTrue(high == int(test_h, 2))

  def test_seth(this):
    for run_n in range(0, 10000):
      test_val = random.randint(-10, 20)
      b = Memory.Byte()

      if (test_val) < 0 or (test_val > 15):
        with this.assertRaises(ValueError):
          b.seth(test_val)
      else:
        b.seth(test_val)
        this.assertTrue(b.geth(int) == test_val)

  def test_setl(this):
    for run_n in range(0, 10000):
      test_val = random.randint(-10, 20)
      b = Memory.Byte()

      if (test_val) < 0 or (test_val > 15):
        with this.assertRaises(ValueError):
          b.setl(test_val)
      else:
        b.setl(test_val)
        this.assertTrue(b.getl(int) == test_val)

  def test_setb(this):
    for run_n in range(0, 10000):
      b = Memory.Byte()
      test_addr = random.randint(0, 7)
      test_val = random.randint(0, 1)
      this.assertTrue(b.get() == 0)
      b.setb(test_val, test_addr)
      this.assertTrue(b.getb(test_addr, int) == test_val)

  def test_edges(this):
    b = Memory.Byte()
    b.set(0)
    this.assertTrue(b.get(int) == 0)

    b.set(255)
    this.assertTrue(b.get(int) == 255)
    del(b)


class MainMemory_test(unittest.TestCase):

  def test_initilization(this):
    m = Memory.MainMemory()
    for addr in range(m.size()):
      this.assertTrue(m.get(addr).get(int) == 0)
    del(m)

  def test_get_set(this):
    for run_n in range(0, 10):
      m = Memory.MainMemory()
      success = True
      for i in range(m.size()):
        test_val = random.randint(0, 255)
        m.set(i, test_val)
        if test_val != m.get(i).get(int):
          success = False

      this.assertTrue(success)
      m.clear()

  def test_dump_load(this):
    for run_n in range(0, 10):
      m1 = Memory.MainMemory()
      for i in range(m1.size()):
        test_val = random.randint(0, 255)
        m1.set(i, test_val)
      s = m1.dump()
      m2 = Memory.MainMemory()
      m2.load(s)
      for i in range(m1.size()):
        this.assertTrue(m1.get(i).get(int) == m2.get(i).get(int))
      m1.clear()
      m2.clear()

  def test_get_range(this):
    for run_n in range(0, 10):
      m = Memory.MainMemory()
      val1 = random.randint(0, 255)
      val2 = random.randint(0, 255)
      addr = random.randint(0, 4094)
      m.set(addr, val1)
      m.set(addr+1, val2)
      r = m.getRange(addr, addr+1)
      this.assertTrue(len(r) == 2)
      this.assertTrue(r[0].get(int) == val1)
      this.assertTrue(r[1].get(int) == val2)
      m.clear()

A pickle8/assemble.py => pickle8/assemble.py +240 -0
@@ 0,0 1,240 @@
#!/usr/bin/env python3
########10########20########30## DOCUMENTATION #50########60########70########80
#
#  OVERVIEW
#  ========
#  This applications in an assembler which given a valid .asm file, will omit
#  pickle-8 opcodes. Specifically, when a program is assembled successfully, a
#  pickle-8 .memory and .register file representing the desired initial CPU
#  state will be written out.
#
#  SYNTAX
#  ======
#  The assembly files read by this application utilize the following syntax
#
#    * all text on a line after a literal `//` is ignored (including the //)
#    * all constants are specified as integer values (eg. ``25``)
#    * all registers begin with ``r_`` (eg. ``r_a``)
#    * opcodes and their arguments are space delimited; the opcode always comes
#      first. Usually, instructions follow the format `
#      `opcode [arg1] [arg2] ...``. 
#
#  USAGE
#  =====
#
#   See ./assemble.py -h
#
########10########20########30##### LICENSE ####50########60########70########80
#  Copyright (c) 2016, Charles Daniels
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are met:
#
#  1. Redistributions of source code must retain the above copyright notice,
#     this list of conditions and the following disclaimer.
#
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
#  3. Neither the name of the copyright holder nor the names of its
#     contributors may be used to endorse or promote products derived from
#     this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#  POSSIBILITY OF SUCH DAMAGE.
#
########10########20########30########40########50########60########70########80

import logging
import sys
import os
import re
import ByteUtil
import argparse
logging.basicConfig(level=logging.DEBUG)

parser = argparse.ArgumentParser(description='pickle-8 assembler')
parser.add_argument('--start', '-s',
                    help='Starting address for code in main memory',
                    default=512, type=int)
parser.add_argument("--input", "-i",
                    help="Absolute or relative path to input file. Use `-` for stdin.",
                    required=True)
parser.add_argument("--output", "-o", required=True,
                    help="Absolute or relative path to output basename")
args = vars(parser.parse_args())

logging.debug("starting memory address is {}".format(args['start']))
logging.debug("input is: {}".format(args['input']))
logging.debug("output basepath is {}".format(args['output']))

general_registers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
                     'c', 'd', 'e', 'f']

# note that we match all integers as constants - this means the assembler will
# allow you to try to compile with constants that are too large for their
# underlying register/data structure.
constant_match = re.compile("[0-9]+")
register_match = re.compile("r_[1-9a-f]")
opcode_match = ["set", "cls", "ret", "jp", "call", "se", "sne", "add"]

input_file = None
if args['input'] == '-':
  input_file = sys.stdin
elif os.path.isfile(args['input']):
  input_file = open(args['input'], 'r')
else:
  logging.error("input file does not exist or is not a file")
  exit(1)



addr = args["start"]

for line in input_file:
  line = re.sub('//.*', '', line)
  sl = line.split()
  constants = []
  registers = []

  # match tokens
  for token in sl:
    # match constants
    if constant_match.match(token):
      logging.debug("constant token: {}".format(token))
      constants.append(int(token))

    # match registers
    elif register_match.match(token):
      logging.debug("register token: {}".format(token))
      registers.append(token.replace("r_", ""))

    # match opcodes
    elif token in opcode_match:
      logging.debug("opcode token: {}".format(token))

    # show a warning if we don't know whats happening
    else:
      logging.warning("unmatched token: {}".format(token))

  instruction = sl[0]

  # we should have as many tokens as elements in the line (less the opcode)
  if len(sl) != len(registers) + len(constants) + 1:
    logging.error("failed to process line '{}', one or more unmatched tokens"
                  .format(line))
    exit(1)

  # for convenience, we split things into 4 nibbles and keep them as strings,
  # mostly so we can use python's nice subscription syntax  
  opcode = ['0000', '0000', '0000', '0000']  # 2 bytes
  if instruction == 'set':
    opcode[0] = '0110'  # 6
    opcode[1] = ByteUtil.fromHex(registers[0], 4)

    val = ByteUtil.fromInt(sl[2], 8)
    right = val[:4]
    left = val[4:]
    opcode[3] = left
    opcode[2] = right

  elif instruction == 'cls':
    # CLS
    # 00E0 = 0 0 14 0 
    # clear the display
    opcode = ['0000', '0000', '1110', '0000']

  elif instruction== 'ret':
    # RET
    # 00EE = 0 0 14 14 
    # Return from a subroutine.
    # 
    # PC is set to the address at the top of the stack, then SP is reduced by 1

    opcode = ['0000', '0000', '1110', '1110']

  elif instruction == 'jp':
    # JP
    # 1nnn = 1 n1 n2 n3
    # Jump unconditionally to the address nnn (e nibbles)

    # target address
    tgt = ByteUtil.fromInt(sl[1], 12)  
    opcode[0] = '0001'

    opcode[1] = tgt[0:4]  # n1
    opcode[2] = tgt[4:8]  # n2
    opcode[3] = tgt[8:]   # n3

  elif instruction == 'call':
    # CALL
    # 2nnn = 2 n1 n2 n3
    # Call subroutine at nnn
    # 
    # SP incremented, current PC put on top of the stack, PC is set to nnn
    tgt = ByteUtil.fromInt(sl[1], 12)
    opcode[0] = '0010'

    opcode[1] = tgt[0:4] # n1
    opcode[2] = tgt[4:8] # n2
    opcode[3] = tgt[8:]  # n3

  elif instruction == 'se':
    # SE
    # 3xkk = 3 x k1 k2
    # Skips the next instruction if register x equals the byte kk
    opcode[0] = '0011'  # 3
    register = registers[0]

    if len(registers) > 1:
      # case where we are comparing registers
      opcode[0] = '0101'  # 5
      opcode[1] = ByteUtil.fromHex(registers[0], 4)
      opcode[2] = ByteUtil.fromHex(registers[1], 4)
    else:
      opcode[1] = ByteUtil.fromHex(registers[0], 4)
      val = ByteUtil.fromInt(sl[2], 8)
      opcode[2] = val[:4]
      opcode[3] = val[4:]

  elif instruction == 'sne':
    opcode[0] = '0100'  # 4
    register = registers[0]

    opcode[1] = ByteUtil.fromHex(registers[0], 4)
    val = ByteUtil.fromInt(sl[2], 8)
    opcode[2] = val[:4]
    opcode[3] = val[4:]

  elif instruction == 'add':
    opcode[0] = ByteUtil.fromInt(7, 4)
    opcode[1] = ByteUtil.fromHex(registers[0], 4)
    val = ByteUtil.fromInt(constants[0], 8)
    opcode[2] = val[:4]
    opcode[3] = val[4:]

  logging.debug("{} -> i: {} c:{} r: {} -> {}"
                .format(line.replace('\n', ''), instruction, constants,
                        registers, opcode))
  with open(args["output"] + ".memory", 'a') as o:
    lower = int(opcode[1] + opcode[0], 2)
    upper = int(opcode[3] + opcode[2], 2)
    line1 = "{}:{}\n".format(addr, lower)
    line2 = "{}:{}\n".format(addr + 1, upper)
    o.write(line1)
    o.write(line2)
  addr += 2

input_file.close()
logging.info("assembly finished")

A pickle8/pickle8.py => pickle8/pickle8.py +22 -0
@@ 0,0 1,22 @@
import Memory
import logging
import random
import CPU

logging.basicConfig(filename='pickle8.log', level=logging.DEBUG)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(levelname)-s:%(name)-s:%(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)

logging.info("loading CPU state from file...")
p = CPU.Processor()
p.load('cpustate')
logging.debug("memory state: \n{}".format(p.memory.prettyFormat()))
logging.info("executing program code...")
while p.tick():
  pass
p.dump('final')
logging.info("simulation complete.")
logging.debug("memory state: \n{}".format(p.memory.prettyFormat()))