From 33d5093e5a626aa58f77fd1cfc7eaae25953d3b2 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 13:20:18 +0100 Subject: [PATCH 01/12] Resolve forward references more explicitly. --- src/sixtypical/ast.py | 12 ++++++---- src/sixtypical/parser.py | 50 +++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/sixtypical/ast.py b/src/sixtypical/ast.py index 718636f..f7aad36 100644 --- a/src/sixtypical/ast.py +++ b/src/sixtypical/ast.py @@ -37,14 +37,16 @@ class AST(object): def all_children(self): for attr in self.children_attrs: for child in self.attrs[attr]: + if child is not None: + yield child + for subchild in child.all_children(): + yield subchild + for attr in self.child_attrs: + child = self.attrs[attr] + if child is not None: yield child for subchild in child.all_children(): yield subchild - for attr in self.child_attrs: - child = self.attrs[attr] - yield child - for subchild in child.all_children(): - yield subchild class Program(AST): diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 9cd7311..5077948 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -18,6 +18,14 @@ class SymEntry(object): return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model) +class ForwardReference(object): + def __init__(self, name): + self.name = name + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self.name) + + class ParsingContext(object): def __init__(self): self.symbols = {} # token -> SymEntry @@ -45,7 +53,6 @@ class Parser(object): def __init__(self, context, text, filename): self.context = context self.scanner = Scanner(text, filename) - self.backpatch_instrs = [] def syntax_error(self, msg): self.scanner.syntax_error(msg) @@ -102,21 +109,28 @@ class Parser(object): defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w)) for routine in routines: routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w)) - for instr in self.backpatch_instrs: - if instr.opcode in ('call', 'goto'): - name = instr.location - model = self.lookup(name) - if not isinstance(model.type, (RoutineType, VectorType)): - self.syntax_error('Illegal call of non-executable "%s"' % name) - instr.location = model - if instr.opcode in ('copy',) and isinstance(instr.src, str): - name = instr.src - model = self.lookup(name) - if not isinstance(model.type, (RoutineType, VectorType)): - self.syntax_error('Illegal copy of non-executable "%s"' % name) - instr.src = model - return Program(self.scanner.line_number, defns=defns, routines=routines) + program = Program(self.scanner.line_number, defns=defns, routines=routines) + + for node in program.all_children(): + if isinstance(node, SingleOp): + instr = node + if instr.opcode in ('call', 'goto'): + forward_reference = instr.location + name = forward_reference.name + model = self.lookup(name) + if not isinstance(model.type, (RoutineType, VectorType)): + self.syntax_error('Illegal call of non-executable "%s"' % name) + instr.location = model + if instr.opcode in ('copy',) and isinstance(instr.src, ForwardReference): + forward_reference = instr.src + name = forward_reference.name + model = self.lookup(name) + if not isinstance(model.type, (RoutineType, VectorType)): + self.syntax_error('Illegal copy of non-executable "%s"' % name) + instr.src = model + + return program def typedef(self): self.scanner.expect('typedef') @@ -337,7 +351,7 @@ class Parser(object): if loc is not None: return loc else: - return name + return ForwardReference(name) else: loc = self.lookup(self.scanner.token) self.scanner.scan() @@ -452,8 +466,7 @@ class Parser(object): self.scanner.scan() name = self.scanner.token self.scanner.scan() - instr = SingleOp(self.scanner.line_number, opcode=opcode, location=name, dest=None, src=None) - self.backpatch_instrs.append(instr) + instr = SingleOp(self.scanner.line_number, opcode=opcode, location=ForwardReference(name), dest=None, src=None) return instr elif self.scanner.token in ("copy",): opcode = self.scanner.token @@ -462,7 +475,6 @@ class Parser(object): self.scanner.expect(',') dest = self.indlocexpr() instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src) - self.backpatch_instrs.append(instr) return instr elif self.scanner.consume("with"): self.scanner.expect("interrupts") From 26a2fb448c9698fbe878cf86249850f55d516aa8 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 13:27:18 +0100 Subject: [PATCH 02/12] Generalize forward reference resolution a smidge. --- src/sixtypical/parser.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 5077948..9da1c4f 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -122,13 +122,9 @@ class Parser(object): if not isinstance(model.type, (RoutineType, VectorType)): self.syntax_error('Illegal call of non-executable "%s"' % name) instr.location = model - if instr.opcode in ('copy',) and isinstance(instr.src, ForwardReference): - forward_reference = instr.src - name = forward_reference.name - model = self.lookup(name) - if not isinstance(model.type, (RoutineType, VectorType)): - self.syntax_error('Illegal copy of non-executable "%s"' % name) - instr.src = model + if instr.opcode in ('copy',): + if isinstance(instr.src, ForwardReference): + instr.src = self.lookup(instr.src.name) return program From af0e1b41c747fca83d836cc64cc9bf46fe7222fb Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 13:40:41 +0100 Subject: [PATCH 03/12] Forward-reference resolution becomes increasingly general. --- HISTORY.md | 2 ++ src/sixtypical/analyzer.py | 2 ++ src/sixtypical/parser.py | 13 ++++--------- tests/SixtyPical Analysis.md | 29 +++++++++++++++++++++++++++++ tests/SixtyPical Syntax.md | 25 ------------------------- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7d7b22f..aea6685 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,6 +5,8 @@ History of SixtyPical ---- * `save X, Y, Z { }` now allowed as a shortcut for nested `save`s. +* Trying to call or goto a non-routine-typed symbol is now an + analysis error, not a syntax error. * Split TODO off into own file. * `sixtypical` no longer writes the compiled binary to standard output. The `--output` command-line argument should be given diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 1e69965..98959e1 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -550,6 +550,8 @@ class Analyzer(object): context.invalidate_range(dest) elif opcode == 'call': type = instr.location.type + if not isinstance(type, (RoutineType, VectorType)): + raise TypeMismatchError(instr, instr.location) if isinstance(type, VectorType): type = type.of_type for ref in type.inputs: diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 9da1c4f..84dc753 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -115,16 +115,11 @@ class Parser(object): for node in program.all_children(): if isinstance(node, SingleOp): instr = node + if isinstance(instr.src, ForwardReference): + instr.src = self.lookup(instr.src.name) if instr.opcode in ('call', 'goto'): - forward_reference = instr.location - name = forward_reference.name - model = self.lookup(name) - if not isinstance(model.type, (RoutineType, VectorType)): - self.syntax_error('Illegal call of non-executable "%s"' % name) - instr.location = model - if instr.opcode in ('copy',): - if isinstance(instr.src, ForwardReference): - instr.src = self.lookup(instr.src.name) + if isinstance(instr.location, ForwardReference): + instr.location = self.lookup(instr.location.name) return program diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 0caecc8..2e06530 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -196,6 +196,35 @@ If a routine reads or writes a user-define memory location, it needs to declare | } = ok +### call ### + +You can't call a non-routine. + + | byte up + | + | routine main outputs x, y trashes z, n { + | ld x, 0 + | ld y, 1 + | call up + | } + ? TypeMismatchError: up + + | routine main outputs x, y trashes z, n { + | ld x, 0 + | ld y, 1 + | call x + | } + ? TypeMismatchError: x + +Nor can you goto a non-routine. + + | byte foo + | + | routine main { + | goto foo + | } + ? TypeMismatchError: foo + ### ld ### Can't `ld` from a memory location that isn't initialized. diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index e2ef953..1aa9a9c 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -404,24 +404,6 @@ Can't call routine that hasn't been defined. | } ? SyntaxError -And you can't call a non-routine. - - | byte up - | - | routine main { - | ld x, 0 - | ld y, 1 - | call up - | } - ? SyntaxError - - | routine main { - | ld x, 0 - | ld y, 1 - | call x - | } - ? SyntaxError - But you can call a routine that is yet to be defined, further on. | routine main { @@ -589,13 +571,6 @@ goto. | } ? SyntaxError - | byte foo - | - | routine main { - | goto foo - | } - ? SyntaxError - Buffers and pointers. | buffer[2048] buf From d70092c3738a260bd13a9599ffd98294cf799800 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 13:42:18 +0100 Subject: [PATCH 04/12] Even more general. --- src/sixtypical/parser.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 84dc753..156512b 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -115,11 +115,12 @@ class Parser(object): for node in program.all_children(): if isinstance(node, SingleOp): instr = node + if isinstance(instr.location, ForwardReference): + instr.location = self.lookup(instr.location.name) if isinstance(instr.src, ForwardReference): instr.src = self.lookup(instr.src.name) - if instr.opcode in ('call', 'goto'): - if isinstance(instr.location, ForwardReference): - instr.location = self.lookup(instr.location.name) + if isinstance(instr.dest, ForwardReference): + instr.dest = self.lookup(instr.dest.name) return program From cc98e023c40d5c53431eb9868005fe445ab559e0 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 14:02:40 +0100 Subject: [PATCH 05/12] Simply always produce ForwardReferences in locexpr(forward=True). --- src/sixtypical/parser.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 156512b..2e68e27 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -112,15 +112,19 @@ class Parser(object): program = Program(self.scanner.line_number, defns=defns, routines=routines) + def resolve_fwd_reference(obj, field): + field_value = getattr(obj, field, None) + if isinstance(field_value, ForwardReference): + setattr(obj, field, self.lookup(field_value.name)) + elif isinstance(field_value, IndexedRef): + if isinstance(field_value.ref, ForwardReference): + field_value.ref = self.lookup(field_value.ref.name) + for node in program.all_children(): if isinstance(node, SingleOp): - instr = node - if isinstance(instr.location, ForwardReference): - instr.location = self.lookup(instr.location.name) - if isinstance(instr.src, ForwardReference): - instr.src = self.lookup(instr.src.name) - if isinstance(instr.dest, ForwardReference): - instr.dest = self.lookup(instr.dest.name) + resolve_fwd_reference(node, 'location') + resolve_fwd_reference(node, 'src') + resolve_fwd_reference(node, 'dest') return program @@ -339,11 +343,7 @@ class Parser(object): elif forward: name = self.scanner.token self.scanner.scan() - loc = self.context.fetch(name) - if loc is not None: - return loc - else: - return ForwardReference(name) + return ForwardReference(name) else: loc = self.lookup(self.scanner.token) self.scanner.scan() From 29a5bba85c9f4b8aeceace505f2dd45fc58b7dff Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 14:27:49 +0100 Subject: [PATCH 06/12] Distinct symbol resolution phase (as a method on parser.) --- src/sixtypical/model.py | 14 ---------- src/sixtypical/parser.py | 59 ++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index e2d7757..50d57ad 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -18,20 +18,6 @@ class Type(object): def __hash__(self): return hash(self.name) - def backpatch_constraint_labels(self, resolver): - def resolve(w): - if not isinstance(w, str): - return w - return resolver(w) - if isinstance(self, TableType): - self.of_type.backpatch_constraint_labels(resolver) - elif isinstance(self, VectorType): - self.of_type.backpatch_constraint_labels(resolver) - elif isinstance(self, RoutineType): - self.inputs = set([resolve(w) for w in self.inputs]) - self.outputs = set([resolve(w) for w in self.outputs]) - self.trashes = set([resolve(w) for w in self.trashes]) - TYPE_BIT = Type('bit', max_range=(0, 1)) TYPE_BYTE = Type('byte', max_range=(0, 255)) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 2e68e27..38f9fec 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -74,6 +74,41 @@ class Parser(object): def clear_statics(self): self.context.statics = {} + def resolve_symbols(self, program): + + def backpatch_constraint_labels(type_, resolver): + def resolve(w): + if not isinstance(w, str): + return w + return resolver(w) + if isinstance(type_, TableType): + backpatch_constraint_labels(type_.of_type, resolver) + elif isinstance(type_, VectorType): + backpatch_constraint_labels(type_.of_type, resolver) + elif isinstance(type_, RoutineType): + type_.inputs = set([resolve(w) for w in type_.inputs]) + type_.outputs = set([resolve(w) for w in type_.outputs]) + type_.trashes = set([resolve(w) for w in type_.trashes]) + + for defn in program.defns: + backpatch_constraint_labels(defn.location.type, lambda w: self.lookup(w)) + for routine in program.routines: + backpatch_constraint_labels(routine.location.type, lambda w: self.lookup(w)) + + def resolve_fwd_reference(obj, field): + field_value = getattr(obj, field, None) + if isinstance(field_value, ForwardReference): + setattr(obj, field, self.lookup(field_value.name)) + elif isinstance(field_value, IndexedRef): + if isinstance(field_value.ref, ForwardReference): + field_value.ref = self.lookup(field_value.ref.name) + + for node in program.all_children(): + if isinstance(node, SingleOp): + resolve_fwd_reference(node, 'location') + resolve_fwd_reference(node, 'src') + resolve_fwd_reference(node, 'dest') + # --- grammar productions def program(self): @@ -102,30 +137,8 @@ class Parser(object): routines.append(routine) self.scanner.check_type('EOF') - # now backpatch the executable types. - #for type_name, type_ in self.context.typedefs.items(): - # type_.backpatch_constraint_labels(lambda w: self.lookup(w)) - for defn in defns: - defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w)) - for routine in routines: - routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w)) - program = Program(self.scanner.line_number, defns=defns, routines=routines) - - def resolve_fwd_reference(obj, field): - field_value = getattr(obj, field, None) - if isinstance(field_value, ForwardReference): - setattr(obj, field, self.lookup(field_value.name)) - elif isinstance(field_value, IndexedRef): - if isinstance(field_value.ref, ForwardReference): - field_value.ref = self.lookup(field_value.ref.name) - - for node in program.all_children(): - if isinstance(node, SingleOp): - resolve_fwd_reference(node, 'location') - resolve_fwd_reference(node, 'src') - resolve_fwd_reference(node, 'dest') - + self.resolve_symbols(program) return program def typedef(self): From 07ef1e243a2e3405d770ee8ce2aca6f40fe2679a Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 14:51:16 +0100 Subject: [PATCH 07/12] backpatch_constraint_labels can resolve ForwardReferences. --- src/sixtypical/model.py | 8 ++++---- src/sixtypical/parser.py | 25 ++++++++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index 50d57ad..7204dde 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -27,11 +27,11 @@ TYPE_WORD = Type('word', max_range=(0, 65535)) class RoutineType(Type): """This memory location contains the code for a routine.""" - def __init__(self, inputs=None, outputs=None, trashes=None): + def __init__(self, inputs, outputs, trashes): self.name = 'routine' - self.inputs = inputs or set() - self.outputs = outputs or set() - self.trashes = trashes or set() + self.inputs = inputs + self.outputs = outputs + self.trashes = trashes def __repr__(self): return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % ( diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 38f9fec..2ce127e 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -74,26 +74,29 @@ class Parser(object): def clear_statics(self): self.context.statics = {} - def resolve_symbols(self, program): + # ---- symbol resolution - def backpatch_constraint_labels(type_, resolver): + def resolve_symbols(self, program): + # This could stand to be better unified. + + def backpatch_constraint_labels(type_): def resolve(w): - if not isinstance(w, str): + if not isinstance(w, ForwardReference): return w - return resolver(w) + return self.lookup(w.name) if isinstance(type_, TableType): - backpatch_constraint_labels(type_.of_type, resolver) + backpatch_constraint_labels(type_.of_type) elif isinstance(type_, VectorType): - backpatch_constraint_labels(type_.of_type, resolver) + backpatch_constraint_labels(type_.of_type) elif isinstance(type_, RoutineType): type_.inputs = set([resolve(w) for w in type_.inputs]) type_.outputs = set([resolve(w) for w in type_.outputs]) type_.trashes = set([resolve(w) for w in type_.trashes]) for defn in program.defns: - backpatch_constraint_labels(defn.location.type, lambda w: self.lookup(w)) + backpatch_constraint_labels(defn.location.type) for routine in program.routines: - backpatch_constraint_labels(routine.location.type, lambda w: self.lookup(w)) + backpatch_constraint_labels(routine.location.type) def resolve_fwd_reference(obj, field): field_value = getattr(obj, field, None) @@ -279,7 +282,11 @@ class Parser(object): outputs = set(self.labels()) if self.scanner.consume('trashes'): trashes = set(self.labels()) - return (inputs, outputs, trashes) + return ( + set([ForwardReference(n) for n in inputs]), + set([ForwardReference(n) for n in outputs]), + set([ForwardReference(n) for n in trashes]) + ) def legacy_routine(self): self.scanner.expect('routine') From 684b26dd5c972617a54929cf7dca94389015fdf6 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 15:03:41 +0100 Subject: [PATCH 08/12] locexpr() always returns a ForwardReference if it can't lookup it. --- src/sixtypical/parser.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 2ce127e..c65ed53 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -357,19 +357,19 @@ class Parser(object): accum.append(self.locexpr()) return accum - def locexpr(self, forward=False): + def locexpr(self): if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'): return self.const() - elif forward: + else: name = self.scanner.token self.scanner.scan() - return ForwardReference(name) - else: - loc = self.lookup(self.scanner.token) - self.scanner.scan() - return loc + loc = self.context.fetch(name) + if loc: + return loc + else: + return ForwardReference(name) - def indlocexpr(self, forward=False): + def indlocexpr(self): if self.scanner.consume('['): loc = self.locexpr() self.scanner.expect(']') @@ -380,10 +380,10 @@ class Parser(object): loc = self.locexpr() return AddressRef(loc) else: - return self.indexed_locexpr(forward=forward) + return self.indexed_locexpr() - def indexed_locexpr(self, forward=False): - loc = self.locexpr(forward=forward) + def indexed_locexpr(self): + loc = self.locexpr() if not isinstance(loc, str): index = None if self.scanner.consume('+'): @@ -483,7 +483,7 @@ class Parser(object): elif self.scanner.token in ("copy",): opcode = self.scanner.token self.scanner.scan() - src = self.indlocexpr(forward=True) + src = self.indlocexpr() self.scanner.expect(',') dest = self.indlocexpr() instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src) From fee36294db45523cb60a2e49bdc5252e6a2aa415 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 15:35:42 +0100 Subject: [PATCH 09/12] Update TODO and HISTORY. --- HISTORY.md | 7 +++++-- TODO.md | 5 ----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index aea6685..e77b673 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,8 +5,11 @@ History of SixtyPical ---- * `save X, Y, Z { }` now allowed as a shortcut for nested `save`s. -* Trying to call or goto a non-routine-typed symbol is now an - analysis error, not a syntax error. +* If the name in a location expression isn't found in the symbol + table, a forward reference will _always_ be generated; and forward + references in _all_ operations will be resolved after parsing. +* As a consequence, trying to call or goto a non-routine-typed symbol + is now an analysis error, not a syntax error. * Split TODO off into own file. * `sixtypical` no longer writes the compiled binary to standard output. The `--output` command-line argument should be given diff --git a/TODO.md b/TODO.md index ebdff52..d885d7f 100644 --- a/TODO.md +++ b/TODO.md @@ -27,11 +27,6 @@ Allow Which uses some other storage location instead of the stack. A local static would be a good candidate for such. -### Make all symbols forward-referencable - -Basically, don't do symbol-table lookups when parsing, but do have a more formal -"symbol resolution" (linking) phase right after parsing. - ### Associate each pointer with the buffer it points into Check that the buffer being read or written to through pointer, appears in appropriate From 443c3186c299707d4711f14e76141da6fb35598d Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 17:38:56 +0100 Subject: [PATCH 10/12] Change global instead of returning with carry set. --- eg/c64/demo-game/demo-game.60p | 47 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/eg/c64/demo-game/demo-game.60p b/eg/c64/demo-game/demo-game.60p index fd72065..964a2e1 100644 --- a/eg/c64/demo-game/demo-game.60p +++ b/eg/c64/demo-game/demo-game.60p @@ -36,6 +36,7 @@ typedef routine screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 outputs dispatch_game_state, actor_pos, actor_delta, actor_logic, + player_died, screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic game_state_routine @@ -45,13 +46,13 @@ typedef routine // // Routines that conform to this type also follow this convention: // -// Set carry if the player perished. Carry clear otherwise. +// Set player_died to 1 if the player perished. Unchanged otherwise. // typedef routine - inputs pos, delta, joy2, screen - outputs pos, delta, new_pos, screen, c - trashes a, x, y, z, n, v, ptr + inputs pos, delta, joy2, screen, player_died + outputs pos, delta, new_pos, screen, player_died + trashes a, x, y, z, n, v, c, ptr logic_routine // ---------------------------------------------------------------- @@ -87,6 +88,8 @@ word new_pos word table[256] actor_delta word delta +byte player_died + vector logic_routine table[256] actor_logic vector logic_routine dispatch_logic @@ -241,7 +244,7 @@ define check_new_position_in_bounds routine routine init_game inputs actor_pos, actor_delta, actor_logic - outputs actor_pos, actor_delta, actor_logic + outputs actor_pos, actor_delta, actor_logic, player_died trashes pos, a, y, z, n, c, v { ld y, 0 @@ -259,9 +262,11 @@ routine init_game } until z ld y, 0 - copy word 0, actor_pos + y + copy word 40, actor_pos + y copy word 0, actor_delta + y copy player_logic, actor_logic + y + + st y, player_died } // ---------------------------------------------------------------- @@ -301,10 +306,9 @@ define player_logic logic_routine st off, c add ptr, pos copy 81, [ptr] + y - - st off, c } else { - st on, c + ld a, 1 + st a, player_died } // FIXME these trashes, strictly speaking, probably shouldn't be needed, @@ -314,8 +318,6 @@ define player_logic logic_routine trash ptr trash y trash v - } else { - st off, c } } @@ -351,10 +353,6 @@ define enemy_logic logic_routine st off, c add ptr, pos copy 82, [ptr] + y - - st off, c - } else { - st on, c } // FIXME these trashes, strictly speaking, probably shouldn't be needed, @@ -373,8 +371,6 @@ define enemy_logic logic_routine copy $ffd8, delta } } - - st off, c } // ---------------------------------------------------------------- @@ -384,6 +380,7 @@ define enemy_logic logic_routine define game_state_title_screen game_state_routine { ld y, 0 + st y, player_died for y up to 17 { ld a, press_fire_msg + y @@ -408,6 +405,7 @@ define game_state_title_screen game_state_routine define game_state_play game_state_routine { ld x, 0 + st x, player_died for x up to 15 { copy actor_pos + x, pos copy actor_delta + x, delta @@ -420,23 +418,26 @@ define game_state_play game_state_routine save x { copy actor_logic + x, dispatch_logic call dispatch_logic - - if c { - // Player died! Want no dead! - call clear_screen - copy game_state_game_over, dispatch_game_state - } } copy pos, actor_pos + x copy delta, actor_delta + x } + ld a, player_died + if not z { + // Player died! Want no dead! + call clear_screen + copy game_state_game_over, dispatch_game_state + } + goto save_cinv } define game_state_game_over game_state_routine { + ld y, 0 + st y, player_died st off, c call check_button From 505fcb7b922d76ac255f792162b02324c4a9e033 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 17:41:40 +0100 Subject: [PATCH 11/12] game_state_routines pass through player_died; saves a few bytes. --- eg/c64/demo-game/demo-game.60p | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eg/c64/demo-game/demo-game.60p b/eg/c64/demo-game/demo-game.60p index 964a2e1..a521e30 100644 --- a/eg/c64/demo-game/demo-game.60p +++ b/eg/c64/demo-game/demo-game.60p @@ -33,6 +33,7 @@ typedef routine inputs joy2, press_fire_msg, dispatch_game_state, actor_pos, actor_delta, actor_logic, + player_died, screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 outputs dispatch_game_state, actor_pos, actor_delta, actor_logic, @@ -380,7 +381,6 @@ define enemy_logic logic_routine define game_state_title_screen game_state_routine { ld y, 0 - st y, player_died for y up to 17 { ld a, press_fire_msg + y @@ -436,8 +436,6 @@ define game_state_play game_state_routine define game_state_game_over game_state_routine { - ld y, 0 - st y, player_died st off, c call check_button From ff54a568a8da442979afdeb62351dcbda31bfe0e Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 17:47:59 +0100 Subject: [PATCH 12/12] Update TODO --- TODO.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/TODO.md b/TODO.md index d885d7f..908f893 100644 --- a/TODO.md +++ b/TODO.md @@ -1,20 +1,16 @@ TODO for SixtyPical =================== -### `low` and `high` address operators +### 16-bit `cmp` -To turn `word` type into `byte`. +This is because we don't actually want `low` and `high` address operators +that turn `word` type into `byte`. -Trying to remember if we have a compelling case for this or now. The best I can think -of is for implementing 16-bit `cmp` in an efficient way. Maybe we should see if we -can get by with 16-bit `cmp` instead though. +This is because this immediately makes things harder (that is, effectively +impossible) to analyze. -The problem is that once a byte is extracted, putting it back into a word is awkward. -The address operators have to modify a destination in a special way. That is, when -you say `st a, >word`, you are updating `word` to be `word & $ff | a << 8`, somelike. -Is that consistent with `st`? Well, probably it is, but we have to explain it. -It might make more sense, then, for it to be "part of the operation" instead of "part of -the reference"; something like `st.hi x, word`; `st.lo y, word`. Dunno. +16-bit `cmp` also benefits from some special differences between `cmp` +and `sub` on 6502, so it would be nice to capture them. ### Save values to other-than-the-stack @@ -32,7 +28,7 @@ would be a good candidate for such. Check that the buffer being read or written to through pointer, appears in appropriate inputs or outputs set. -In the analysis, when we obtain a pointer, we need to record, in contect, what buffer +In the analysis, when we obtain a pointer, we need to record, in context, what buffer that pointer came from. When we write through that pointer, we need to set that buffer as written. @@ -73,8 +69,8 @@ error. More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot appear elsewhere.) -If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can. -The constraints should iron out the same both ways. +If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can, +if the block is in tail position. The constraints should iron out the same both ways. And - once we have this - why do we need `goto` to be in tail position, strictly? As long as the routine has consistent type context every place it exits, that should be fine. @@ -86,3 +82,8 @@ Search a searchlist of include paths. And use them to make libraries of routine One such library routine might be an `interrupt routine` type for various architectures. Since "the supervisor" has stored values on the stack, we should be able to trash them with impunity, in such a routine. + +### Line numbers in analysis error messages + +For analysis errors, there is a line number, but it's the line of the routine +after the routine in which the analysis error occurred. Fix this.