~crc_/retro-napia

dcc43ce0ab53276e07c24d812a42020ea6ef6001 — crc 20 days ago 3f11f43 master
latest napia.py from arland

FossilOrigin-Name: f9d0b04c4229eda0833eeec378abb3390a969d1e0350e71252e360a3c5f2a009
1 files changed, 433 insertions(+), 568 deletions(-)

M napia.py
M napia.py => napia.py +433 -568
@@ 1,614 1,479 @@
# napia, in Python 3
# Copyright (c) 2022, Arland Childers
import sys, time

import sys

# Configuration

DEBUG = False
BLOCK_PATH = 'napia.blocks'
IMAGE_PATH = 'napia.rom'

# Constrain a value to a signed, 32-bit range, accounting for
# integer overflow. (The napia system *requires* this)

def int_32(val):
    if not -0x80000000 <= val <= 0x7fffffff:
        val = (val + 0x80000000) % 0x100000000 - 0x80000000
    return val

# Reading & Writing Blocks
  if not -0x80000000 <= val <= 0x7fffffff:
    val = (val + 0x80000000) % 0x100000000 - 0x80000000
  return val

# The block storage is a file, containing multiple blocks of 1,024
# cells (32-bit, 4 byte) values. The system allows for reading or
# writing a single block at a time.

def read_block(memory, data):
    buffer = data.pop()
    block = data.pop()
    k=[]
    with open(BLOCK_PATH, 'rb') as f:
        f.seek(4096 * block)
        for i in range(1024):
            a,b,c,d=list(f.read(4))
            k.append(a+(b<<8)+(c<<16)+(d<<24))
    memory[buffer:buffer+1024] = k
    del k
  buffer = data.pop()
  block = data.pop()
  k=[]
  with open(BLOCK_PATH, 'rb') as f:
    f.seek(4096 * block)
    for i in range(1024):
      a,b,c,d=list(f.read(4))
      k.append(a+(b<<8)+(c<<16)+(d<<24))
  memory[buffer:buffer+1024] = k
  del k


def write_block(memory, data):
    buffer = data.pop()
    block = data.pop()
    with open(BLOCK_PATH, 'wb') as f:
        f.seek(4096 * block, 0)
        for i in memory[buffer:buffer + 1024]:
            f.write((i>>0)&0xff)
            f.write((i>>8)&0xff)
            f.write((i>>16)&0xff)
            f.write((i>>24)&0xff)

# Saving & Loading the Image
      f.seek(4096 * block, 0)
      for i in memory[buffer:buffer + 1024]:
        f.write((i>>0)&0xff)
        f.write((i>>8)&0xff)
        f.write((i>>16)&0xff)
        f.write((i>>24)&0xff)

# The image ("napia.rom") holds a copy of the napia memory. On
# start, this is loaded into the actual RAM. The system supports
# saving the current memory to storage in place of the original
# image.

def save_image(memory):
    with open(IMAGE_PATH, 'wb') as f:
        for i in memory:
            cell = bytearray([((i>>0)&0xff), ((i>>8)&0xff), ((i>>16)&0xff),  ((i>>24)&0xff)])
            f.write(cell)
  with open(IMAGE_PATH, 'wb') as f:
    for i in memory:
      f.write(bytearray([((i>>0)&0xff),((i>>8)&0xff), ((i>>16)&0xff),  ((i>>24)&0xff)]))

def read_image(memory):
    with open(IMAGE_PATH, 'rb') as f:
        for i in range(65536):
            a,b,c,d=list(f.read(4))
            memory[i] = a+(b<<8)+(c<<16)+(d<<24)

# I/O

# This implemenation has a modular set of I/O devices.
  with open(IMAGE_PATH, 'rb') as f:
    for i in range(65536):
      a,b,c,d=list(f.read(4))
      memory[i] = a+(b<<8)+(c<<16)+(d<<24)

# The base class for I/O devices is io_base. This sets up a few
# common data structures to allow the device to access aspects
# of the napia instance.

# Notes:
#
# - for memhooks, run is executed first
# - memhook gives full access to the napia instance

#for memhooks, run is executed first
#also. memhook gives full access to the napia instance
class io_base():
    def __init__(self):
        self.register = []
        self.memhook_query=None
        self.needs=[0,0]
        self.setup()
    def setup(self):
        pass
    def run(self, op, data, address):
        return data
    def memhook(self, napia):
        pass

# console (screen) output
  def __init__(self):
    self.register = []
    self.memhook_query=None
    self.needs=[0,0]
    self.setup()
  def setup(self):
    pass
  def run(self, op, data, address):
    return data
  def memhook(self, napia):
    pass

class io_output(io_base):
    def setup(self):
        self.register = [0]
        self.needs = [1,0]
    def run(self,op,data,address):
        print(chr(data.pop()),end='')

# console (keyboard) input

# We read in a string (stored in "grape"; Arland was hungry)
# when writing this code), and return a single character from
# it. When no characters remain, a new string is read.
#
# The input gets a \n appended. Without this, a RetroForth image
# won't receive a separator and thus not process the last word
# in the input.
  def setup(self):
    self.register = [0]
    self.needs = [1,0]
  def run(self,op,data,address):
    print(chr(data.pop()),end='')
    

class io_input(io_base):
    def setup(self):
        self.register = [1]
        self.needs = [0,1]
        self.grape = []
    def run(self,op,data,address):
        if not len(self.grape):
            self.grape = list(input())+['\n']
        l=self.grape[0]
        self.grape = self.grape[1:]
        data.push(ord(l))
  def setup(self):
    self.register = [1]
    self.needs = [0,1]
    self.grape = []
  def run(self,op,data,address):
    if not len(self.grape):
      self.grape = list(input())+['\n']
    l=self.grape[0]
    self.grape = self.grape[1:]
    data.push(ord(l))

# block interactions

# This wraps the block read/write functions into a napia device.

class io_blocks(io_base):
    def setup(self):
        self.register = [2, 3]
        self.needs = [2, 0]
        self.memhook_query = True
        self.action = None
    def run(self,op,data,address):
        self.action = op,data
    def memhook(self, napia):
        if self.action[0] == 2:
            read_block(napia.memory,self.action[1])
        if self.action[0] == 3:
            write_block(napia.memory,self.action[1])

# The image device allows saving an image to disk or resetting
# the system.

  def setup(self):
    self.register = [2, 3]
    self.needs = [2, 0]
    self.memhook_query = True
    self.action = None
  def run(self,op,data,address):
    self.action = op,data
  def memhook(self, napia):
    if self.action[0] == 2:
      read_block(napia.memory,self.action[1])
    if self.action[0] == 3:
      write_block(napia.memory,self.action[1])
  
    
class io_napia_image(io_base):
    def setup(self):
        self.register = [4, 5]
        self.memhook_query=True
        self.op=0
    def run(self,op,data,address):
        self.op=op
    def memhook(self,napia):
        if self.op == 4:
            save_image(napia.memory)
        if self.op == 5:
            napia.reset()
            read_image(napia.memory)

# We also have a state device. This handles exiting when done
# and obtaining the stack depths.
  def setup(self):
    self.register = [4, 5]
    self.memhook_query=True
    self.op=0
  def run(self,op,data,address):
    self.op=op
  def memhook(self,napia):
    if self.op == 4:
      save_image(napia.memory)
    if self.op == 5:
      napia.reset()
      read_image(napia.memory)


class io_napia_state(io_base):
    def setup(self):
        self.register = [6, 7]
    def run(self, op, data, address):
        if op == 6:
            sys.exit()
        if op == 7:
            data.push(data.pointer)
            data.push(address.pointer)
  def setup(self):
    self.register = [6, 7]
    self.needs = [0,2]
  def run(self, op, data, address):
    if op == 6:
      sys.exit()
    if op == 7:
      data.push(data.pointer)
      data.push(address.pointer)


class io_time(io_base):
  def setup(self):
    self.register = [8]
    self.needs = [0,1]
  def run(self, op, data, address):
    data.push(round(time.time()))



# Stacks

# As a dual-stack system, napia implements classes for making
# and using them easily.

class stack(list):
    def __init__(self, size):
        self += [0]*size
        self.pointer = 0
    def push(self, a):
        self.pointer += 1
        self[self.pointer] = int_32(a)
    def pop(self):
        r = self[self.pointer]
        self[self.pointer] = 0
        self.pointer -= 1
        return r
    def reset(self):
        for i in range(len(self)):
            self[i]=0
        self.pointer = 0

# The core class implements the registers and stacks for each
# processor core.
  def __init__(self, size):
    self += [0]*size
    self.pointer = 0
  def push(self, a):
    self.pointer += 1
    self[self.pointer] = int_32(a)
  def pop(self):
    r = self[self.pointer]
    self[self.pointer] = 0
    self.pointer -= 1
    return r
  def reset(self):
    for i in range(len(self)):
      self[i]=0
    self.pointer = 0




class core():
    def __init__(self):
        self.registers = [0]*24
        self.data = stack(32)
        self.address = stack(256)
        self.ip = 0
    def reset(self):
        del self.registers, self.data, self.address, self.ip
        self.registers = [0]*24
        self.data = stack(32)
        self.address = stack(256)
        self.ip = 0

# The napia class ties everything above together, and includes
# functions implementing the actual instructions and code to run
# an image.
  def __init__(self):
    self.registers = [0]*24
    self.data = stack(32)
    self.address = stack(256)
    self.ip = 0
  def reset(self):
    del self.registers, self.data, self.address, self.ip
    self.registers = [0]*24
    self.data = stack(32)
    self.address = stack(256)
    self.ip = 0


class napia():
    def __init__(self, extra_io=[]):
        self.cores = []
        for i in range(10):
            self.cores.append(core())
        self.active = [0]*8
        self.active[0]=1
        self.current_core = 0
        self.memory = stack(65536)
        self.ivt = [0]*129
        self.interrupts_active=True
        self.io_devices=[io_output(),io_input(), io_blocks(),io_napia_image(),io_napia_state()]+extra_io

    def current(self):
        return self.cores[self.current_core]

    def patch_memory(self, stuff={}):
        for i in stuff:
            l=i
            for o in stuff[i]:
                self.memory[l] = o
                l += 1

    def reset(self):
        id=0
        for i in self.cores:
            i.reset()
            self.active[id]=0
        self.active[0]=1
        self.current_core = 0
        self.current().ip-=1 # compensate_for_the_ip_jump
        self.memory.reset()
        self.ivt = [0]*129
        self.interrupts_active=True
        self.io_devices[1].grape = []

    def push(self,a):
        self.current().data.push(a)

    def pop(self):
        return self.current().data.pop()

    # The stack_check() triggers interrupts on stack over or underflow.

    # t is the number of values consumed
    # g is the number of values returned
    # diff is the relative change to the address stack

    def stack_check(self, t,g, diff):
        if self.interrupts_active:
            p=self.current().data.pointer
            if p<t or p<0:
                self.current().data.reset()
                self.interrupt(1)
                return True
            elif ((p+g)-t)>31 or p>31:
                self.current().data.reset()
                self.interrupt(2)
                return True
            elif self.current().address.pointer+diff<0:
                self.interrupt(3)
                return True
            elif self.current().address.pointer+diff>255:
                self.interrupt(4)
                return True
            else:
                return False
        else:
            return False

    # interrupt runs an interrupt handler. Interrupts are run on
    # core 8, which is dedicated to this task.

    def interrupt(self, i):
        if self.interrupts_active:
            if self.ivt[i]:
                self.interrupts_active=0
                prev = self.current_core
                self.current_core = 8
                self.current().reset()
                self.current().ip=self.ivt[i]
                self.current().address.push(0)
                while self.current().address.pointer:
                    if self.current().ip>65535:
                        raise
                    bundle=self.memory[self.current().ip]
                    self.process_bundle(bundle)
                    self.current().ip += 1
                self.current_core = prev
                self.interrupts_active=1

    # Instruction Set
    
    # napia has 41 instructions. These consist of the 30 from ilo,
    # with an additional 11 adding support for the processor cores
    # and interrupts.
    
    # A listing of the instruction set follows, with stack comments
    # and a brief description of each. After this, each will be
    # implemented as a separate function.
    
    # For dispatching the instructions, we use a lookup table. This
    # is nicer than having a huge if/elif/else block.
    
    '''
      00  00  ..     -    non-op
      01  01  li     -n   push value in following cell to stack
      02  02  du    n-nn  duplicate top stack item
      03  03  dr    n-    discard top stack item
      04  04  sw   ab-ba  swap top two stack items
      05  05  pu    n-    move top stack item to address stack
      06  06  po     -n   move top address stack item to data stack
      07  07  ju    a-    jump to an address
                          modifies the instruction pointer
      08  08  ca    a-    call a function
                          modifies the instruction pointer
      09  09  cc   af-    call a function if the flag is non-zero
                          modifies the instruction pointer
      10  0A  cj   af-    jump to a function if the flag is non-zero
                          modifies the instruction pointer
      11  0B  re     -    return from a call or conditional call
                          modifies the instruction pointer
      12  0C  eq   ab-f   compare two values for equality. a == b
      13  0D  ne   ab-f   compare two values for inequality. a != b
      14  0E  lt   ab-f   compare two values for less than. a < b
      15  0F  gt   ab-f   compare two values for greater than. a > b
      16  10  fe    a-n   fetch a stored value in memory
      17  11  st   na-    store a value into memory
      18  12  ad   ab-c   add two numbers. a + b
      19  13  su   ab-c   subtract two numbers. a - b
      20  14  mu   ab-c   multiply two numbers. a * b
      21  15  di   ab-cd  divide and get remainder. a / b, a % b
      22  16  an   ab-c   bitwise and
      23  17  or   ab-c   bitwise or
      24  18  xo   ab-c   bitwise xor
      25  19  sl   ab-c   shift left. a << b
      26  1A  sr   ab-c   shift right. a >> b
      27  1B  cp  sdn-f   compare two memory regions
      28  1C  cy  sdn-    copy memory
      29  1D  io    n-    perform i/o operation
      30  1E  ic    n-    initialize a core
      31  1F  ac   an-    activate a core, sets core ip to a
      32  20  pc    n-    pause a core
      33  21  sc    n-    resume/start a core
      34  22  rr    n-n   read register n of current core
      35  23  wr   vn-    write value v to regster n of current core
      36  24  mx    a-    run code at address a on solo core
      37  25  sv   an-    set interrupt handler for n to addr. a
      38  26  ti    n-    trigger interrupt n
      39  27  si     -    start handling interrupts
      40  28  hi     -    halt handling interrupts
      
      A condensed summary table:

      Opode  Instruction Names  Data Stack Effects
      =====  =================  ====================================
      00-05  .. li du dr sw pu  -     -n    n-nn   n-    nm-mn  n-
      06-11  po ju ca cc cj re  -n    a-    a-     af-   af-    -
      12-17  eq ne lt gt fe st  nn-f  nn-f  nn-f   nn-f  a-n    na-
      18-23  ad su mu di an or  nn-n  nn-n  nn-nn  nn-n  nn-n   nn-n
      24-29  xo sl sr cp cy io  nn-n  nn-n  nn-n   nnn-  nnn-   n-
      30-35  ic ac pc sc rr wr  n-    an-   n-     n-    n-n    vn-
      36-40  mx sv ti si hi     a-    an-   n-     -      -
      =====  =================  ====================================
    '''
    
    def no(self):#0
        pass
    def li(self):#1
        self.current().ip+=1
        self.push(self.memory[self.current().ip])
    def du(self):#2
        m=self.pop()
        self.push(m)
        self.push(m)
    def dr(self):#3
        self.current().data.pop()
    def sw(self):#4
        a=self.pop()
        b=self.pop()
        self.push(a)
        self.push(b)
    def pu(self):#5
        self.current().address.push(self.pop())
    def po(self):#6
        self.push(self.current().address.pop())
    def ju(self):#7
        self.current().ip = self.pop()-1
    def ca(self):#8
        self.current().address.push(self.current().ip)
        self.current().ip = self.pop()-1
    def cc(self):#9
        a = self.pop()
        b = self.pop()
        if b == 0:
            pass
        else:
            self.current().address.push(self.current().ip)
            self.current().ip = a-1
    def cj(self):#10
        a = self.pop()
        b = self.pop()
        if b == 0:
            pass
        else:
            self.current().ip = a-1
    def re(self):#11
        self.current().ip = self.current().address.pop()
    def eq(self):#12
        a=self.pop()
        b=self.pop()
        self.push(-(b==a))
    def ne(self):#13
        a=self.pop()
        b=self.pop()
        self.push(-(b!=a))
    def lt(self):#14
        a=self.pop()
        b=self.pop()
        self.push(-(b<a))
    def gt(self):#15
        a=self.pop()
        b=self.pop()
        self.push(-(b>a))
    def fe(self):#16
        self.push(self.memory[self.pop()])
    def st(self):#17
        a = self.pop()
        v = self.pop()
        self.memory[a]=v
    def ad(self):#18
        a=self.pop()
        b=self.pop()
        self.push(b+a)
    def su(self):#19
        a=self.pop()
        b=self.pop()
        self.push(b-a)
    def mu(self):#20
        a=self.pop()
        b=self.pop()
        self.push(b*a)
    def di(self):#21
        a=self.pop()
        b=self.pop()
        if a != 0:
            self.push(b%a)
            self.push(b//a)
        else:
            self.interrupt(6)
    def an(self):#22
        a=self.pop()
        b=self.pop()
        self.push(b & a)
    def OR(self):#23
        a=self.pop()
        b=self.pop()
        self.push(b | a)
    def xo(self):#24
        a=self.pop()
        b=self.pop()
        self.push(b ^ a)
    def sl(self):#25
        a=self.pop()
        b=self.pop()
        self.push(b<<a)
    def sr(self):#26
        a=self.pop()
        b=self.pop()
        self.push(b>>a)
    def cp(self):#27
        l=self.pop()
        d=self.pop()
        s=self.pop()
        self.push(-(self.memory[d:d+l]==self.memory[s:s+l]))
    def cy(self):#28
        l=self.pop()
        d=self.pop()
        s=self.pop()
        self.memory[d:d+l]=self.memory[s:s+l]
    def io(self):#29
        op = self.pop()
        for i in self.io_devices:
            if op in i.register:
                if not self.stack_check(i.needs[0],i.needs[1],0):
                    i.run(op,self.current().data,self.current().address)
                    if i.memhook_query:
                        i.memhook(self)
    def ic(self):#30
        a=self.pop()
        self.active[a] = 0
        self.cores[a].reset()
    def ac(self):#31
        a=self.pop()
        self.active[a]=1
        self.cores[a].ip=self.pop()
    def pc(self):#32
        self.active[self.pop()]=0
    def sc(self):#33
        self.active[self.pop()]=1
    def rr(self):#34
        k=self.pop()
        self.push(self.current().registers[k])
    def wr(self):#35
        a = self.pop()
        b = self.pop()
        self.current().registers[a]=b
    def mx(self):#36
        to=self.pop()
        prev=self.current_core
        o=self.current().data
        self.current_core = 9
        self.current().data=o
  def __init__(self, extra_io=[]):
    self.cores = []
    for i in range(8):
      self.cores.append(core())
    self.active = [0]*8
    self.active[0]=1
    self.current_core = 0
    self.interrupt_core = core()
    self.solo_core = core()
    self.memory = stack(65536)
    self.ivt = [0]*129
    self.interrupts_active=True
    self.io_devices=[io_output(),io_input(), io_blocks(),io_napia_image(),io_napia_state(),io_time()]+extra_io
  def current(self):
    if self.current_core>=0:
      return self.cores[self.current_core]
    elif self.current_core==-1:
      return self.interrupt_core
    elif self.current_core==-2:
      return self.solo_core
    raise Exception(self.current_core)
  def patch_memory(self, stuff={}):
    for i in stuff:
      l=i
      for o in stuff[i]:
        self.memory[l] = o
        l += 1
  
  def reset(self):
    id=0
    for i in self.cores:
      i.reset()
      self.active[id]=0
    self.active[0]=1
    self.current_core = 0
    self.current().ip-=1 # compensate_for_the_ip_jump
    self.memory.reset()
    self.ivt = [0]*129
    self.interrupts_active=True
    self.io_devices[1].grape = []
  
  
  def push(self,a):
    self.current().data.push(a)
  def pop(self):
    return self.current().data.pop()
  
  def stack_check(self, t,g, diff):
    if self.interrupts_active:
      p=self.current().data.pointer
      if p<t or p<0:
        self.current().data.reset()
        self.interrupt(1)
        return True
      elif ((p+g)-t)>31 or p>31:
        self.current().data.reset()
        self.interrupt(2)
        return True
      elif self.current().address.pointer+diff<0:
        self.interrupt(3)
        return True
      elif self.current().address.pointer+diff>255:
        self.interrupt(4)
        return True
      else:
        return False
    else:
      return False
        
  
  def interrupt(self, i):
    if self.interrupts_active:
      if self.ivt[i]:
        self.interrupts_active=0
        prev = self.current_core
        self.current_core = -1
        self.current().reset()
        self.current().ip=to
        self.current().ip=self.ivt[i]
        self.current().address.push(0)
        while self.current().address.pointer:
            if self.current().ip>65535:
                raise
            bundle=self.memory[self.current().ip]
            self.process_bundle(bundle)
            self.current().ip += 1
        n=self.current().data
        self.current_core=prev
        self.current().data=n
    def sv(self):#37
        a=self.pop()
        b=self.pop()
        self.ivt[a] = b
    def ti(self):#38
        self.interrupt(self.pop())
    def si(self):#39
        self.interrupts_active=True
    def hi(self):#40
        self.interrupts_active=False

    def process_instruction(self, instr):
        #  nolidudrswpu pojucacccj reeqneltgtfestadsumudianorxoslsrcpcyioicacpcscrrwrmxsvtisihi
        t=[0,0,1,1,2,1, 0,1,1,2,2, 0,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,3,3,1,1,2,1,1,1,2,1,2,1,0,0]
        g=[0,1,2,0,2,0, 1,0,0,0,0, 0,1,1,1,1,1,0,1,1,1,2,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0]
        a=[0,0,0,0,0,1,-1,0,1,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
        ips = [
          self.no, self.li, self.du, self.dr, self.sw, self.pu,
          self.po, self.ju, self.ca, self.cc, self.cj, self.re,
          self.eq, self.ne, self.lt, self.gt, self.fe, self.st,
          self.ad, self.su, self.mu, self.di, self.an, self.OR,
          self.xo, self.sl, self.sr, self.cp, self.cy, self.io,
          self.ic, self.ac, self.pc, self.sc, self.rr, self.wr,
          self.mx, self.sv, self.ti, self.si, self.hi]
        if instr:
            if DEBUG:
                print(f'instr:{str(ips[instr])[20:22]}', end=' ')
                print(f'ip:{self.current().ip}', end=' ')
                print(f'sp:{self.current().data.pointer}/{self.current().address.pointer}', end=' ')
                print(f'tos:{self.current().data[self.current().data.pointer]}', end=' ')
                print(f'core:{self.current_core}')
            if not self.stack_check(t[instr],g[instr],a[instr]):
                ips[instr]()
    
    # Instructions are packed into bundles of four. This will run
    # each instruction in a bundle.
    #
    # It also triggers interrupt 0 after the bundle finishes.

    def process_bundle(self, bundle):
        self.process_instruction((bundle>>0)&0xff)
        self.process_instruction((bundle>>8)&0xff)
        self.process_instruction((bundle>>16)&0xff)
        self.process_instruction((bundle>>24)&0xff)
        self.interrupt(0)
    
    # switch to the next active core

    # perhaps this should detect if all cores are inactive and
    # exit?
    
    def next_core(self):
          if self.current().ip>65535:
            raise
          bundle=self.memory[self.current().ip]
          self.process_bundle(bundle)
          self.current().ip += 1
        self.current_core = prev
        self.interrupts_active=1
  
  def no(self):#0
    pass
  def li(self):#1
    self.current().ip+=1
    self.push(self.memory[self.current().ip])
  def du(self):#2
    m=self.pop()
    self.push(m)
    self.push(m)
  def dr(self):#3
    self.current().data.pop()
  def sw(self):#4
    a=self.pop()
    b=self.pop()
    self.push(a)
    self.push(b)
  def pu(self):#5
    self.current().address.push(self.pop())
  def po(self):#6
    self.push(self.current().address.pop())
  def ju(self):#7
    self.current().ip = self.pop()-1
  def ca(self):#8
    self.current().address.push(self.current().ip)
    self.current().ip = self.pop()-1
  def cc(self):#9
    a = self.pop()
    b = self.pop()
    if b == 0:
      pass
    else:
      self.current().address.push(self.current().ip)
      self.current().ip = a-1
  def cj(self):#10
    a = self.pop()
    b = self.pop()
    if b == 0:
      pass
    else:
      self.current().ip = a-1
  def re(self):#11
    self.current().ip = self.current().address.pop()
  def eq(self):#12
    a=self.pop()
    b=self.pop()
    self.push(-(b==a))
  def ne(self):#13
    a=self.pop()
    b=self.pop()
    self.push(-(b!=a))
  def lt(self):#14
    a=self.pop()
    b=self.pop()
    self.push(-(b<a))
  def gt(self):#15
    a=self.pop()
    b=self.pop()
    self.push(-(b>a))
  def fe(self):#16
    self.push(self.memory[self.pop()])
  def st(self):#17
    a = self.pop()
    v = self.pop()
    self.memory[a]=v
  def ad(self):#18
    a=self.pop()
    b=self.pop()
    self.push(b+a)
  def su(self):#19
    a=self.pop()
    b=self.pop()
    self.push(b-a)
  def mu(self):#20
    a=self.pop()
    b=self.pop()
    self.push(b*a)
  def di(self):#21
    a=self.pop()
    b=self.pop()
    if a != 0:
      self.push(b%a)
      self.push(b//a)
    else:
      self.interrupt(6)
  def an(self):#22
    a=self.pop()
    b=self.pop()
    self.push(b & a)
  def OR(self):#23
    a=self.pop()
    b=self.pop()
    self.push(b | a)
  def xo(self):#24
    a=self.pop()
    b=self.pop()
    self.push(b ^ a)
  def sl(self):#25
    a=self.pop()
    b=self.pop()
    self.push(b<<a)
  def sr(self):#26
    a=self.pop()
    b=self.pop()
    self.push(b>>a)
  def cp(self):#27
    l=self.pop()
    d=self.pop()
    s=self.pop()
    self.push(-(self.memory[d:d+l]==self.memory[s:s+l]))
  def cy(self):#28
    l=self.pop()
    d=self.pop()
    s=self.pop()
    self.memory[d:d+l]=self.memory[s:s+l]
  def io(self):#29
    op = self.pop()
    for i in self.io_devices:
      if op in i.register:
        if not self.stack_check(i.needs[0],i.needs[1],0):
          i.run(op,self.current().data,self.current().address)
          if i.memhook_query:
            i.memhook(self)
  def ic(self):#30
    a=self.pop()
    self.active[a] = 0
    self.cores[a].reset()
  def ac(self):#31
    a=self.pop()
    self.active[a]=1
    self.cores[a].ip=self.pop()
  def pc(self):#32
    self.active[self.pop()]=0
  def sc(self):#33
    self.active[self.pop()]=1
  def rr(self):#34
    k=self.pop()
    self.push(self.current().registers[k])
  def wr(self):#35
    a = self.pop()
    b = self.pop()
    self.current().registers[a]=b
  def mx(self):#36
    to=self.pop()
    prev=self.current_core
    o=self.current().data
    self.current_core = -2
    self.current().data=o
    self.current().reset()
    self.current().ip=to
    self.current().address.push(0)
    while self.current().address.pointer:
      if self.current().ip>65535:
        raise
      bundle=self.memory[self.current().ip]
      self.process_bundle(bundle)
      self.current().ip += 1
    n=self.current().data
    self.current_core=prev
    self.current().data=n
  def sv(self):#37
    a=self.pop()
    b=self.pop()
    self.ivt[a] = b
  def ti(self):#38
    self.interrupt(self.pop())
  def si(self):#39
    self.interrupts_active=True
  def hi(self):#40
    self.interrupts_active=False
  
  
  def process_instruction(self, instr):
    #  nolidudrswpu pojucacccj reeqneltgtfestadsumudianorxoslsrcpcyioicacpcscrrwrmxsvtisihi
    t=[0,0,1,1,2,1, 0,1,1,2,2, 0,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,3,3,1,1,2,1,1,1,2,1,2,1,0,0]
    g=[0,1,2,0,2,0, 1,0,0,0,0, 0,1,1,1,1,1,0,1,1,1,2,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0]
    a=[0,0,0,0,0,1,-1,0,1,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    ips = [
      self.no, self.li, self.du, self.dr, self.sw, self.pu, 
      self.po, self.ju, self.ca, self.cc, self.cj, self.re, 
      self.eq, self.ne, self.lt, self.gt, self.fe, self.st, 
      self.ad, self.su, self.mu, self.di, self.an, self.OR, 
      self.xo, self.sl, self.sr, self.cp, self.cy, self.io,
      self.ic, self.ac, self.pc, self.sc, self.rr, self.wr, 
      self.mx, self.sv, self.ti, self.si, self.hi]
    if instr:
      if DEBUG:
        print(f'  instr:{str(ips[instr])[20:22]} ip:{self.current().ip} sp:{self.current().data.pointer}/{self.current().address.pointer} tos:{self.current().data[self.current().data.pointer]} core:{self.current_core}')
      if not self.stack_check(t[instr],g[instr],a[instr]):
        ips[instr]()
  
  def process_bundle(self, bundle):
    self.process_instruction((bundle>>0)&0xff)
    self.process_instruction((bundle>>8)&0xff)
    self.process_instruction((bundle>>16)&0xff)
    self.process_instruction((bundle>>24)&0xff)
    self.interrupt(0)
  
  def run(self):
    while 1:
      if self.current().ip>65535:
        return
      bundle=self.memory[self.current().ip]
      self.process_bundle(bundle)
      self.current().ip+=1
      self.current_core+=1
      if self.current_core >= len(self.cores):
        self.current_core=0
      while not self.active[self.current_core]:
        self.current_core+=1
        if self.current_core >= 8:
            self.current_core=0
        while not self.active[self.current_core]:
            self.current_core+=1
            if self.current_core >= 8:
                self.current_core=0

    def run(self):
        while 1:
            if self.current().ip>65535:
                return
            bundle=self.memory[self.current().ip]
            self.process_bundle(bundle)
            self.current().ip+=1
            self.next_core()


if __name__ == '__main__':
    this = napia()
    read_image(this.memory)
    this.run()
        if self.current_core >= len(self.cores):
          self.current_core=0
      
  

a=napia()
read_image(a.memory)
a.run()