#!/usr/bin/python3
#disassembler for DCPU 16 1.1
#Andrew Teesdale, Jr. 12/06/2023

import sys

# bbbbbbaaaaaaoooo

#def get_opcode(instr):

instrs = \
[i.split(":")[1].split(" -")[0].lstrip()[:3] for i in """0x0: non-basic instruction - see below
0x1: SET a, b - sets a to b
0x2: ADD a, b - sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise
0x3: SUB a, b - sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise
0x4: MUL a, b - sets a to a*b, sets O to ((a*b)>>16)&0xffff
0x5: DIV a, b - sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead.
0x6: MOD a, b - sets a to a%b. if b==0, sets a to 0 instead.
0x7: SHL a, b - sets a to a<<b, sets O to ((a<<b)>>16)&0xffff
0x8: SHR a, b - sets a to a>>b, sets O to ((a<<16)>>b)&0xffff
0x9: AND a, b - sets a to a&b
0xa: BOR a, b - sets a to a|b
0xb: XOR a, b - sets a to a^b
0xc: IFE a, b - performs next instruction only if a==b
0xd: IFN a, b - performs next instruction only if a!=b
0xe: IFG a, b - performs next instruction only if a>b
0xf: IFB a, b - performs next instruction only if (a&b)!=0""".split("\n")]

values = ['A', 'B', 'C', 'X', 'Y', 'Z', 'I', 'J', #0x00-0x07      # register
          '[A]', '[B]', '[C]', '[X]', '[Y]', '[Z]', '[I]', '[J]', # [register]
          '[{w} + A]', '[{w} + B]', '[{w} + C]', '[{w} + X]',     # [next word + register]
          '[{w} + Y]', '[{w} + Z]', '[{w} + I]', '[{w} + J]', 
          'POP',
          'PEEK',
          'PUSH',
          'SP',
          'PC',
          'O',
          '[{w}]',
          '{w}',
          'literal']
"""
0x00-0x07: register (A, B, C, X, Y, Z, I or J, in that order)
0x08-0x0f: [register]
0x10-0x17: [next word + register]
   0x18: POP / [SP++]
   0x19: PEEK / [SP]
   0x1a: PUSH / [--SP]
   0x1b: SP
   0x1c: PC
   0x1d: O
   0x1e: [next word]
   0x1f: next word (literal)
0x20-0x3f: literal value 0x00-0x1f (literal)"""

if len(sys.argv) != 2:
   sys.stderr.write("err: not enough arguments\n")
   sys.exit(1)
   
code  = list(reversed([int(x, 16) for x in sys.argv[1].split(" ")]))

def val(v):
   if v>=0x20 and v<=0x3f:
      return v-0x20
   else:
      return values[v]
      
      
count = 0 # count of words consumed
oc    = 0 # previous count of words consumed last iteration

# Loop while there are still words left to process
while len(code) > 0:
   oc     = count
   instr  = code.pop()
   count += 1
   b_val  = ((instr & (0b111111 << 10)) >> 10)
   a_val  = ((instr & (0b111111 <<  4)) >> 4)
   opcode =   instr &  0b001111
   nice   = f"{instrs[opcode]} {val(a_val)}, {val(b_val)}"
   
   print(oc, end = " ")
   print(nice, end="")
   while "{w}" in nice:
      nice = nice.replace("{w}", "0x{0:04X}".format(code.pop()), 1)
      count += 1
      
   print("\t\t"+nice)

