1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-11-25 23:49:17 +00:00

Improve evaluator internals. Finish test suite for analysis.

This commit is contained in:
Chris Pressey 2015-10-16 19:32:18 +01:00
parent 4d61f439bc
commit 22c58ba556
4 changed files with 198 additions and 52 deletions

View File

@ -58,5 +58,4 @@ if __name__ == '__main__':
if options.execute:
context = eval_program(program)
for key, value in sorted(context.iteritems()):
print "%s: %s" % (key, value)
print str(context)

View File

@ -158,7 +158,7 @@ def analyze_instr(instr, context, routines):
context.assert_writeable(dest, FLAG_Z, FLAG_N)
context.set_initialized(dest, FLAG_Z, FLAG_N)
elif opcode in ('shl', 'shr'):
context.assert_initialized(dest)
context.assert_initialized(dest, FLAG_C)
context.assert_writeable(dest, FLAG_Z, FLAG_N, FLAG_C)
context.set_initialized(dest, FLAG_Z, FLAG_N, FLAG_C)
elif opcode == 'call':

View File

@ -1,31 +1,38 @@
# encoding: UTF-8
from sixtypical.ast import Program, Routine, Block, Instr
from sixtypical.model import ConstantRef, LocationRef
from sixtypical.model import (
ConstantRef, LocationRef,
REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
)
# TODO: should not inherit from dict
class Context(dict):
class Context(object):
def __init__(self):
self._store = {}
def __str__(self):
return '\n'.join("%s: %s" % p for p in sorted(self._store.iteritems()))
def get(self, ref):
if isinstance(ref, ConstantRef):
return ref.value
elif isinstance(ref, LocationRef):
return self[ref.name]
return self._store[ref.name]
else:
raise ValueError(ref)
def set(self, ref, value):
assert isinstance(ref, LocationRef)
self[ref.name] = value
self._store[ref.name] = value
def eval_program(program):
assert isinstance(program, Program)
routines = {r.name: r for r in program.routines}
context = Context({
'a': 0, 'x': 0, 'y': 0,
'c': 0, 'n': 0, 'z': 0, 'v': 0
})
context = Context()
for ref in (REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C):
context.set(ref, 0)
eval_routine(routines['main'], context, routines)
return context
@ -49,90 +56,90 @@ def eval_instr(instr, context, routines):
if opcode == 'ld':
result = context.get(src)
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'st':
context.set(dest, context.get(src))
elif opcode == 'add':
carry = context['c']
carry = context.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now + val + carry
if result > 255:
result &= 255
context['c'] = 1
context.set(FLAG_C, 1)
else:
context['c'] = 0
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_C, 0)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'sub':
carry = context['c']
carry = context.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now - val - carry
if result < 0:
result &= 255
context['c'] = 1
context.set(FLAG_C, 1)
else:
context['c'] = 0
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_C, 0)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'inc':
val = context.get(dest)
result = (val + 1) & 255
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'dec':
val = context.get(dest)
result = (val - 1) & 255
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'cmp':
val = context.get(src)
now = context.get(dest)
result = now - val
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
if result < 0:
result &= 255
context['c'] = 1
context.set(FLAG_C, 1)
else:
context['c'] = 0
context.set(FLAG_C, 0)
elif opcode == 'and':
result = context.get(dest) & context.get(src)
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'or':
result = context.get(dest) | context.get(src)
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'xor':
result = context.get(dest) ^ context.get(src)
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'shl':
val = context.get(dest)
carry = context['c']
context['c'] = 1 if val & 128 else 0
carry = context.get(FLAG_C)
context.set(FLAG_C, 1 if val & 128 else 0)
result = ((val << 1) + carry) & 255
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'shr':
val = context.get(dest)
carry = context['c']
context['c'] = 1 if val & 1 else 0
carry = context.get(FLAG_C)
context.set(FLAG_C, 1 if val & 1 else 0)
result = (val >> 1) + (carry * 128)
context['z'] = 1 if result == 0 else 0
context['n'] = 1 if result & 128 else 0
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'call':
eval_routine(routines[instr.name], context, routines)

View File

@ -349,27 +349,167 @@ Location must be initialized and writeable.
### cmp ###
TODO: write some tests
Some rudimentary tests for cmp.
| routine main
| inputs a
| trashes z, c, n
| {
| cmp a, 4
| }
= ok
| routine main
| inputs a
| trashes z, n
| {
| cmp a, 4
| }
? IllegalWriteError: c
| routine main
| trashes z, c, n
| {
| cmp a, 4
| }
? UninitializedAccessError: a
### and ###
TODO: write some tests
Some rudimentary tests for and.
| routine main
| inputs a
| outputs a, z, n
| {
| and a, 4
| }
= ok
| routine main
| inputs a
| trashes z, n
| {
| and a, 4
| }
? IllegalWriteError: a
| routine main
| trashes z, n
| {
| and a, 4
| }
? UninitializedAccessError: a
### or ###
TODO: write some tests
Writing unit tests on a train. Wow.
| routine main
| inputs a
| outputs a, z, n
| {
| or a, 4
| }
= ok
| routine main
| inputs a
| trashes z, n
| {
| or a, 4
| }
? IllegalWriteError: a
| routine main
| trashes z, n
| {
| or a, 4
| }
? UninitializedAccessError: a
### xor ###
TODO: write some tests
Writing unit tests on a train. Wow.
| routine main
| inputs a
| outputs a, z, n
| {
| xor a, 4
| }
= ok
| routine main
| inputs a
| trashes z, n
| {
| xor a, 4
| }
? IllegalWriteError: a
| routine main
| trashes z, n
| {
| xor a, 4
| }
? UninitializedAccessError: a
### shl ###
TODO: write some tests
Some rudimentary tests for shl.
| routine main
| inputs a, c
| outputs a, c, z, n
| {
| shl a
| }
= ok
| routine main
| inputs a, c
| outputs c, z, n
| {
| shl a
| }
? IllegalWriteError: a
| routine main
| inputs a
| outputs a, c, z, n
| {
| shl a
| }
? UninitializedAccessError: c
### shr ###
TODO: write some tests
Some rudimentary tests for shr.
| routine main
| inputs a, c
| outputs a, c, z, n
| {
| shr a
| }
= ok
| routine main
| inputs a, c
| outputs c, z, n
| {
| shr a
| }
? IllegalWriteError: a
| routine main
| inputs a
| outputs a, c, z, n
| {
| shr a
| }
? UninitializedAccessError: c
### call ###