1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-11-24 15:32:27 +00:00

The evaluator doesn't add much and keeps falling behind; remove it.

This commit is contained in:
Chris Pressey 2018-02-05 13:17:23 +00:00
parent 20c824743e
commit 15072eff52
7 changed files with 7 additions and 714 deletions

View File

@ -8,6 +8,7 @@ History of SixtyPical
* Initialized `byte table` values need not have all 256 bytes initialized.
* Constraints for `vector` type come immediately after the type, not the variable.
* `vector table` storage, and ability to copy vectors in and out of same.
* Removed the evaluator. The reference implementation only analyzes and compiles.
* Fixed bug where index register wasn't required to be initialized before table access.
0.10

View File

@ -21,8 +21,8 @@ based on common machine-language programming idioms, such as
* explicit tail calls
* indirect subroutine calls
The reference implementation can execute, analyze, and compile SixtyPical
programs to 6502 machine code.
The reference implementation can analyze and compile SixtyPical programs to
6502 machine code.
Documentation
-------------
@ -45,8 +45,6 @@ Finish the little demo "game" where you can move a block around the screen with
the joystick (i.e. bring it up to par with the original demo game that was written
for SixtyPical)
### `vector table` type
### `low` and `high` address operators
To turn `word` type into `byte`.

View File

@ -2,7 +2,7 @@
"""Usage: sixtypical [OPTIONS] FILES
Analyzes and/or executes and/or compiles a Sixtypical program.
Analyzes and compiles a Sixtypical program.
"""
from os.path import realpath, dirname, join
@ -19,7 +19,6 @@ import sys
import traceback
from sixtypical.parser import Parser
from sixtypical.evaluator import Evaluator
from sixtypical.analyzer import Analyzer
from sixtypical.emitter import Emitter, Byte, Word
from sixtypical.compiler import Compiler
@ -43,9 +42,6 @@ if __name__ == '__main__':
optparser.add_option("--traceback",
action="store_true",
help="")
optparser.add_option("--execute",
action="store_true",
help="")
(options, args) = optparser.parse_args(sys.argv[1:])
@ -88,7 +84,3 @@ if __name__ == '__main__':
pprint(emitter.accum)
else:
emitter.serialize(fh)
if options.execute:
context = Evaluator().eval_program(program)
print str(context)

View File

@ -2,8 +2,9 @@ SixtyPical
==========
This document describes the SixtyPical programming language version 0.11,
both its execution aspect and its static analysis aspect (even though
these are, technically speaking, separate concepts.)
both its static semantics (the capabilities and limits of the static
analyses it defines) and its runtime semantics (with reference to the
semantics of 6502 machine code.)
This document is nominally normative, but the tests in the `tests` directory
are even more normative.

View File

@ -1,208 +0,0 @@
# encoding: UTF-8
from sixtypical.ast import Program, Routine, Block, Instr
from sixtypical.model import (
ConstantRef, LocationRef, PartRef, IndirectRef,
REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
)
class Context(object):
def __init__(self):
self._store = {}
def __str__(self):
return '\n'.join("%s: %s" % (name, value)
for (name, value) in sorted(self._store.iteritems())
if not isinstance(value, Routine))
def get(self, ref):
if isinstance(ref, ConstantRef):
return ref.value
elif isinstance(ref, LocationRef):
return self._store[ref.name]
elif isinstance(ref, PartRef):
value = self.get(ref.ref)
if ref.height == 0:
return value & 255
elif ref.height == 1:
return (value >> 8) & 255
else:
raise NotImplementedError
else:
raise ValueError(ref)
def set(self, ref, value):
if isinstance(ref, PartRef):
old = self.get(ref.ref)
if ref.height == 0:
value = (old & (255 << 8)) | value
elif ref.height == 1:
value = (value << 8) | (old & 255)
else:
raise NotImplementedError
ref = ref.ref
assert isinstance(ref, LocationRef)
self._store[ref.name] = value
class Evaluator(object):
def eval_program(self, program):
assert isinstance(program, Program)
context = Context()
for ref in (REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C):
context.set(ref, 0)
main = None
for defn in program.defns:
if defn.initial is not None:
context.set(defn.location, defn.initial)
for routine in program.routines:
context.set(routine.location, routine)
if routine.name == 'main':
main = routine
self.eval_routine(main, context)
return context
def eval_routine(self, routine, context):
assert isinstance(routine, Routine)
self.next_routine = routine
while self.next_routine:
routine = self.next_routine
self.next_routine = None
self.eval_block(routine.block, context)
def eval_block(self, block, context):
assert isinstance(block, Block)
for i in block.instrs:
self.eval_instr(i, context)
if self.next_routine:
break
def eval_instr(self, instr, context):
assert isinstance(instr, Instr)
opcode = instr.opcode
dest = instr.dest
src = instr.src
if opcode == 'ld':
result = context.get(src)
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.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now + val + carry
if result > 255:
result &= 255
context.set(FLAG_C, 1)
else:
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.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now - val - carry
if result < 0:
result &= 255
context.set(FLAG_C, 1)
else:
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.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.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.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.set(FLAG_C, 1)
else:
context.set(FLAG_C, 0)
elif opcode == 'and':
result = context.get(dest) & context.get(src)
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.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.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.get(FLAG_C)
context.set(FLAG_C, 1 if val & 128 else 0)
result = ((val << 1) + carry) & 255
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.get(FLAG_C)
context.set(FLAG_C, 1 if val & 1 else 0)
result = (val >> 1) + (carry * 128)
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':
self.eval_routine(context.get(instr.location), context)
elif opcode == 'goto':
self.next_routine = context.get(instr.location)
elif opcode == 'if':
val = context.get(src)
test = (val != 0) if not instr.inverted else (val == 0)
if test:
self.eval_block(instr.block1, context)
elif instr.block2:
self.eval_block(instr.block2, context)
elif opcode == 'repeat':
self.eval_block(instr.block, context)
while context.get(src) == 0:
self.eval_block(instr.block, context)
elif opcode == 'copy':
if isinstance(src, IndirectRef):
raise NotImplementedError("this doesn't actually work")
src = src.ref
if isinstance(dest, IndirectRef):
raise NotImplementedError("this doesn't actually work")
dest = dest.ref
context.set(dest, context.get(src))
# these are trashed; so could be anything really
context.set(REG_A, 0)
context.set(FLAG_Z, 0)
context.set(FLAG_N, 0)
elif opcode == 'with-sei':
self.eval_block(instr.block)
else:
raise NotImplementedError

View File

@ -2,6 +2,5 @@
falderal --substring-error \
tests/SixtyPical\ Syntax.md \
tests/SixtyPical\ Execution.md \
tests/SixtyPical\ Analysis.md \
tests/SixtyPical\ Compilation.md

View File

@ -1,490 +0,0 @@
SixtyPical Execution
====================
This is a test suite, written in [Falderal][] format, for the dynamic
execution behaviour of the Sixtypical language, disgregarding static analysis.
[Falderal]: http://catseye.tc/node/Falderal
-> Functionality "Execute SixtyPical program" is implemented by
-> shell command "bin/sixtypical --execute %(test-body-file)"
-> Tests for functionality "Execute SixtyPical program"
Rudimentary program.
| routine main {
| ld a, 0
| add a, 1
| }
= a: 1
= c: 0
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
Program accesses a memory location.
| byte lives
|
| routine main {
| ld a, 0
| st a, lives
| ld x, lives
| add x, 1
| st x, lives
| }
= a: 0
= c: 0
= lives: 1
= n: 0
= v: 0
= x: 1
= y: 0
= z: 0
Program accesses a memory location with initial value.
| byte lives : 3
|
| routine main {
| ld a, lives
| }
= a: 3
= c: 0
= lives: 3
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
Add honours carry.
| routine main {
| ld a, 255
| st on, c
| add a, 0
| }
= a: 0
= c: 1
= n: 0
= v: 0
= x: 0
= y: 0
= z: 1
| routine main {
| ld a, $ff
| st off, c
| add a, 1
| }
= a: 0
= c: 1
= n: 0
= v: 0
= x: 0
= y: 0
= z: 1
Subtract honours carry.
| routine main {
| ld a, 0
| st on, c
| sub a, 0
| }
= a: 255
= c: 1
= n: 1
= v: 0
= x: 0
= y: 0
= z: 0
| routine main {
| ld a, 0
| st off, c
| sub a, 1
| }
= a: 255
= c: 1
= n: 1
= v: 0
= x: 0
= y: 0
= z: 0
Inc and dec do not honour carry, but do set n and z.
| routine main {
| ld x, 254
| st on, c
| inc x
| }
= a: 0
= c: 1
= n: 1
= v: 0
= x: 255
= y: 0
= z: 0
| routine main {
| ld y, 1
| st on, c
| dec y
| }
= a: 0
= c: 1
= n: 0
= v: 0
= x: 0
= y: 0
= z: 1
Compare affects, but does not use, carry.
| routine main {
| ld a, 1
| st on, c
| cmp a, 1
| }
= a: 1
= c: 0
= n: 0
= v: 0
= x: 0
= y: 0
= z: 1
| routine main {
| ld a, 1
| st off, c
| cmp a, 5
| }
= a: 1
= c: 1
= n: 1
= v: 0
= x: 0
= y: 0
= z: 0
AND.
| routine main {
| ld a, 15
| and a, 18
| }
= a: 2
= c: 0
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
OR.
| routine main {
| ld a, 34
| or a, 18
| }
= a: 50
= c: 0
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
XOR.
| routine main {
| ld a, 34
| xor a, 18
| }
= a: 48
= c: 0
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
Shift left.
| routine main {
| ld a, 129
| st off, c
| shl a
| }
= a: 2
= c: 1
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
| routine main {
| ld a, 0
| st on, c
| shl a
| }
= a: 1
= c: 0
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
Shift right.
| routine main {
| ld a, 129
| st off, c
| shr a
| }
= a: 64
= c: 1
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
| routine main {
| ld a, 0
| st on, c
| shr a
| }
= a: 128
= c: 0
= n: 1
= v: 0
= x: 0
= y: 0
= z: 0
Call routine.
| routine up {
| inc x
| inc y
| }
| routine main {
| ld x, 0
| ld y, 1
| call up
| call up
| }
= a: 0
= c: 0
= n: 0
= v: 0
= x: 2
= y: 3
= z: 0
If.
| routine main {
| ld x, 40
| cmp x, 40
| if z {
| ld a, 1
| } else {
| ld a, 8
| }
| ld x, 2
| }
= a: 1
= c: 0
= n: 0
= v: 0
= x: 2
= y: 0
= z: 0
| routine main {
| ld x, 39
| cmp x, 40
| if z {
| ld a, 1
| } else {
| ld a, 8
| }
| ld x, 2
| }
= a: 8
= c: 1
= n: 0
= v: 0
= x: 2
= y: 0
= z: 0
If without else.
| routine main {
| ld x, 39
| cmp x, 40
| if z {
| ld a, 1
| }
| ld x, 2
| }
= a: 0
= c: 1
= n: 0
= v: 0
= x: 2
= y: 0
= z: 0
`not` inverts the sense of the test.
| routine main {
| ld x, 40
| cmp x, 40
| if not z {
| ld a, 1
| } else {
| ld a, 8
| }
| ld x, 2
| }
= a: 8
= c: 0
= n: 0
= v: 0
= x: 2
= y: 0
= z: 0
| routine main {
| ld x, 39
| cmp x, 40
| if not z {
| ld a, 1
| }
| ld x, 2
| }
= a: 1
= c: 1
= n: 0
= v: 0
= x: 2
= y: 0
= z: 0
Repeat loop.
| routine main {
| ld x, 0
| ld y, 15
| repeat {
| inc x
| inc y
| cmp x, 10
| } until z
| }
= a: 0
= c: 0
= n: 0
= v: 0
= x: 10
= y: 25
= z: 1
Copy instruction. Note that the state of a, z, and n are not defined
after copy executes.
| routine main {
| ld x, 5
| copy x, y
| }
= a: 0
= c: 0
= n: 0
= v: 0
= x: 5
= y: 5
= z: 0
Copy word to word.
| word foo : 2000
| word bar
|
| routine main {
| copy foo, bar
| }
= a: 0
= bar: 2000
= c: 0
= foo: 2000
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
Copy literal word to word.
| word bar
|
| routine main {
| copy word 2000, bar
| }
= a: 0
= bar: 2000
= c: 0
= n: 0
= v: 0
= x: 0
= y: 0
= z: 0
Indirect call.
| vector foo outputs x trashes z, n
|
| routine bar outputs x trashes z, n {
| ld x, 200
| }
|
| routine main inputs bar outputs x, foo trashes a, z, n {
| copy bar, foo
| call foo
| }
= a: 0
= c: 0
= n: 1
= v: 0
= x: 200
= y: 0
= z: 0
goto.
| routine bar outputs x trashes z, n {
| ld x, 200
| }
|
| routine main outputs x trashes a, z, n {
| ld y, 200
| goto bar
| }
= a: 0
= c: 0
= n: 1
= v: 0
= x: 200
= y: 200
= z: 0