From 51b98df8299ff2a0e663a0a61b069677eeab8fa5 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Sun, 26 Aug 2018 14:35:28 +0100 Subject: [PATCH 01/45] Split TODO off into own file. --- HISTORY.md | 5 +++ README.md | 111 +---------------------------------------------------- TODO.md | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 109 deletions(-) create mode 100644 TODO.md diff --git a/HISTORY.md b/HISTORY.md index a004965..fb985c7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,11 @@ History of SixtyPical ===================== +0.17 +---- + +* Split TODO off into own file. + 0.16 ---- diff --git a/README.md b/README.md index 17b548e..69321b7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ SixtyPical ========== -_Version 0.16. Work-in-progress, everything is subject to change._ +_Version 0.17. Work-in-progress, everything is subject to change._ **SixtyPical** is a 6502-like programming language with advanced static analysis. @@ -68,111 +68,4 @@ Documentation * [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md) * [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md) * [Output formats supported by `sixtypical`](doc/Output%20Formats.md) - -TODO ----- - -### `low` and `high` address operators - -To 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. - -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. - -### Save multiple values in single block - -As a shortcut for the idiom - - save a { save var { - ... - } } - -allow - - save a, var { - ... - } - -### Save values to other-than-the-stack - -Allow - - save a to temp_a { - ... - } - -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 -inputs or outputs set. - -In the analysis, when we obtain a pointer, we need to record, in contect, what buffer -that pointer came from. - -When we write through that pointer, we need to set that buffer as written. - -When we read through the pointer, we need to check that the buffer is readable. - -### Table overlays - -They are uninitialized, but the twist is, the address is a buffer that is -an input to and/or output of the routine. So, they are defined (insofar -as the buffer is defined.) - -They are therefore a "view" of a section of a buffer. - -This is slightly dangerous since it does permit aliases: the buffer and the -table refer to the same memory. - -Although, if they are `static`, you could say, in the routine in which they -are `static`, as soon as you've established one, you can no longer use the -buffer; and the ones you establish must be disjoint. - -(That seems to be the most compelling case for restricting them to `static`.) - -An alternative would be `static` pointers, which are currently not possible because -pointers must be zero-page, thus `@`, thus uninitialized. - -### Question "consistent initialization" - -Question the value of the "consistent initialization" principle for `if` statement analysis. - -Part of this is the trashes at the end; I think what it should be is that the trashes -after the `if` is the union of the trashes in each of the branches; this would obviate the -need to `trash` values explicitly, but if you tried to access them afterwards, it would still -error. - -### Tail-call optimization - -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. - -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. - -### "Include" directives - -Search a searchlist of include paths. And use them to make libraries of routines. - -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. +* [TODO](TODO.md) diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..7e8d116 --- /dev/null +++ b/TODO.md @@ -0,0 +1,107 @@ +TODO for SixtyPical +=================== + +### `low` and `high` address operators + +To 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. + +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. + +### Save multiple values in single block + +As a shortcut for the idiom + + save a { save var { + ... + } } + +allow + + save a, var { + ... + } + +### Save values to other-than-the-stack + +Allow + + save a to temp_a { + ... + } + +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 +inputs or outputs set. + +In the analysis, when we obtain a pointer, we need to record, in contect, what buffer +that pointer came from. + +When we write through that pointer, we need to set that buffer as written. + +When we read through the pointer, we need to check that the buffer is readable. + +### Table overlays + +They are uninitialized, but the twist is, the address is a buffer that is +an input to and/or output of the routine. So, they are defined (insofar +as the buffer is defined.) + +They are therefore a "view" of a section of a buffer. + +This is slightly dangerous since it does permit aliases: the buffer and the +table refer to the same memory. + +Although, if they are `static`, you could say, in the routine in which they +are `static`, as soon as you've established one, you can no longer use the +buffer; and the ones you establish must be disjoint. + +(That seems to be the most compelling case for restricting them to `static`.) + +An alternative would be `static` pointers, which are currently not possible because +pointers must be zero-page, thus `@`, thus uninitialized. + +### Question "consistent initialization" + +Question the value of the "consistent initialization" principle for `if` statement analysis. + +Part of this is the trashes at the end; I think what it should be is that the trashes +after the `if` is the union of the trashes in each of the branches; this would obviate the +need to `trash` values explicitly, but if you tried to access them afterwards, it would still +error. + +### Tail-call optimization + +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. + +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. + +### "Include" directives + +Search a searchlist of include paths. And use them to make libraries of routines. + +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. From 3675a186a1f27b363ecb5dc900dca6ca96ec8a19 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Wed, 5 Sep 2018 12:59:38 +0100 Subject: [PATCH 02/45] Add notes for Apple II target. --- doc/Apple II Notes.md | 14 ++++++++++++++ doc/Output Formats.md | 6 ++---- 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 doc/Apple II Notes.md diff --git a/doc/Apple II Notes.md b/doc/Apple II Notes.md new file mode 100644 index 0000000..b965c61 --- /dev/null +++ b/doc/Apple II Notes.md @@ -0,0 +1,14 @@ +Notes for building SixtyPical programs for Apple II +=================================================== + +And running them on `linapple`. + +We'll do `eg/rudiments/add-pass.60p`. It does nothing. + + bin/sixtypical --origin=0x2000 --output-format=raw eg/rudiments/add-pass.60p > add-pass.bin + cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk + a2in B sixtypical.dsk ADD-PASS add-pass.bin + a2ls sixtypical.dsk + linapple -d1 sixtypical.dsk -autoboot + +Next... we should do one that does something. diff --git a/doc/Output Formats.md b/doc/Output Formats.md index bebe1fd..d01363d 100644 --- a/doc/Output Formats.md +++ b/doc/Output Formats.md @@ -8,8 +8,7 @@ Output Formats The file contains only the emitted bytes of the compiled SixtyPical program. -The default origin is $0000; it is not unlikely you will want to -override this. +The default origin is $0000; you will likely want to override this. Note that the origin is not stored in the output file in this format; that information must be recorded separately. @@ -20,8 +19,7 @@ The first two bytes of the file contain the origin address in little-endian format. The remainder of the file is the emitted bytes of the compiled SixtyPical program, starting at that origin. -The default origin is $C000; it is likely you will want to -override this. +The default origin is $C000; you will likely want override this. This format coincides with Commodore's PRG format for disk files, thus its name. From 3e11d7122cffa5069e21f6011f4d88b3dbe3b94b Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Wed, 5 Sep 2018 15:20:44 +0100 Subject: [PATCH 03/45] Add apple2 target to loadngo.sh, apple2 example program. --- eg/apple2/print.60p | 20 ++++++++++++++++++++ loadngo.sh | 17 ++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 eg/apple2/print.60p diff --git a/eg/apple2/print.60p b/eg/apple2/print.60p new file mode 100644 index 0000000..aa72441 --- /dev/null +++ b/eg/apple2/print.60p @@ -0,0 +1,20 @@ +// Write ">AB>" to "standard output" + +routine cout + inputs a + trashes a + @ $FDED + +routine main + inputs a + trashes a, z, n +{ + ld a, 62 + call cout + ld a, 65 + call cout + ld a, 66 + call cout + ld a, 62 + call cout +} diff --git a/loadngo.sh b/loadngo.sh index e196167..79df088 100755 --- a/loadngo.sh +++ b/loadngo.sh @@ -1,6 +1,6 @@ #!/bin/sh -usage="Usage: loadngo.sh (c64|vic20|atari2600) [--dry-run] " +usage="Usage: loadngo.sh (c64|vic20|atari2600|apple2) [--dry-run] " arch="$1" shift 1 @@ -21,6 +21,21 @@ elif [ "X$arch" = "Xvic20" ]; then elif [ "X$arch" = "Xatari2600" ]; then output_format='atari2600-cart' emu='stella' +elif [ "X$arch" = "Xapple2" ]; then + src="$1" + out=/tmp/a-out.bin + bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src > $out + ls -la $out + cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk + # TODO: replace HELLO with something that does like + # BLOAD "PROG" + # CALL 8192 + # (not BRUN because it does not always return to BASIC afterwards not sure why) + a2rm sixtypical.dsk PROG + a2in B sixtypical.dsk PROG $out + linapple -d1 sixtypical.dsk -autoboot + rm -f $out sixtypical.dsk + exit 0 else echo $usage && exit 1 fi From fca00ff1278659e0e10c39033ede88a48f4fc0b5 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Wed, 5 Sep 2018 21:36:18 +0100 Subject: [PATCH 04/45] Rewrite the intro blurb in the README. --- README.md | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 69321b7..779f8bd 100644 --- a/README.md +++ b/README.md @@ -3,30 +3,36 @@ SixtyPical _Version 0.17. Work-in-progress, everything is subject to change._ -**SixtyPical** is a 6502-like programming language with advanced -static analysis. +**SixtyPical** is a low-level programming language with advanced +static analysis. Many of its primitive instructions resemble +those of the 6502 CPU — in fact it is intended to be compiled to +6502 machine code — but along with these are constructs which +ease structuring and analysizing the code. -"6502-like" means that it has similar restrictions as programming -in 6502 assembly (e.g. the programmer must choose the registers that -values will be stored in) and is concomitantly easy for a compiler to -translate it to 6502 machine language code. +SixtyPical aims to fill this niche: -"Advanced static analysis" includes _abstract interpretation_, where we -go through the program step by step, tracking not just the changes that -happen during a _specific_ execution of the program, but _sets_ of changes -that could _possibly_ happen in any run of the program. This lets us -determine that certain things can never happen, which we can then formulate -as safety checks. +* You'd use assembly, but you don't want to spend hours + debugging (say) a memory overrun that happened because of a + ridiculous silly error. +* You'd use C, but you don't want the overhead of compiler-added + code to manage the stack and registers. -In practice, this means it catches things like +SixtyPical gives the programmer a coding regimen on par with assembly +language in terms of size and hands-on-ness, but also able to catch +many ridiculous silly errors at compile time, such as * you forgot to clear carry before adding something to the accumulator -* a subroutine that you call trashes a register you thought was preserved +* a subroutine that you called trashes a register you thought it preserved * you tried to read or write a byte beyond the end of a byte array * you tried to write the address of something that was not a routine, to a jump vector -and suchlike. It also provides some convenient operations based on +Many of these checks are done with _abstract interpretation_, where we +go through the program step by step, tracking not just the changes that +happen during a _specific_ execution of the program, but _sets_ of changes +that could _possibly_ happen in any run of the program. + +SixtyPical also provides some convenient operations based on machine-language programming idioms, such as * copying values from one register to another (via a third register when @@ -35,8 +41,15 @@ machine-language programming idioms, such as * explicit tail calls * indirect subroutine calls -The reference implementation can analyze and compile SixtyPical programs to -6502 machine code. +SixtyPical is defined by a specification document, a set of test cases, +and a reference implementation written in Python 2. The reference +implementation can analyze and compile SixtyPical programs to 6502 machine code, +which can be run on several 6502-based 8-bit architectures: + +* Commodore 64 +* Commodore VIC-20 +* Atari 2600 VCS +* Apple II Quick Start ----------- From c11869f322ec9260cd090339e79ece7381d41909 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Wed, 5 Sep 2018 21:45:58 +0100 Subject: [PATCH 05/45] Not making any promises, but reduce the errors under Python 3. --- src/sixtypical/analyzer.py | 20 ++++++++++---------- src/sixtypical/compiler.py | 2 +- src/sixtypical/emitter.py | 2 +- src/sixtypical/model.py | 2 +- src/sixtypical/parser.py | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index ecb3b57..11c21ff 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -372,23 +372,23 @@ class Analyzer(object): context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes) if self.debug: - print "at start of routine `{}`:".format(routine.name) - print context + print("at start of routine `{}`:".format(routine.name)) + print(context) self.analyze_block(routine.block, context) trashed = set(context.each_touched()) - set(context.each_meaningful()) if self.debug: - print "at end of routine `{}`:".format(routine.name) - print context - print "trashed: ", LocationRef.format_set(trashed) - print "outputs: ", LocationRef.format_set(type_.outputs) + print("at end of routine `{}`:".format(routine.name)) + print(context) + print("trashed: ", LocationRef.format_set(trashed)) + print("outputs: ", LocationRef.format_set(type_.outputs)) trashed_outputs = type_.outputs & trashed if trashed_outputs: - print "TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs) - print '' - print '-' * 79 - print '' + print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs)) + print('') + print('-' * 79) + print('') # even if we goto another routine, we can't trash an output. for ref in trashed: diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 34ed70f..be610b8 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -112,7 +112,7 @@ class Compiler(object): routine_name = roster_row[-1] self.compile_routine(self.routines[routine_name]) - for location, label in self.trampolines.iteritems(): + for location, label in self.trampolines.items(): self.emitter.resolve_label(label) self.emitter.emit(JMP(Indirect(self.get_label(location.name)))) self.emitter.emit(RTS()) diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index a1b962c..6ea308a 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -13,7 +13,7 @@ class Emittable(object): class Byte(Emittable): def __init__(self, value): - if isinstance(value, basestring): + if isinstance(value, str): value = ord(value) if value < -127 or value > 255: raise IndexError(value) diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index 30d5d54..6effc49 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -20,7 +20,7 @@ class Type(object): def backpatch_constraint_labels(self, resolver): def resolve(w): - if not isinstance(w, basestring): + if not isinstance(w, str): return w return resolver(w) if isinstance(self, TableType): diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index e16cf66..8cee87a 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -90,7 +90,7 @@ class Parser(object): self.scanner.check_type('EOF') # now backpatch the executable types. - #for type_name, type_ in self.context.typedefs.iteritems(): + #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)) @@ -103,7 +103,7 @@ 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, basestring): + if instr.opcode in ('copy',) and isinstance(instr.src, str): name = instr.src model = self.lookup(name) if not isinstance(model.type, (RoutineType, VectorType)): @@ -359,7 +359,7 @@ class Parser(object): def indexed_locexpr(self, forward=False): loc = self.locexpr(forward=forward) - if not isinstance(loc, basestring): + if not isinstance(loc, str): index = None if self.scanner.consume('+'): index = self.locexpr() From 3c1564ea0924dae1006b67677a106d20169adb94 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 12:50:47 +0100 Subject: [PATCH 06/45] Abort loadngo apple2, if the SixtyPical program couldn't compile. --- eg/apple2/lores.60p | 20 ++++++++++++++++++++ loadngo.sh | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 eg/apple2/lores.60p diff --git a/eg/apple2/lores.60p b/eg/apple2/lores.60p new file mode 100644 index 0000000..ac88a4c --- /dev/null +++ b/eg/apple2/lores.60p @@ -0,0 +1,20 @@ +byte ds_graphics @ $C050 +byte ds_text @ $C051 +byte ds_full @ $C052 +byte ds_split @ $C053 +byte ds_page1 @ $C054 +byte ds_page2 @ $C055 +byte ds_lores @ $C056 +byte ds_hires @ $C057 + +routine main + inputs a + outputs ds_lores, ds_page1, ds_split, ds_graphics + trashes a, z, n +{ + ld a, 0 + st a, ds_lores + st a, ds_page1 + st a, ds_split + st a, ds_graphics +} diff --git a/loadngo.sh b/loadngo.sh index 79df088..e5b9328 100755 --- a/loadngo.sh +++ b/loadngo.sh @@ -24,7 +24,7 @@ elif [ "X$arch" = "Xatari2600" ]; then elif [ "X$arch" = "Xapple2" ]; then src="$1" out=/tmp/a-out.bin - bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src > $out + bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src > $out || exit 1 ls -la $out cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk # TODO: replace HELLO with something that does like From 3fd7e52bc738c975e647e8f0a03d00109b8d189f Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 14:21:29 +0100 Subject: [PATCH 07/45] Reduce number of errors under Python 3 another smidge. --- bin/sixtypical | 2 +- src/sixtypical/model.py | 2 +- tests/SixtyPical Analysis.md | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bin/sixtypical b/bin/sixtypical index 680b41e..9c3824c 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Usage: sixtypical [OPTIONS] FILES diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index 6effc49..e2d7757 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -172,7 +172,7 @@ class LocationRef(Ref): @classmethod def format_set(cls, location_refs): - return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)]) + return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs, key=lambda x: x.name)]) class IndirectRef(Ref): diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 5e23587..c5eaff8 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -2001,6 +2001,8 @@ It will continue to be defined outside the block. A trashed value that has been saved can be used inside the block. It will continue to be trashed outside the block. +(Note, both x and a are unmeaningful in this test.) + | routine main | inputs a | outputs a, x @@ -2013,7 +2015,7 @@ It will continue to be trashed outside the block. | ld x, 1 | } | } - ? UnmeaningfulOutputError: x + ? UnmeaningfulOutputError The known range of a value will be preserved outside the block as well. @@ -2272,7 +2274,9 @@ Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). | } ? ForbiddenWriteError: lives -a, z, and n are trashed, and must be declared as such +a, z, and n are trashed, and must be declared as such. + +(Note, both n and z are forbidden writes in this tests.) | byte lives | routine main @@ -2280,7 +2284,7 @@ a, z, and n are trashed, and must be declared as such | { | copy 0, lives | } - ? ForbiddenWriteError: n + ? ForbiddenWriteError a, z, and n are trashed, and must not be declared as outputs. From 4bba75857f1cd3abcb436675b7d944c35c282b70 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 16:18:41 +0100 Subject: [PATCH 08/45] --output cmdline argument, serialize() returns an array of bytes. --- HISTORY.md | 3 +++ bin/sixtypical | 10 +++++++--- loadngo.sh | 4 ++-- src/sixtypical/emitter.py | 18 +++++++++++------- src/sixtypical/gen6502.py | 9 ++++----- tests/SixtyPical Analysis.md | 6 ++++-- tests/SixtyPical Compilation.md | 2 +- tests/SixtyPical Fallthru.md | 4 ++-- 8 files changed, 34 insertions(+), 22 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index fb985c7..34644df 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,6 +5,9 @@ History of SixtyPical ---- * Split TODO off into own file. +* `sixtypical` no longer writes the compiled binary to standard + output. The `--output` command-line argument should be given. +* Many tests pass when `sixtypical` is run with Python 3. 0.16 ---- diff --git a/bin/sixtypical b/bin/sixtypical index 9c3824c..0ec8db2 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Usage: sixtypical [OPTIONS] FILES @@ -79,7 +79,7 @@ def process_input_files(filenames, options): if options.analyze_only: return - fh = sys.stdout + fh = open(options.output, 'wb') if options.output_format == 'raw': start_addr = 0x0000 @@ -111,7 +111,7 @@ def process_input_files(filenames, options): # If we are outputting a .PRG, we output the load address first. # We don't use the Emitter for this b/c not part of addr space. if options.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'): - fh.write(Word(start_addr).serialize(0)) + fh.write(bytearray(Word(start_addr).serialize())) emitter = Emitter(start_addr) for byte in prelude: @@ -140,6 +140,10 @@ if __name__ == '__main__': help="The SixtyPical source files to compile." ) + argparser.add_argument( + "--output", "-o", type=str, metavar='FILENAME', + help="File to which generated 6502 code will be written." + ) argparser.add_argument( "--origin", type=str, default=None, help="Location in memory where the `main` routine will be " diff --git a/loadngo.sh b/loadngo.sh index e5b9328..b2d6461 100755 --- a/loadngo.sh +++ b/loadngo.sh @@ -24,7 +24,7 @@ elif [ "X$arch" = "Xatari2600" ]; then elif [ "X$arch" = "Xapple2" ]; then src="$1" out=/tmp/a-out.bin - bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src > $out || exit 1 + bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src --output $out || exit 1 ls -la $out cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk # TODO: replace HELLO with something that does like @@ -53,7 +53,7 @@ fi ### do it ### out=/tmp/a-out.prg -bin/sixtypical --traceback --output-format=$output_format $src > $out || exit 1 +bin/sixtypical --traceback --output-format=$output_format $src --output $out || exit 1 ls -la $out $emu $out rm -f $out diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index 6ea308a..59d98fb 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -8,6 +8,7 @@ class Emittable(object): raise NotImplementedError def serialize(self, addr): + """Should return an array of unsigned bytes (integers from 0 to 255.)""" raise NotImplementedError @@ -25,7 +26,7 @@ class Byte(Emittable): return 1 def serialize(self, addr=None): - return chr(self.value) + return [self.value] def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.value) @@ -43,7 +44,7 @@ class Word(Emittable): word = self.value low = word & 255 high = (word >> 8) & 255 - return chr(low) + chr(high) + return [low, high] def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.value) @@ -60,9 +61,11 @@ class Table(Emittable): return self._size def serialize(self, addr=None): - buf = ''.join([emittable.serialize() for emittable in self.value]) + buf = [] + for emittable in self.value: + buf.extend(emittable.serialize()) while len(buf) < self.size(): - buf += chr(0) + buf.append(0) return buf def __repr__(self): @@ -130,7 +133,7 @@ class HighAddressByte(Emittable): return 1 def serialize(self, addr=None): - return self.label.serialize()[0] + return [self.label.serialize()[0]] def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.label) @@ -145,7 +148,7 @@ class LowAddressByte(Emittable): return 1 def serialize(self, addr=None): - return self.label.serialize()[1] + return [self.label.serialize()[1]] def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.label) @@ -167,10 +170,11 @@ class Emitter(object): self.addr += thing.size() def serialize(self, stream): + """`stream` should be a file opened in binary mode.""" addr = self.start_addr for emittable in self.accum: chunk = emittable.serialize(addr) - stream.write(chunk) + stream.write(bytearray(chunk)) addr += len(chunk) def make_label(self, name=None): diff --git a/src/sixtypical/gen6502.py b/src/sixtypical/gen6502.py index 7b8d07c..352263a 100644 --- a/src/sixtypical/gen6502.py +++ b/src/sixtypical/gen6502.py @@ -20,7 +20,7 @@ class Implied(AddressingMode): return 0 def serialize(self, addr=None): - return '' + return [] def __repr__(self): return "%s()" % (self.__class__.__name__) @@ -109,10 +109,9 @@ class Instruction(Emittable): return 1 + self.operand.size() if self.operand else 0 def serialize(self, addr=None): - return ( - chr(self.opcodes[self.operand.__class__]) + - self.operand.serialize(addr) - ) + serialized_operand = self.operand.serialize(addr) + assert isinstance(serialized_operand, list), self.operand.__class__ + return [self.opcodes[self.operand.__class__]] + serialized_operand def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.operand) diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index c5eaff8..b4b0b11 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -2276,7 +2276,7 @@ Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). a, z, and n are trashed, and must be declared as such. -(Note, both n and z are forbidden writes in this tests.) +(Note, both n and z are forbidden writes in this test.) | byte lives | routine main @@ -2288,13 +2288,15 @@ a, z, and n are trashed, and must be declared as such. a, z, and n are trashed, and must not be declared as outputs. +(Note, both n and a are unmeaningful outputs in this test.) + | byte lives | routine main | outputs lives, a, z, n | { | copy 0, lives | } - ? UnmeaningfulOutputError: n + ? UnmeaningfulOutputError Unless of course you subsequently initialize them. diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 1f72aad..7070374 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -7,7 +7,7 @@ SixtyPical to 6502 machine code. [Falderal]: http://catseye.tc/node/Falderal -> Functionality "Compile SixtyPical program" is implemented by - -> shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter Tests for functionality "Compile SixtyPical program" diff --git a/tests/SixtyPical Fallthru.md b/tests/SixtyPical Fallthru.md index 47f0310..1eca502 100644 --- a/tests/SixtyPical Fallthru.md +++ b/tests/SixtyPical Fallthru.md @@ -48,7 +48,7 @@ Treat R as a mutable set and start with an empty list of lists L. Then, - Remove all elements occurring in C, from R. - Repeat until R is empty. -When times comes to generate code, generate it in the order given by L. +When time comes to generate code, generate it in the order given by L. In addition, each sublist in L represents a number of routines to generate; all except the final routine in such a sublist need not have any jump instruction generated for its final `goto`. @@ -65,7 +65,7 @@ to pass these tests to be considered an implementation of SixtyPical. -> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)" -> Functionality "Compile SixtyPical program with fallthru optimization" is implemented by - -> shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter Tests for functionality "Dump fallthru info for SixtyPical program" From 10062fe2fb6456fd1df9f8d7faf2f80e870cf517 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 16:32:48 +0100 Subject: [PATCH 09/45] Fix whitespace (but not sorting) issue in --dump-fallthru-info. --- bin/sixtypical | 2 +- tests/SixtyPical Fallthru.md | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/bin/sixtypical b/bin/sixtypical index 0ec8db2..cbaf1e3 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -68,7 +68,7 @@ def process_input_files(filenames, options): return if label: sys.stdout.write("*** {}:\n".format(label)) - sys.stdout.write(json.dumps(data, indent=4, sort_keys=True)) + sys.stdout.write(json.dumps(data, indent=4, sort_keys=True, separators=(',', ':'))) sys.stdout.write("\n") fa = FallthruAnalyzer(debug=options.debug) diff --git a/tests/SixtyPical Fallthru.md b/tests/SixtyPical Fallthru.md index 1eca502..2a0981c 100644 --- a/tests/SixtyPical Fallthru.md +++ b/tests/SixtyPical Fallthru.md @@ -94,7 +94,7 @@ If `main` does a `goto foo`, then it can fall through to `foo`. | } = [ = [ - = "main", + = "main", = "foo" = ] = ] @@ -119,9 +119,9 @@ of them to fall through, when selecting the order of routines. | } = [ = [ - = "main", + = "main", = "foo" - = ], + = ], = [ = "bar" = ] @@ -144,7 +144,7 @@ nothing ever falls through to `main`. = [ = [ = "main" - = ], + = ], = [ = "foo" = ] @@ -172,9 +172,9 @@ fall through to the other. = [ = [ = "main" - = ], + = ], = [ - = "bar", + = "bar", = "foo" = ] = ] @@ -204,10 +204,10 @@ routine. = [ = [ = "main" - = ], + = ], = [ = "bar" - = ], + = ], = [ = "foo" = ] @@ -238,9 +238,9 @@ If, however, they are the same goto, one can be optimized away. = [ = [ = "main" - = ], + = ], = [ - = "foo", + = "foo", = "bar" = ] = ] @@ -269,10 +269,10 @@ because we don't necessarily know what actual routine the vector contains. = [ = [ = "main" - = ], + = ], = [ = "bar" - = ], + = ], = [ = "foo" = ] @@ -321,14 +321,14 @@ Our algorithm might not be strictly optimal, but it does a good job. | } = [ = [ - = "main", - = "r1", - = "r2", - = "r3", + = "main", + = "r1", + = "r2", + = "r3", = "r4" - = ], + = ], = [ - = "r5", + = "r5", = "r6" = ] = ] From 8efa73f79de385be71b4eb975eda9563623f680e Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 17:15:10 +0100 Subject: [PATCH 10/45] Explicitly sort the chains by their content, for stable sort. --- HISTORY.md | 2 +- src/sixtypical/fallthru.py | 2 +- tests/SixtyPical Fallthru.md | 30 +++++++++++++++--------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 34644df..cf902e3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -7,7 +7,7 @@ History of SixtyPical * Split TODO off into own file. * `sixtypical` no longer writes the compiled binary to standard output. The `--output` command-line argument should be given. -* Many tests pass when `sixtypical` is run with Python 3. +* All tests pass when `sixtypical` is run under Python 3.5.2. 0.16 ---- diff --git a/src/sixtypical/fallthru.py b/src/sixtypical/fallthru.py index 85024bd..995fbcf 100644 --- a/src/sixtypical/fallthru.py +++ b/src/sixtypical/fallthru.py @@ -43,7 +43,7 @@ class FallthruAnalyzer(object): while pending_routines: chains = [self.find_chain(k, pending_routines) for k in pending_routines.keys()] - chains.sort(key=len, reverse=True) + chains.sort(key=lambda x: (len(x), str(x)), reverse=True) c = chains[0] roster.append(c) for k in c: diff --git a/tests/SixtyPical Fallthru.md b/tests/SixtyPical Fallthru.md index 2a0981c..75e3779 100644 --- a/tests/SixtyPical Fallthru.md +++ b/tests/SixtyPical Fallthru.md @@ -174,8 +174,8 @@ fall through to the other. = "main" = ], = [ - = "bar", - = "foo" + = "foo", + = "bar" = ] = ] @@ -206,10 +206,10 @@ routine. = "main" = ], = [ - = "bar" + = "foo" = ], = [ - = "foo" + = "bar" = ] = ] @@ -271,10 +271,10 @@ because we don't necessarily know what actual routine the vector contains. = "main" = ], = [ - = "bar" + = "foo" = ], = [ - = "foo" + = "bar" = ] = ] @@ -416,12 +416,12 @@ in the "true" branch is a `goto`. | { | } = $080D RTS - = $080E LDA #$FF - = $0810 RTS - = $0811 LDA #$00 - = $0813 BNE $081D - = $0815 LDA #$01 - = $0817 JMP $080E - = $081A JMP $0822 - = $081D LDA #$02 - = $081F JMP $080D + = $080E LDA #$00 + = $0810 BNE $081A + = $0812 LDA #$01 + = $0814 JMP $081F + = $0817 JMP $081F + = $081A LDA #$02 + = $081C JMP $080D + = $081F LDA #$FF + = $0821 RTS From 0a91d6bbc04609a4703bc045c387ef53a41e0dba Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 17:21:43 +0100 Subject: [PATCH 11/45] Rename method for more distinction/clarity. --- bin/sixtypical | 2 +- src/sixtypical/emitter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/sixtypical b/bin/sixtypical index cbaf1e3..0416311 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -129,7 +129,7 @@ def process_input_files(filenames, options): if options.debug: pprint(emitter.accum) else: - emitter.serialize(fh) + emitter.serialize_to(fh) if __name__ == '__main__': diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index 59d98fb..d4434e5 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -169,7 +169,7 @@ class Emitter(object): self.accum.append(thing) self.addr += thing.size() - def serialize(self, stream): + def serialize_to(self, stream): """`stream` should be a file opened in binary mode.""" addr = self.start_addr for emittable in self.accum: From 24f093b1dbf42cac4a1807ed3b8b16f6aa4eb4ef Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 18:11:47 +0100 Subject: [PATCH 12/45] Make all serialize() methods take addr() as an arg, not kwarg. --- bin/sixtypical | 2 +- src/sixtypical/emitter.py | 38 ++++++++++++++++++++------------------ src/sixtypical/gen6502.py | 28 +++++++++++++--------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/bin/sixtypical b/bin/sixtypical index 0416311..5e6c420 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -111,7 +111,7 @@ def process_input_files(filenames, options): # If we are outputting a .PRG, we output the load address first. # We don't use the Emitter for this b/c not part of addr space. if options.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'): - fh.write(bytearray(Word(start_addr).serialize())) + fh.write(bytearray(Word(start_addr).serialize(0))) emitter = Emitter(start_addr) for byte in prelude: diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index d4434e5..5b4d346 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -8,7 +8,9 @@ class Emittable(object): raise NotImplementedError def serialize(self, addr): - """Should return an array of unsigned bytes (integers from 0 to 255.)""" + """Should return an array of unsigned bytes (integers from 0 to 255.) + `addr` is the address the value is being serialized at; for most objects + it makes no difference, but some objects (like relative branches) do care.""" raise NotImplementedError @@ -25,7 +27,7 @@ class Byte(Emittable): def size(self): return 1 - def serialize(self, addr=None): + def serialize(self, addr): return [self.value] def __repr__(self): @@ -40,7 +42,7 @@ class Word(Emittable): def size(self): return 2 - def serialize(self, addr=None): + def serialize(self, addr): word = self.value low = word & 255 high = (word >> 8) & 255 @@ -60,10 +62,10 @@ class Table(Emittable): def size(self): return self._size - def serialize(self, addr=None): + def serialize(self, addr): buf = [] for emittable in self.value: - buf.extend(emittable.serialize()) + buf.extend(emittable.serialize(addr)) # FIXME: addr + offset while len(buf) < self.size(): buf.append(0) return buf @@ -87,17 +89,17 @@ class Label(Emittable): def size(self): return 2 - def serialize(self, addr=None, offset=0): + def serialize(self, addr, offset=0): assert self.addr is not None, "unresolved label: %s" % self.name - return Word(self.addr + offset).serialize() + return Word(self.addr + offset).serialize(addr) def serialize_relative_to(self, addr): assert self.addr is not None, "unresolved label: %s" % self.name - return Byte(self.addr - (addr + 2)).serialize() + return Byte(self.addr - (addr + 2)).serialize(addr) - def serialize_as_zero_page(self, offset=0): + def serialize_as_zero_page(self, addr, offset=0): assert self.addr is not None, "unresolved label: %s" % self.name - return Byte(self.addr + offset).serialize() + return Byte(self.addr + offset).serialize(addr) def __repr__(self): addr_s = ', addr=%r' % self.addr if self.addr is not None else '' @@ -114,11 +116,11 @@ class Offset(Emittable): def size(self): self.label.size() - def serialize(self, addr=None): - return self.label.serialize(offset=self.offset) + def serialize(self, addr): + return self.label.serialize(addr, offset=self.offset) - def serialize_as_zero_page(self, offset=0): - return self.label.serialize_as_zero_page(offset=self.offset) + def serialize_as_zero_page(self, addr, offset=0): + return self.label.serialize_as_zero_page(addr, offset=self.offset) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset) @@ -132,8 +134,8 @@ class HighAddressByte(Emittable): def size(self): return 1 - def serialize(self, addr=None): - return [self.label.serialize()[0]] + def serialize(self, addr): + return [self.label.serialize(addr)[0]] def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.label) @@ -147,8 +149,8 @@ class LowAddressByte(Emittable): def size(self): return 1 - def serialize(self, addr=None): - return [self.label.serialize()[1]] + def serialize(self, addr): + return [self.label.serialize(addr)[1]] def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.label) diff --git a/src/sixtypical/gen6502.py b/src/sixtypical/gen6502.py index 352263a..d3c14c7 100644 --- a/src/sixtypical/gen6502.py +++ b/src/sixtypical/gen6502.py @@ -8,7 +8,7 @@ class AddressingMode(Emittable): """Size of the operand for the mode (not including the opcode)""" raise NotImplementedError - def serialize(self, addr=None): + def serialize(self, addr): raise NotImplementedError def __repr__(self): @@ -19,7 +19,7 @@ class Implied(AddressingMode): def size(self): return 0 - def serialize(self, addr=None): + def serialize(self, addr): return [] def __repr__(self): @@ -34,8 +34,8 @@ class Immediate(AddressingMode): def size(self): return 1 - def serialize(self, addr=None): - return self.value.serialize() + def serialize(self, addr): + return self.value.serialize(addr) class Absolute(AddressingMode): @@ -46,8 +46,8 @@ class Absolute(AddressingMode): def size(self): return 2 - def serialize(self, addr=None): - return self.value.serialize() + def serialize(self, addr): + return self.value.serialize(addr) class AbsoluteX(Absolute): @@ -66,8 +66,8 @@ class ZeroPage(AddressingMode): def size(self): return 1 - def serialize(self, addr=None): - return self.value.serialize_as_zero_page() + def serialize(self, addr): + return self.value.serialize_as_zero_page(addr) class Indirect(AddressingMode): @@ -78,8 +78,8 @@ class Indirect(AddressingMode): def size(self): return 2 - def serialize(self, addr=None): - return self.value.serialize() + def serialize(self, addr): + return self.value.serialize(addr) class IndirectY(ZeroPage): @@ -94,7 +94,7 @@ class Relative(AddressingMode): def size(self): return 1 - def serialize(self, addr=None): + def serialize(self, addr): return self.value.serialize_relative_to(addr) @@ -108,10 +108,8 @@ class Instruction(Emittable): def size(self): return 1 + self.operand.size() if self.operand else 0 - def serialize(self, addr=None): - serialized_operand = self.operand.serialize(addr) - assert isinstance(serialized_operand, list), self.operand.__class__ - return [self.opcodes[self.operand.__class__]] + serialized_operand + def serialize(self, addr): + return [self.opcodes[self.operand.__class__]] + self.operand.serialize(addr) def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.operand) From 99a0d5624aafb7645350fe9cfc1c8ede687323d5 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 18:25:29 +0100 Subject: [PATCH 13/45] Awful binding, but at least this code isn't in main anymore. --- bin/sixtypical | 60 +++++++++---------------------------- src/sixtypical/outputter.py | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 46 deletions(-) create mode 100644 src/sixtypical/outputter.py diff --git a/bin/sixtypical b/bin/sixtypical index 5e6c420..3ca394d 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -20,7 +20,7 @@ import traceback from sixtypical.parser import Parser, ParsingContext from sixtypical.analyzer import Analyzer -from sixtypical.emitter import Emitter, Byte, Word +from sixtypical.outputter import Outputter from sixtypical.compiler import Compiler @@ -79,57 +79,25 @@ def process_input_files(filenames, options): if options.analyze_only: return - fh = open(options.output, 'wb') - - if options.output_format == 'raw': - start_addr = 0x0000 - prelude = [] - elif options.output_format == 'prg': - start_addr = 0xc000 - prelude = [] - elif options.output_format == 'c64-basic-prg': - start_addr = 0x0801 - prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32, - 0x30, 0x36, 0x31, 0x00, 0x00, 0x00] - elif options.output_format == 'vic20-basic-prg': - start_addr = 0x1001 - prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34, - 0x31, 0x30, 0x39, 0x00, 0x00, 0x00] - elif options.output_format == 'atari2600-cart': - start_addr = 0xf000 - prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9, - 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb] - else: - raise NotImplementedError("Unknown output format: {}".format(options.output_format)) + outputter = Outputter(options.output_format) if options.origin is not None: if options.origin.startswith('0x'): - start_addr = int(options.origin, 16) + outputter.set_start_addr(int(options.origin, 16)) else: - start_addr = int(options.origin, 10) + outputter.set_start_addr(int(options.origin, 10)) - # If we are outputting a .PRG, we output the load address first. - # We don't use the Emitter for this b/c not part of addr space. - if options.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'): - fh.write(bytearray(Word(start_addr).serialize(0))) + with open(options.output, 'wb') as fh: + # this is *awful* binding + emitter = outputter.write_prelude(fh) + compiler = Compiler(emitter) + compiler.compile_program(program, compilation_roster=compilation_roster) + outputter.write_postlude(emitter) - emitter = Emitter(start_addr) - for byte in prelude: - emitter.emit(Byte(byte)) - compiler = Compiler(emitter) - compiler.compile_program(program, compilation_roster=compilation_roster) - - # If we are outputting a cartridge with boot and BRK address - # at the end, pad to ROM size minus 4 bytes, and emit addresses. - if options.output_format == 'atari2600-cart': - emitter.pad_to_size(4096 - 4) - emitter.emit(Word(start_addr)) - emitter.emit(Word(start_addr)) - - if options.debug: - pprint(emitter.accum) - else: - emitter.serialize_to(fh) + if options.debug: + pprint(emitter.accum) + else: + emitter.serialize_to(fh) if __name__ == '__main__': diff --git a/src/sixtypical/outputter.py b/src/sixtypical/outputter.py new file mode 100644 index 0000000..b6cbbfc --- /dev/null +++ b/src/sixtypical/outputter.py @@ -0,0 +1,52 @@ +"""Executable file writer.""" + +from sixtypical.emitter import Emitter, Byte, Word + + +class Outputter(object): + def __init__(self, output_format): + self.output_format = output_format + if output_format == 'raw': + self.start_addr = 0x0000 + self.prelude = [] + elif output_format == 'prg': + self.start_addr = 0xc000 + self.prelude = [] + elif output_format == 'c64-basic-prg': + self.start_addr = 0x0801 + self.prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32, + 0x30, 0x36, 0x31, 0x00, 0x00, 0x00] + elif output_format == 'vic20-basic-prg': + self.start_addr = 0x1001 + self.prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34, + 0x31, 0x30, 0x39, 0x00, 0x00, 0x00] + elif output_format == 'atari2600-cart': + self.start_addr = 0xf000 + self.prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9, + 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb] + else: + raise NotImplementedError("Unknown output format: {}".format(output_format)) + + def set_start_addr(self, start_addr): + self.start_addr = start_addr + + def write_prelude(self, fh): + + # If we are outputting a .PRG, we output the load address first. + # We don't use the Emitter for this b/c not part of addr space. + if self.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'): + fh.write(bytearray(Word(self.start_addr).serialize(0))) + + emitter = Emitter(self.start_addr) + for byte in self.prelude: + emitter.emit(Byte(byte)) + + return emitter + + def write_postlude(self, emitter): + # If we are outputting a cartridge with boot and BRK address + # at the end, pad to ROM size minus 4 bytes, and emit addresses. + if self.output_format == 'atari2600-cart': + emitter.pad_to_size(4096 - 4) + emitter.emit(Word(self.start_addr)) + emitter.emit(Word(self.start_addr)) From 0b6a66fdbbb1cffd5cd2ab65323ea18a4d0cefda Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 6 Sep 2018 18:26:30 +0100 Subject: [PATCH 14/45] pyflakes. --- src/sixtypical/analyzer.py | 2 +- src/sixtypical/compiler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 11c21ff..53857a5 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save +from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.model import ( TYPE_BYTE, TYPE_WORD, TableType, BufferType, PointerType, VectorType, RoutineType, diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index be610b8..faed525 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save +from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.model import ( ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef, TYPE_BIT, TYPE_BYTE, TYPE_WORD, From 8ce787a7972ed221dfe72168867efacb6623b5bd Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 09:10:20 +0100 Subject: [PATCH 15/45] Inheritance hierarchy; binding is a little less awful. --- bin/sixtypical | 22 ++++----- src/sixtypical/outputter.py | 94 +++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/bin/sixtypical b/bin/sixtypical index 3ca394d..9b2d2cd 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -20,7 +20,7 @@ import traceback from sixtypical.parser import Parser, ParsingContext from sixtypical.analyzer import Analyzer -from sixtypical.outputter import Outputter +from sixtypical.outputter import outputter_class_for from sixtypical.compiler import Compiler @@ -79,25 +79,23 @@ def process_input_files(filenames, options): if options.analyze_only: return - outputter = Outputter(options.output_format) - + start_addr = None if options.origin is not None: if options.origin.startswith('0x'): - outputter.set_start_addr(int(options.origin, 16)) + start_addr = int(options.origin, 16) else: - outputter.set_start_addr(int(options.origin, 10)) + start_addr = int(options.origin, 10) with open(options.output, 'wb') as fh: - # this is *awful* binding - emitter = outputter.write_prelude(fh) - compiler = Compiler(emitter) + outputter = outputter_class_for(options.output_format)(fh, start_addr=start_addr) + outputter.write_prelude() + compiler = Compiler(outputter.emitter) compiler.compile_program(program, compilation_roster=compilation_roster) - outputter.write_postlude(emitter) - + outputter.write_postlude() if options.debug: - pprint(emitter.accum) + pprint(outputter.emitter) else: - emitter.serialize_to(fh) + outputter.emitter.serialize_to(fh) if __name__ == '__main__': diff --git a/src/sixtypical/outputter.py b/src/sixtypical/outputter.py index b6cbbfc..b3828ef 100644 --- a/src/sixtypical/outputter.py +++ b/src/sixtypical/outputter.py @@ -4,49 +4,71 @@ from sixtypical.emitter import Emitter, Byte, Word class Outputter(object): - def __init__(self, output_format): - self.output_format = output_format - if output_format == 'raw': - self.start_addr = 0x0000 - self.prelude = [] - elif output_format == 'prg': - self.start_addr = 0xc000 - self.prelude = [] - elif output_format == 'c64-basic-prg': - self.start_addr = 0x0801 - self.prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32, - 0x30, 0x36, 0x31, 0x00, 0x00, 0x00] - elif output_format == 'vic20-basic-prg': - self.start_addr = 0x1001 - self.prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34, - 0x31, 0x30, 0x39, 0x00, 0x00, 0x00] - elif output_format == 'atari2600-cart': - self.start_addr = 0xf000 - self.prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9, - 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb] - else: - raise NotImplementedError("Unknown output format: {}".format(output_format)) + def __init__(self, fh, start_addr=None): + self.start_addr = self.__class__.start_addr + if start_addr is not None: + self.start_addr = start_addr + self.prelude = self.__class__.prelude + self.fh = fh + self.emitter = Emitter(self.start_addr) - def set_start_addr(self, start_addr): - self.start_addr = start_addr + def write_header(self): + pass - def write_prelude(self, fh): + def write_prelude(self): + self.write_header() + for byte in self.prelude: + self.emitter.emit(Byte(byte)) + def write_postlude(self): + pass + + +class RawOutputter(Outputter): + start_addr = 0x0000 + prelude = [] + + +class PrgOutputter(Outputter): + start_addr = 0xc000 + prelude = [] + + def write_header(self): # If we are outputting a .PRG, we output the load address first. # We don't use the Emitter for this b/c not part of addr space. - if self.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'): - fh.write(bytearray(Word(self.start_addr).serialize(0))) + self.fh.write(bytearray(Word(self.start_addr).serialize(0))) - emitter = Emitter(self.start_addr) - for byte in self.prelude: - emitter.emit(Byte(byte)) - return emitter +class C64BasicPrgOutputter(PrgOutputter): + start_addr = 0x0801 + prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32, + 0x30, 0x36, 0x31, 0x00, 0x00, 0x00] - def write_postlude(self, emitter): + +class Vic20BasicPrgOutputter(PrgOutputter): + start_addr = 0x1001 + prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34, + 0x31, 0x30, 0x39, 0x00, 0x00, 0x00] + + +class Atari2600CartOutputter(Outputter): + start_addr = 0xf000 + prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9, + 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb] + + def write_postlude(self): # If we are outputting a cartridge with boot and BRK address # at the end, pad to ROM size minus 4 bytes, and emit addresses. - if self.output_format == 'atari2600-cart': - emitter.pad_to_size(4096 - 4) - emitter.emit(Word(self.start_addr)) - emitter.emit(Word(self.start_addr)) + self.emitter.pad_to_size(4096 - 4) + self.emitter.emit(Word(self.start_addr)) + self.emitter.emit(Word(self.start_addr)) + + +def outputter_class_for(output_format): + return { + 'raw': RawOutputter, + 'prg': PrgOutputter, + 'c64-basic-prg': C64BasicPrgOutputter, + 'vic20-basic-prg': Vic20BasicPrgOutputter, + 'atari2600-cart': Atari2600CartOutputter, + }[output_format] From 0593ce6a669e6e0e4eecfe5f8d08d54ec7a19b1b Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 09:26:11 +0100 Subject: [PATCH 16/45] Move merge_programs() out of main. Handle missing -o options. --- bin/sixtypical | 17 ++--------------- src/sixtypical/parser.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/bin/sixtypical b/bin/sixtypical index 9b2d2cd..eb1867c 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -18,25 +18,12 @@ from pprint import pprint import sys import traceback -from sixtypical.parser import Parser, ParsingContext +from sixtypical.parser import Parser, ParsingContext, merge_programs from sixtypical.analyzer import Analyzer from sixtypical.outputter import outputter_class_for from sixtypical.compiler import Compiler -def merge_programs(programs): - """Assumes that the programs do not have any conflicts.""" - - from sixtypical.ast import Program - - full = Program(1, defns=[], routines=[]) - for p in programs: - full.defns.extend(p.defns) - full.routines.extend(p.routines) - - return full - - def process_input_files(filenames, options): context = ParsingContext() @@ -76,7 +63,7 @@ def process_input_files(filenames, options): compilation_roster = fa.serialize() dump(compilation_roster) - if options.analyze_only: + if options.analyze_only or options.output is None: return start_addr = None diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 8cee87a..3b87fa2 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -479,3 +479,17 @@ class Parser(object): return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest) else: self.syntax_error('bad opcode "%s"' % self.scanner.token) + + +# - - - - + + +def merge_programs(programs): + """Assumes that the programs do not have any conflicts.""" + + full = Program(1, defns=[], routines=[]) + for p in programs: + full.defns.extend(p.defns) + full.routines.extend(p.routines) + + return full From b63bcfcd2bf18864b1c80740e6f9fcfc1eb3f5dc Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 09:36:51 +0100 Subject: [PATCH 17/45] Begin introducing shortcut syntax for nested `save`s. --- src/sixtypical/analyzer.py | 16 ++++++++++------ tests/SixtyPical Analysis.md | 14 ++++++++++++++ tests/SixtyPical Compilation.md | 22 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 53857a5..df7b3da 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -771,17 +771,21 @@ class Analyzer(object): context.set_writeable(instr.dest) def analyze_save(self, instr, context): - if len(instr.locations) != 1: - raise NotImplementedError("Only 1 location in save is supported right now") - location = instr.locations[0] - self.assert_type(TYPE_BYTE, location) + batons = [] + for location in instr.locations: + self.assert_type(TYPE_BYTE, location) + baton = context.extract(location) + batons.append(baton) - baton = context.extract(location) self.analyze_block(instr.block, context) if context.encountered_gotos(): raise IllegalJumpError(instr, instr) - context.re_introduce(baton) + for location in reversed(instr.locations): + baton = batons.pop() + context.re_introduce(baton) + + # FIXME check if A needs to be the outer thing that is saved, I think it does. if location == REG_A: pass else: diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index b4b0b11..7dd97b1 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -2107,6 +2107,20 @@ first in a nested series of `save`s. | } = ok +There is a shortcut syntax for a nested series of `save`s. + + | routine main + | inputs a + | outputs a + | trashes z, n + | { + | save a, x { + | ld a, 0 + | ld x, 1 + | } + | } + = ok + Not just registers, but also user-defined locations can be saved. | byte foo diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 7070374..ee0da72 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -601,6 +601,28 @@ Compiling `save`. = $0816 PLA = $0817 RTS +Compiling `save` with shortcut syntax. + + | routine main + | inputs a + | outputs a + | trashes z, n + | { + | save a, x { + | ld a, 0 + | ld x, 1 + | } + | } + = $080D PHA + = $080E TXA + = $080F PHA + = $0810 LDA #$00 + = $0812 LDX #$01 + = $0814 PLA + = $0815 TAX + = $0816 PLA + = $0817 RTS + Compiling `save` on a user-defined location. | byte foo From 19461bc2056292ec523e36032dedb18e0d43238c Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 11:27:42 +0100 Subject: [PATCH 18/45] Support `save X, Y, Z {}` as a shortcut syntax for nested `save`s. --- HISTORY.md | 5 +++- TODO.md | 14 ---------- src/sixtypical/compiler.py | 53 +++++++++++++++++++++----------------- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index cf902e3..7d7b22f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,9 +4,12 @@ History of SixtyPical 0.17 ---- +* `save X, Y, Z { }` now allowed as a shortcut for nested `save`s. * Split TODO off into own file. * `sixtypical` no longer writes the compiled binary to standard - output. The `--output` command-line argument should be given. + output. The `--output` command-line argument should be given + to get a compiled binary; otherwise only analysis is run. +* Internal cleanups, including a hierarchy of `Outputters`. * All tests pass when `sixtypical` is run under Python 3.5.2. 0.16 diff --git a/TODO.md b/TODO.md index 7e8d116..ebdff52 100644 --- a/TODO.md +++ b/TODO.md @@ -16,20 +16,6 @@ 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. -### Save multiple values in single block - -As a shortcut for the idiom - - save a { save var { - ... - } } - -allow - - save a, var { - ... - } - ### Save values to other-than-the-stack Allow diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index faed525..3061443 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -618,27 +618,32 @@ class Compiler(object): self.emitter.emit(CLI()) def compile_save(self, instr): - location = instr.locations[0] - if location == REG_A: - self.emitter.emit(PHA()) - self.compile_block(instr.block) - self.emitter.emit(PLA()) - elif location == REG_X: - self.emitter.emit(TXA()) - self.emitter.emit(PHA()) - self.compile_block(instr.block) - self.emitter.emit(PLA()) - self.emitter.emit(TAX()) - elif location == REG_Y: - self.emitter.emit(TYA()) - self.emitter.emit(PHA()) - self.compile_block(instr.block) - self.emitter.emit(PLA()) - self.emitter.emit(TAY()) - else: - src_label = self.get_label(location.name) - self.emitter.emit(LDA(Absolute(src_label))) - self.emitter.emit(PHA()) - self.compile_block(instr.block) - self.emitter.emit(PLA()) - self.emitter.emit(STA(Absolute(src_label))) + for location in instr.locations: + if location == REG_A: + self.emitter.emit(PHA()) + elif location == REG_X: + self.emitter.emit(TXA()) + self.emitter.emit(PHA()) + elif location == REG_Y: + self.emitter.emit(TYA()) + self.emitter.emit(PHA()) + else: + src_label = self.get_label(location.name) + self.emitter.emit(LDA(Absolute(src_label))) + self.emitter.emit(PHA()) + + self.compile_block(instr.block) + + for location in reversed(instr.locations): + if location == REG_A: + self.emitter.emit(PLA()) + elif location == REG_X: + self.emitter.emit(PLA()) + self.emitter.emit(TAX()) + elif location == REG_Y: + self.emitter.emit(PLA()) + self.emitter.emit(TAY()) + else: + src_label = self.get_label(location.name) + self.emitter.emit(PLA()) + self.emitter.emit(STA(Absolute(src_label))) From e7674c44ce53dea70e4f4b642a64597e3908556e Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 11:33:24 +0100 Subject: [PATCH 19/45] Confirm constraint on `save`ing `a`. --- src/sixtypical/analyzer.py | 3 ++- tests/SixtyPical Analysis.md | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index df7b3da..1e69965 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -785,7 +785,8 @@ class Analyzer(object): baton = batons.pop() context.re_introduce(baton) - # FIXME check if A needs to be the outer thing that is saved, I think it does. + # We do this check outside the loop, because A is only preserved + # if it is the outermost thing being `save`d. if location == REG_A: pass else: diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 7dd97b1..0caecc8 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -2121,6 +2121,20 @@ There is a shortcut syntax for a nested series of `save`s. | } = ok +`a` is only preserved if it is the outermost thing `save`d. + + | routine main + | inputs a + | outputs a + | trashes z, n + | { + | save x, a { + | ld a, 0 + | ld x, 1 + | } + | } + ? UnmeaningfulOutputError: a + Not just registers, but also user-defined locations can be saved. | byte foo From c73590f88c87c0356f6232edeed5e8c811ad7c92 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 12:24:07 +0100 Subject: [PATCH 20/45] Begin refactoring how the ParsingContext is used by the Parser. --- src/sixtypical/parser.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 3b87fa2..fdd47a5 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -33,7 +33,7 @@ class ParsingContext(object): def __str__(self): return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts) - def lookup(self, name): + def fetch(self, name): if name in self.statics: return self.statics[name].model if name in self.symbols: @@ -51,11 +51,16 @@ class Parser(object): self.scanner.syntax_error(msg) def lookup(self, name): - model = self.context.lookup(name) + model = self.context.fetch(name) if model is None: self.syntax_error('Undefined symbol "{}"'.format(name)) return model + def declare(self, name, symentry): + if self.context.fetch(name): + self.syntax_error('Symbol "%s" already declared' % name) + self.context.symbols[name] = symentry + # --- grammar productions def program(self): @@ -70,10 +75,7 @@ class Parser(object): typenames.extend(self.context.typedefs.keys()) while self.scanner.on(*typenames): defn = self.defn() - name = defn.name - if self.context.lookup(name): - self.syntax_error('Symbol "%s" already declared' % name) - self.context.symbols[name] = SymEntry(defn, defn.location) + self.declare(defn.name, SymEntry(defn, defn.location)) defns.append(defn) while self.scanner.on('define', 'routine'): if self.scanner.consume('define'): @@ -83,9 +85,7 @@ class Parser(object): else: routine = self.legacy_routine() name = routine.name - if self.context.lookup(name): - self.syntax_error('Symbol "%s" already declared' % name) - self.context.symbols[name] = SymEntry(routine, routine.location) + self.declare(name, SymEntry(routine, routine.location)) routines.append(routine) self.scanner.check_type('EOF') @@ -302,7 +302,7 @@ class Parser(object): c = {} for defn in statics: name = defn.name - if self.context.lookup(name): + if self.context.fetch(name): self.syntax_error('Symbol "%s" already declared' % name) c[name] = SymEntry(defn, defn.location) return c @@ -334,7 +334,7 @@ class Parser(object): elif forward: name = self.scanner.token self.scanner.scan() - loc = self.context.lookup(name) + loc = self.context.fetch(name) if loc is not None: return loc else: From 5bad7ff576140441a348b71e0a3f061237f572a2 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 12:43:55 +0100 Subject: [PATCH 21/45] Another conversion away from self.context.fetch to self.declare. --- src/sixtypical/parser.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index fdd47a5..9cd7311 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -56,10 +56,16 @@ class Parser(object): self.syntax_error('Undefined symbol "{}"'.format(name)) return model - def declare(self, name, symentry): + def declare(self, name, symentry, static=False): if self.context.fetch(name): self.syntax_error('Symbol "%s" already declared' % name) - self.context.symbols[name] = symentry + if static: + self.context.statics[name] = symentry + else: + self.context.symbols[name] = symentry + + def clear_statics(self): + self.context.statics = {} # --- grammar productions @@ -286,9 +292,11 @@ class Parser(object): else: statics = self.statics() - self.context.statics = self.compose_statics_dict(statics) + self.clear_statics() + for defn in statics: + self.declare(defn.name, SymEntry(defn, defn.location), static=True) block = self.block() - self.context.statics = {} + self.clear_statics() addr = None location = LocationRef(type_, name) @@ -298,15 +306,6 @@ class Parser(object): location=location, statics=statics ) - def compose_statics_dict(self, statics): - c = {} - for defn in statics: - name = defn.name - if self.context.fetch(name): - self.syntax_error('Symbol "%s" already declared' % name) - c[name] = SymEntry(defn, defn.location) - return c - def labels(self): accum = [] accum.append(self.label()) From 7c9f76c0071b9f12f5e940379ee6c344d066e4de Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 13:03:57 +0100 Subject: [PATCH 22/45] Removing support for it is one thing, updating all tests another. --- src/sixtypical/parser.py | 33 +-------- tests/SixtyPical Syntax.md | 147 +++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 92 deletions(-) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 9cd7311..bd68faa 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -83,14 +83,10 @@ class Parser(object): defn = self.defn() self.declare(defn.name, SymEntry(defn, defn.location)) defns.append(defn) - while self.scanner.on('define', 'routine'): - if self.scanner.consume('define'): - name = self.scanner.token - self.scanner.scan() - routine = self.routine(name) - else: - routine = self.legacy_routine() - name = routine.name + while self.scanner.consume('define'): + name = self.scanner.token + self.scanner.scan() + routine = self.routine(name) self.declare(name, SymEntry(routine, routine.location)) routines.append(routine) self.scanner.check_type('EOF') @@ -258,27 +254,6 @@ class Parser(object): trashes = set(self.labels()) return (inputs, outputs, trashes) - def legacy_routine(self): - self.scanner.expect('routine') - name = self.scanner.token - self.scanner.scan() - (inputs, outputs, trashes) = self.constraints() - type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes) - if self.scanner.consume('@'): - self.scanner.check_type('integer literal') - block = None - addr = int(self.scanner.token) - self.scanner.scan() - else: - block = self.block() - addr = None - location = LocationRef(type_, name) - return Routine( - self.scanner.line_number, - name=name, block=block, addr=addr, - location=location - ) - def routine(self, name): type_ = self.defn_type() if not isinstance(type_, RoutineType): diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index e2ef953..f8e71f8 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -16,7 +16,7 @@ but not necessarily sensible programs. Rudimentary program. - | routine main { + | define main routine { | ld a, 0 | add a, 1 | } @@ -26,7 +26,7 @@ Program with comments. | // Welcome to my program. | - | routine main { + | define main routine { | ld a, 0 | add a, 1 // We are adding the thing. | sub a, 1 @@ -40,7 +40,7 @@ Program with comments. Hex literals. - | routine main { + | define main routine { | ld a, $ff | add a, $01 | } @@ -48,7 +48,7 @@ Hex literals. Syntax error. - | routine foo ( + | define foo routine ( | ld a, 0 | add a, 1 | ) @@ -65,12 +65,12 @@ Another syntax error. Extern routines - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 | - | routine chrin + | define chrin routine | outputs a | trashes x | @ 65487 @@ -78,7 +78,7 @@ Extern routines Trash. - | routine main { + | define main routine { | trash a | trash n | } @@ -86,7 +86,7 @@ Trash. `nop`. - | routine main + | define main routine | { | nop | } @@ -94,7 +94,7 @@ Trash. If with not - | routine foo { + | define foo routine { | ld y, 0 | cmp y, 10 | if not z { @@ -106,7 +106,7 @@ If with not Repeat loop - | routine foo { + | define foo routine { | ld y, 0 | repeat { | inc y @@ -117,7 +117,7 @@ Repeat loop "While" loop - | routine foo inputs y { + | define foo routine inputs y { | repeat { | cmp y, 10 | if not z { @@ -129,7 +129,7 @@ Repeat loop Repeat forever - | routine foo inputs y { + | define foo routine inputs y { | repeat { | inc y | } forever @@ -138,7 +138,7 @@ Repeat forever Repeat with not - | routine foo inputs y { + | define foo routine inputs y { | repeat { | inc y | } until not z @@ -149,7 +149,7 @@ Basic "open-faced for" loops, up and down. | byte table[256] tab | - | routine foo trashes a, x, c, z, v { + | define foo routine trashes a, x, c, z, v { | ld x, 0 | for x up to 15 { | ld a, tab + x @@ -163,7 +163,7 @@ Basic "open-faced for" loops, up and down. Other blocks. - | routine main trashes a, x, c, z, v { + | define main routine trashes a, x, c, z, v { | with interrupts off { | save a, x, c { | ld a, 0 @@ -183,7 +183,7 @@ User-defined memory addresses of different types. | buffer[2048] buf | pointer ptr | - | routine main { + | define main routine { | } = ok @@ -193,7 +193,7 @@ Tables of different types and some operations on them. | word table[256] wmany | vector (routine trashes a) table[256] vmany | - | routine main { + | define main routine { | ld x, 0 | ld a, 0 | st off, c @@ -215,7 +215,7 @@ greater than 0 and less than or equal to 256. | word table[512] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -227,7 +227,7 @@ greater than 0 and less than or equal to 256. | word table[0] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -239,7 +239,7 @@ greater than 0 and less than or equal to 256. | word table[48] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -256,7 +256,7 @@ Typedefs of different types. | typedef routine trashes a game_routine | vector game_routine start_game | - | routine main { + | define main routine { | } = ok @@ -265,7 +265,7 @@ Can't have two typedefs with the same name. | typedef byte frank | typedef word frank | - | routine main { + | define main routine { | } ? SyntaxError @@ -280,7 +280,7 @@ Constants. | | byte lark: lives | - | routine main { + | define main routine { | ld a, lives | } = ok @@ -290,7 +290,7 @@ Can't have two constants with the same name. | const w1 1000 | const w1 word 0 | - | routine main { + | define main routine { | } ? SyntaxError @@ -298,7 +298,7 @@ Explicit memory address. | byte screen @ 1024 | - | routine main { + | define main routine { | ld a, 100 | st a, screen | shl screen @@ -310,7 +310,7 @@ Initialized memory locations. | byte lives : 3 | - | routine main { + | define main routine { | ld a, lives | st a, lives | } @@ -320,7 +320,7 @@ Cannot have both initial value and explicit address. | byte screen : 3 @ 1024 | - | routine main { + | define main routine { | ld a, lives | st a, lives | } @@ -333,7 +333,7 @@ User-defined locations of other types. | word r2 @ 60000 | word r3 : 2000 | - | routine main { + | define main routine { | } = ok @@ -341,15 +341,15 @@ Initialized byte table, initialized with ASCII string. | byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?" | - | routine main { + | define main routine { | } = ok Can't initialize anything but a byte table with a string. - | word message : "WHAT DO YOU WANT TO DO NEXT?" + | word message : "OUCH! WHAT DO YOU DO?" | - | routine main { + | define main routine { | } ? SyntaxError @@ -357,13 +357,13 @@ Initialized byte table, initialized with list of bytes. | byte table[8] charmap : 0, 255, 129, 192, 0, 1, 2, 4 | - | routine main { + | define main routine { | } = ok Can't access an undeclared memory location. - | routine main { + | define main routine { | ld a, 0 | st a, lives | } @@ -374,7 +374,7 @@ Can't define two memory locations with the same name. | byte lives | byte lives | - | routine main { + | define main routine { | ld a, 0 | st a, lives | } @@ -384,19 +384,19 @@ Can't shadow the name of a register or a flag. | byte a | - | routine main { + | define main routine { | } ? SyntaxError | byte z | - | routine main { + | define main routine { | } ? SyntaxError Can't call routine that hasn't been defined. - | routine main { + | define main routine { | ld x, 0 | ld y, 1 | call up @@ -408,14 +408,14 @@ And you can't call a non-routine. | byte up | - | routine main { + | define main routine { | ld x, 0 | ld y, 1 | call up | } ? SyntaxError - | routine main { + | define main routine { | ld x, 0 | ld y, 1 | call x @@ -424,24 +424,24 @@ And you can't call a non-routine. But you can call a routine that is yet to be defined, further on. - | routine main { + | define main routine { | ld x, 0 | ld y, 1 | call up | call up | } - | routine up { + | define up routine { | ld a, 0 | } = ok Can't define two routines with the same name. - | routine main { + | define main routine { | inc x | inc y | } - | routine main { + | define main routine { | ld x, 0 | ld y, 1 | } @@ -451,7 +451,7 @@ Declaring byte and word table memory location. | byte table[256] tab | - | routine main { + | define main routine { | ld x, 0 | ld y, 0 | ld a, tab + x @@ -462,7 +462,7 @@ Declaring byte and word table memory location. | word one | word table[256] many | - | routine main { + | define main routine { | ld x, 0 | copy one, many + x | copy word 0, many + x @@ -478,10 +478,10 @@ Declaring and calling a vector. | trashes a, x, z, n | cinv @ 788 | - | routine foo { + | define foo routine { | ld a, 0 | } - | routine main { + | define main routine { | with interrupts off { | copy foo, cinv | } @@ -497,7 +497,7 @@ Only vectors can be decorated with constraints like that. | trashes a, x, z, n | @ 788 | - | routine main { + | define main routine { | } ? SyntaxError @@ -509,10 +509,10 @@ Constraints set may only contain labels. | trashes a, x, z, n | cinv @ 788 | - | routine foo { + | define foo routine { | ld a, 0 | } - | routine main { + | define main routine { | with interrupts off { | copy foo, cinv | } @@ -528,10 +528,10 @@ A vector can name itself in its inputs, outputs, and trashes. | trashes a, x, z, n | cinv @ 788 | - | routine foo { + | define foo routine { | ld a, 0 | } - | routine main { + | define main routine { | with interrupts off { | copy foo, cinv | } @@ -548,50 +548,50 @@ references in the source of a `copy` instruction. | outputs cinv, x | trashes a, x, z, n | cinv @ 788 - | routine main { + | define main routine { | with interrupts off { | copy foo, cinv | } | call cinv | } - | routine foo { + | define foo routine { | ld a, 0 | } = ok goto. - | routine foo { + | define foo routine { | ld a, 0 | } - | routine main { + | define main routine { | goto foo | } = ok - | routine main { + | define main routine { | goto foo | } - | routine foo { + | define foo routine { | ld a, 0 | } = ok | vector routine foo | - | routine main { + | define main routine { | goto foo | } = ok - | routine main { + | define main routine { | goto foo | } ? SyntaxError | byte foo | - | routine main { + | define main routine { | goto foo | } ? SyntaxError @@ -603,7 +603,7 @@ Buffers and pointers. | pointer ptrb | byte foo | - | routine main { + | define main routine { | copy ^buf, ptr | copy 123, [ptr] + y | copy [ptr] + y, foo @@ -629,7 +629,28 @@ Routines can be defined in a new style. | inc x | } | - | routine main + | define main routine + | outputs vec + | trashes a, z, n + | { + | copy foo, vec + | } + = ok + + | typedef routine + | inputs x + | outputs x + | trashes z, n + | routine_type + | + | vector routine_type vec + | + | define foo routine_type + | { + | inc x + | } + | + | define main routine | outputs vec | trashes a, z, n | { From 33d5093e5a626aa58f77fd1cfc7eaae25953d3b2 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 13:20:18 +0100 Subject: [PATCH 23/45] 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 24/45] 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 25/45] 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 26/45] 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 27/45] 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 28/45] 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 29/45] 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 30/45] 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 31/45] 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 32/45] 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 33/45] 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 34/45] 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. From da44e1b22e92ab9c191f30dbe59117fc5cf00873 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 18:49:39 +0100 Subject: [PATCH 35/45] Many, many define main routine, define foo routine, etc. --- tests/SixtyPical Analysis.md | 470 +++++++++++++++++------------------ 1 file changed, 235 insertions(+), 235 deletions(-) diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 2e06530..f992863 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -15,7 +15,7 @@ static analysis rules. Routines must declare their inputs, outputs, and memory locations they trash. - | routine up + | define up routine | inputs a | outputs a | trashes c, z, v, n @@ -27,7 +27,7 @@ Routines must declare their inputs, outputs, and memory locations they trash. Routines may not declare a memory location to be both an output and trashed. - | routine main + | define main routine | outputs a | trashes a | { @@ -37,14 +37,14 @@ Routines may not declare a memory location to be both an output and trashed. If a routine declares it outputs a location, that location should be initialized. - | routine main + | define main routine | outputs a, x, z, n | { | ld x, 0 | } ? UnmeaningfulOutputError: a - | routine main + | define main routine | inputs a | outputs a | { @@ -54,14 +54,14 @@ If a routine declares it outputs a location, that location should be initialized If a routine declares it outputs a location, that location may or may not have been initialized. Trashing is mainly a signal to the caller. - | routine main + | define main routine | trashes x, z, n | { | ld x, 0 | } = ok - | routine main + | define main routine | trashes x, z, n | { | } @@ -69,20 +69,20 @@ been initialized. Trashing is mainly a signal to the caller. If a routine modifies a location, it needs to either output it or trash it. - | routine main + | define main routine | { | ld x, 0 | } ? ForbiddenWriteError: x - | routine main + | define main routine | outputs x, z, n | { | ld x, 0 | } = ok - | routine main + | define main routine | trashes x, z, n | { | ld x, 0 @@ -91,14 +91,14 @@ If a routine modifies a location, it needs to either output it or trash it. This is true regardless of whether it's an input or not. - | routine main + | define main routine | inputs x | { | ld x, 0 | } ? ForbiddenWriteError: x - | routine main + | define main routine | inputs x | outputs x, z, n | { @@ -106,7 +106,7 @@ This is true regardless of whether it's an input or not. | } = ok - | routine main + | define main routine | inputs x | trashes x, z, n | { @@ -116,20 +116,20 @@ This is true regardless of whether it's an input or not. If a routine trashes a location, this must be declared. - | routine foo + | define foo routine | trashes x | { | trash x | } = ok - | routine foo + | define foo routine | { | trash x | } ? ForbiddenWriteError: x - | routine foo + | define foo routine | outputs x | { | trash x @@ -138,39 +138,39 @@ If a routine trashes a location, this must be declared. If a routine causes a location to be trashed, this must be declared in the caller. - | routine trash_x + | define trash_x routine | trashes x, z, n | { | ld x, 0 | } | - | routine foo + | define foo routine | trashes x, z, n | { | call trash_x | } = ok - | routine trash_x + | define trash_x routine | trashes x, z, n | { | ld x, 0 | } | - | routine foo + | define foo routine | trashes z, n | { | call trash_x | } ? ForbiddenWriteError: x - | routine trash_x + | define trash_x routine | trashes x, z, n | { | ld x, 0 | } | - | routine foo + | define foo routine | outputs x | trashes z, n | { @@ -185,7 +185,7 @@ If a routine reads or writes a user-define memory location, it needs to declare | word w1 @ 60001 | word w2 : 2000 | - | routine main + | define main routine | inputs b1, w1 | outputs b2, w2 | trashes a, z, n @@ -202,14 +202,14 @@ You can't call a non-routine. | byte up | - | routine main outputs x, y trashes z, n { + | define main routine outputs x, y trashes z, n { | ld x, 0 | ld y, 1 | call up | } ? TypeMismatchError: up - | routine main outputs x, y trashes z, n { + | define main routine outputs x, y trashes z, n { | ld x, 0 | ld y, 1 | call x @@ -220,7 +220,7 @@ Nor can you goto a non-routine. | byte foo | - | routine main { + | define main routine { | goto foo | } ? TypeMismatchError: foo @@ -229,7 +229,7 @@ Nor can you goto a non-routine. Can't `ld` from a memory location that isn't initialized. - | routine main + | define main routine | inputs a, x | trashes a, z, n | { @@ -237,7 +237,7 @@ Can't `ld` from a memory location that isn't initialized. | } = ok - | routine main + | define main routine | inputs a | trashes a | { @@ -247,14 +247,14 @@ Can't `ld` from a memory location that isn't initialized. Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). - | routine main + | define main routine | trashes a, z, n | { | ld a, 0 | } = ok - | routine main + | define main routine | outputs a | trashes z, n | { @@ -262,7 +262,7 @@ Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). | } = ok - | routine main + | define main routine | outputs z, n | trashes a | { @@ -270,14 +270,14 @@ Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). | } = ok - | routine main + | define main routine | trashes z, n | { | ld a, 0 | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes a, n | { | ld a, 0 @@ -288,7 +288,7 @@ Can't `ld` a `word` type. | word foo | - | routine main + | define main routine | inputs foo | trashes a, n, z | { @@ -301,7 +301,7 @@ Can't `ld` a `word` type. Can't `st` from a memory location that isn't initialized. | byte lives - | routine main + | define main routine | inputs x | trashes lives | { @@ -310,7 +310,7 @@ Can't `st` from a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | trashes x, lives | { | st x, lives @@ -320,7 +320,7 @@ Can't `st` from a memory location that isn't initialized. Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). | byte lives - | routine main + | define main routine | trashes lives | { | st 0, lives @@ -328,7 +328,7 @@ Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | outputs lives | { | st 0, lives @@ -336,7 +336,7 @@ Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | inputs lives | { | st 0, lives @@ -347,7 +347,7 @@ Can't `st` a `word` type. | word foo | - | routine main + | define main routine | outputs foo | trashes a, n, z | { @@ -363,7 +363,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -376,7 +376,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -389,7 +389,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -402,7 +402,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -417,7 +417,7 @@ The index must be initialized. | byte one | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -430,7 +430,7 @@ Reading from a table, you must use an index. | byte one | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -442,7 +442,7 @@ Reading from a table, you must use an index. | byte one | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -454,7 +454,7 @@ Reading from a table, you must use an index. | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -467,7 +467,7 @@ Reading from a table, you must use an index. | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -480,7 +480,7 @@ Reading from a table, you must use an index. | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -494,7 +494,7 @@ The index must be initialized. | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -507,7 +507,7 @@ There are other operations you can do on tables. (1/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z, v @@ -525,7 +525,7 @@ There are other operations you can do on tables. (2/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z @@ -542,7 +542,7 @@ There are other operations you can do on tables. (3/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z @@ -562,7 +562,7 @@ Copying to and from a word table. | word one | word table[256] many | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -576,7 +576,7 @@ Copying to and from a word table. | word one | word table[256] many | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -589,7 +589,7 @@ Copying to and from a word table. | word one | word table[256] many | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -604,7 +604,7 @@ You can also copy a literal word to a word table. | word table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -628,7 +628,7 @@ constant value falls inside or outside the range of the table. | byte table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -641,7 +641,7 @@ constant value falls inside or outside the range of the table. | byte table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -653,7 +653,7 @@ constant value falls inside or outside the range of the table. | byte table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -669,7 +669,7 @@ This applies to `copy` as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs many, one | outputs many, one | trashes a, x, n, z @@ -683,7 +683,7 @@ This applies to `copy` as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs many, one | outputs many, one | trashes a, x, n, z @@ -696,7 +696,7 @@ This applies to `copy` as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs many, one | outputs many, one | trashes a, x, n, z @@ -714,7 +714,7 @@ a table. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -731,7 +731,7 @@ Test for "clipping", but not enough. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -749,7 +749,7 @@ no longer be guaranteed. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -766,7 +766,7 @@ no longer be guaranteed. Can't `add` from or to a memory location that isn't initialized. - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -777,7 +777,7 @@ Can't `add` from or to a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -788,7 +788,7 @@ Can't `add` from or to a memory location that isn't initialized. ? UnmeaningfulReadError: lives | byte lives - | routine main + | define main routine | inputs lives | outputs a | trashes c, z, v, n @@ -800,7 +800,7 @@ Can't `add` from or to a memory location that isn't initialized. Can't `add` to a memory location that isn't writeable. - | routine main + | define main routine | inputs a | trashes c | { @@ -812,7 +812,7 @@ Can't `add` to a memory location that isn't writeable. You can `add` a word constant to a word memory location. | word score - | routine main + | define main routine | inputs a, score | outputs score | trashes a, c, z, v, n @@ -825,7 +825,7 @@ You can `add` a word constant to a word memory location. `add`ing a word constant to a word memory location trashes `a`. | word score - | routine main + | define main routine | inputs a, score | outputs score, a | trashes c, z, v, n @@ -838,7 +838,7 @@ You can `add` a word constant to a word memory location. To be sure, `add`ing a word constant to a word memory location trashes `a`. | word score - | routine main + | define main routine | inputs score | outputs score | trashes c, z, v, n @@ -852,7 +852,7 @@ You can `add` a word memory location to another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes a, c, z, v, n @@ -866,7 +866,7 @@ You can `add` a word memory location to another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes c, z, v, n @@ -880,7 +880,7 @@ You can `add` a word memory location, or a constant, to a pointer. | pointer ptr | word delta - | routine main + | define main routine | inputs ptr, delta | outputs ptr | trashes a, c, z, v, n @@ -895,7 +895,7 @@ You can `add` a word memory location, or a constant, to a pointer. | pointer ptr | word delta - | routine main + | define main routine | inputs ptr, delta | outputs ptr | trashes c, z, v, n @@ -910,7 +910,7 @@ You can `add` a word memory location, or a constant, to a pointer. Can't `sub` from or to a memory location that isn't initialized. - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -921,7 +921,7 @@ Can't `sub` from or to a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -932,7 +932,7 @@ Can't `sub` from or to a memory location that isn't initialized. ? UnmeaningfulReadError: lives | byte lives - | routine main + | define main routine | inputs lives | outputs a | trashes c, z, v, n @@ -944,7 +944,7 @@ Can't `sub` from or to a memory location that isn't initialized. Can't `sub` to a memory location that isn't writeable. - | routine main + | define main routine | inputs a | trashes c | { @@ -956,7 +956,7 @@ Can't `sub` to a memory location that isn't writeable. You can `sub` a word constant from a word memory location. | word score - | routine main + | define main routine | inputs a, score | outputs score | trashes a, c, z, v, n @@ -969,7 +969,7 @@ You can `sub` a word constant from a word memory location. `sub`ing a word constant from a word memory location trashes `a`. | word score - | routine main + | define main routine | inputs a, score | outputs score, a | trashes c, z, v, n @@ -983,7 +983,7 @@ You can `sub` a word memory location from another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes a, c, z, v, n @@ -997,7 +997,7 @@ You can `sub` a word memory location from another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes c, z, v, n @@ -1011,7 +1011,7 @@ You can `sub` a word memory location from another word memory location. Location must be initialized and writeable. - | routine main + | define main routine | outputs x | trashes z, n | { @@ -1019,7 +1019,7 @@ Location must be initialized and writeable. | } ? UnmeaningfulReadError: x - | routine main + | define main routine | inputs x | trashes z, n | { @@ -1027,7 +1027,7 @@ Location must be initialized and writeable. | } ? ForbiddenWriteError: x - | routine main + | define main routine | inputs x | outputs x | trashes z, n @@ -1040,7 +1040,7 @@ Can't `inc` a `word` type. | word foo | - | routine main + | define main routine | inputs foo | outputs foo | trashes z, n @@ -1053,7 +1053,7 @@ Can't `inc` a `word` type. Location must be initialized and writeable. - | routine main + | define main routine | outputs x | trashes z, n | { @@ -1061,7 +1061,7 @@ Location must be initialized and writeable. | } ? UnmeaningfulReadError: x - | routine main + | define main routine | inputs x | trashes z, n | { @@ -1069,7 +1069,7 @@ Location must be initialized and writeable. | } ? ForbiddenWriteError: x - | routine main + | define main routine | inputs x | outputs x | trashes z, n @@ -1082,7 +1082,7 @@ Can't `dec` a `word` type. | word foo | - | routine main + | define main routine | inputs foo | outputs foo | trashes z, n @@ -1095,7 +1095,7 @@ Can't `dec` a `word` type. Some rudimentary tests for `cmp`. - | routine main + | define main routine | inputs a | trashes z, c, n | { @@ -1103,7 +1103,7 @@ Some rudimentary tests for `cmp`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1111,7 +1111,7 @@ Some rudimentary tests for `cmp`. | } ? ForbiddenWriteError: c - | routine main + | define main routine | trashes z, c, n | { | cmp a, 4 @@ -1122,7 +1122,7 @@ Some rudimentary tests for `cmp`. Some rudimentary tests for `and`. - | routine main + | define main routine | inputs a | outputs a, z, n | { @@ -1130,7 +1130,7 @@ Some rudimentary tests for `and`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1138,7 +1138,7 @@ Some rudimentary tests for `and`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes z, n | { | and a, 4 @@ -1149,7 +1149,7 @@ Some rudimentary tests for `and`. Some rudimentary tests for `or`. - | routine main + | define main routine | inputs a | outputs a, z, n | { @@ -1157,7 +1157,7 @@ Some rudimentary tests for `or`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1165,7 +1165,7 @@ Some rudimentary tests for `or`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes z, n | { | or a, 4 @@ -1176,7 +1176,7 @@ Some rudimentary tests for `or`. Some rudimentary tests for `xor`. - | routine main + | define main routine | inputs a | outputs a, z, n | { @@ -1184,7 +1184,7 @@ Some rudimentary tests for `xor`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1192,7 +1192,7 @@ Some rudimentary tests for `xor`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes z, n | { | xor a, 4 @@ -1204,7 +1204,7 @@ Some rudimentary tests for `xor`. Some rudimentary tests for `shl`. | byte foo - | routine main + | define main routine | inputs foo, a, c | outputs foo, a, c, z, n | { @@ -1213,7 +1213,7 @@ Some rudimentary tests for `shl`. | } = ok - | routine main + | define main routine | inputs a, c | outputs c, z, n | { @@ -1221,7 +1221,7 @@ Some rudimentary tests for `shl`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | inputs a | outputs a, c, z, n | { @@ -1234,7 +1234,7 @@ Some rudimentary tests for `shl`. Some rudimentary tests for `shr`. | byte foo - | routine main + | define main routine | inputs foo, a, c | outputs foo, a, c, z, n | { @@ -1243,7 +1243,7 @@ Some rudimentary tests for `shr`. | } = ok - | routine main + | define main routine | inputs a, c | outputs c, z, n | { @@ -1251,7 +1251,7 @@ Some rudimentary tests for `shr`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | inputs a | outputs a, c, z, n | { @@ -1263,7 +1263,7 @@ Some rudimentary tests for `shr`. Some rudimentary tests for `nop`. - | routine main + | define main routine | { | nop | } @@ -1276,14 +1276,14 @@ initialized. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | { | call foo | } @@ -1293,14 +1293,14 @@ Note that if you call a routine that trashes a location, you also trash it. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n | { | ld x, 0 @@ -1310,14 +1310,14 @@ Note that if you call a routine that trashes a location, you also trash it. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n | trashes lives | { @@ -1330,14 +1330,14 @@ You can't output a value that the thing you called trashed. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n, lives | { | ld x, 0 @@ -1349,14 +1349,14 @@ You can't output a value that the thing you called trashed. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n, lives | { | ld x, 0 @@ -1368,13 +1368,13 @@ You can't output a value that the thing you called trashed. If a routine declares outputs, they are initialized in the caller after calling it. - | routine foo + | define foo routine | outputs x, z, n | { | ld x, 0 | } | - | routine main + | define main routine | outputs a | trashes x, z, n | { @@ -1383,11 +1383,11 @@ calling it. | } = ok - | routine foo + | define foo routine | { | } | - | routine main + | define main routine | outputs a | trashes x | { @@ -1399,20 +1399,20 @@ calling it. If a routine trashes locations, they are uninitialized in the caller after calling it. - | routine foo + | define foo routine | trashes x, z, n | { | ld x, 0 | } = ok - | routine foo + | define foo routine | trashes x, z, n | { | ld x, 0 | } | - | routine main + | define main routine | outputs a | trashes x, z, n | { @@ -1429,7 +1429,7 @@ same constraints. | trashes a | @ 65490 | - | routine main + | define main routine | trashes a, z, n | { | ld a, 65 @@ -1442,7 +1442,7 @@ same constraints. | trashes a | @ 65490 | - | routine main + | define main routine | trashes a, z, n | { | call chrout @@ -1454,7 +1454,7 @@ same constraints. | trashes a | @ 65490 | - | routine main + | define main routine | trashes a, x, z, n | { | ld a, 65 @@ -1467,7 +1467,7 @@ same constraints. Trash does nothing except indicate that we do not care about the value anymore. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n @@ -1478,7 +1478,7 @@ Trash does nothing except indicate that we do not care about the value anymore. | } = ok - | routine foo + | define foo routine | inputs a | outputs a, x | trashes z, n @@ -1489,7 +1489,7 @@ Trash does nothing except indicate that we do not care about the value anymore. | } ? UnmeaningfulOutputError: a - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n @@ -1504,7 +1504,7 @@ Trash does nothing except indicate that we do not care about the value anymore. Both blocks of an `if` are analyzed. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1520,7 +1520,7 @@ Both blocks of an `if` are analyzed. If a location is initialized in one block, is must be initialized in the other as well. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1534,7 +1534,7 @@ If a location is initialized in one block, is must be initialized in the other a | } ? InconsistentInitializationError: x - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1548,7 +1548,7 @@ If a location is initialized in one block, is must be initialized in the other a | } ? InconsistentInitializationError: x - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1567,7 +1567,7 @@ initialized, either because it was set previous to the `if`, or is an input to the routine, and it is initialized in one branch, it need not be initialized in the other. - | routine foo + | define foo routine | outputs x | trashes a, z, n, c | { @@ -1582,7 +1582,7 @@ be initialized in the other. | } = ok - | routine foo + | define foo routine | inputs x | outputs x | trashes a, z, n, c @@ -1599,7 +1599,7 @@ be initialized in the other. An `if` with a single block is analyzed as if it had an empty `else` block. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1611,7 +1611,7 @@ An `if` with a single block is analyzed as if it had an empty `else` block. | } ? InconsistentInitializationError: x - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1624,7 +1624,7 @@ An `if` with a single block is analyzed as if it had an empty `else` block. | } = ok - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1641,7 +1641,7 @@ The cardinal rule for trashes in an `if` is the "union rule": if one branch trashes {`a`} and the other branch trashes {`b`} then the whole `if` statement trashes {`a`, `b`}. - | routine foo + | define foo routine | inputs a, x, z | trashes a, x | { @@ -1653,7 +1653,7 @@ trashes {`a`, `b`}. | } = ok - | routine foo + | define foo routine | inputs a, x, z | trashes a | { @@ -1665,7 +1665,7 @@ trashes {`a`, `b`}. | } ? ForbiddenWriteError: x (in foo, line 10) - | routine foo + | define foo routine | inputs a, x, z | trashes x | { @@ -1681,7 +1681,7 @@ trashes {`a`, `b`}. Repeat loop. - | routine main + | define main routine | outputs x, y, n, z, c | { | ld x, 0 @@ -1696,7 +1696,7 @@ Repeat loop. You can initialize something inside the loop that was uninitialized outside. - | routine main + | define main routine | outputs x, y, n, z, c | { | ld x, 0 @@ -1711,12 +1711,12 @@ You can initialize something inside the loop that was uninitialized outside. But you can't UNinitialize something at the end of the loop that you need initialized at the start. - | routine foo + | define foo routine | trashes y | { | } | - | routine main + | define main routine | outputs x, y, n, z, c | { | ld x, 0 @@ -1736,7 +1736,7 @@ this is an error too. | word one : 0 | word two : 0 | - | routine main + | define main routine | inputs one, two | outputs two | trashes a, z, n @@ -1749,7 +1749,7 @@ this is an error too. The body of `repeat forever` can be empty. - | routine main + | define main routine | { | repeat { | } forever @@ -1758,7 +1758,7 @@ The body of `repeat forever` can be empty. While `repeat` is most often used with `z`, it can also be used with `n`. - | routine main + | define main routine | outputs y, n, z | { | ld y, 15 @@ -1934,7 +1934,7 @@ If the range isn't known to be larger than the final value, you can't go down to You can initialize something inside the loop that was uninitialized outside. - | routine main + | define main routine | outputs x, y, n, z | trashes c | { @@ -1948,12 +1948,12 @@ You can initialize something inside the loop that was uninitialized outside. But you can't UNinitialize something at the end of the loop that you need initialized at the start of that loop. - | routine foo + | define foo routine | trashes y | { | } | - | routine main + | define main routine | outputs x, y, n, z | trashes c | { @@ -1970,7 +1970,7 @@ initialized at the start of that loop. Basic neutral test, where the `save` makes no difference. - | routine main + | define main routine | inputs a, x | outputs a, x | trashes z, n @@ -1985,7 +1985,7 @@ Basic neutral test, where the `save` makes no difference. Saving any location (other than `a`) will trash `a`. - | routine main + | define main routine | inputs a, x | outputs a, x | trashes z, n @@ -1999,7 +1999,7 @@ Saving any location (other than `a`) will trash `a`. Saving `a` does not trash anything. - | routine main + | define main routine | inputs a, x | outputs a, x | trashes z, n @@ -2015,7 +2015,7 @@ Saving `a` does not trash anything. A defined value that has been saved can be trashed inside the block. It will continue to be defined outside the block. - | routine main + | define main routine | outputs x, y | trashes a, z, n | { @@ -2032,7 +2032,7 @@ It will continue to be trashed outside the block. (Note, both x and a are unmeaningful in this test.) - | routine main + | define main routine | inputs a | outputs a, x | trashes z, n @@ -2051,7 +2051,7 @@ The known range of a value will be preserved outside the block as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -2069,7 +2069,7 @@ The known range of a value will be preserved outside the block as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -2089,7 +2089,7 @@ The known properties of a value are preserved inside the block, too. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -2108,7 +2108,7 @@ The known properties of a value are preserved inside the block, too. A value which is not output from the routine, is preserved by the routine; and can appear in a `save` exactly because a `save` preserves it. - | routine main + | define main routine | outputs y | trashes a, z, n | { @@ -2122,7 +2122,7 @@ routine; and can appear in a `save` exactly because a `save` preserves it. Because saving anything except `a` trashes `a`, a common idiom is to save `a` first in a nested series of `save`s. - | routine main + | define main routine | inputs a | outputs a | trashes z, n @@ -2138,7 +2138,7 @@ first in a nested series of `save`s. There is a shortcut syntax for a nested series of `save`s. - | routine main + | define main routine | inputs a | outputs a | trashes z, n @@ -2152,7 +2152,7 @@ There is a shortcut syntax for a nested series of `save`s. `a` is only preserved if it is the outermost thing `save`d. - | routine main + | define main routine | inputs a | outputs a | trashes z, n @@ -2168,7 +2168,7 @@ Not just registers, but also user-defined locations can be saved. | byte foo | - | routine main + | define main routine | trashes a, z, n | { | save foo { @@ -2181,7 +2181,7 @@ But only if they are bytes. | word foo | - | routine main + | define main routine | trashes a, z, n | { | save foo { @@ -2192,7 +2192,7 @@ But only if they are bytes. | byte table[16] tab | - | routine main + | define main routine | trashes a, y, z, n | { | save tab { @@ -2204,13 +2204,13 @@ But only if they are bytes. A `goto` cannot appear within a `save` block, even if it is otherwise in tail position. - | routine other + | define other routine | trashes a, z, n | { | ld a, 0 | } | - | routine main + | define main routine | trashes a, z, n | { | ld a, 1 @@ -2229,7 +2229,7 @@ A `goto` cannot appear within a `save` block, even if it is otherwise in tail po | trashes z, n | bar | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2237,7 +2237,7 @@ A `goto` cannot appear within a `save` block, even if it is otherwise in tail po | inc x | } | - | routine main + | define main routine | outputs bar | trashes a, n, z | { @@ -2256,7 +2256,7 @@ otherwise in tail position. | trashes z, n | bar | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2270,7 +2270,7 @@ otherwise in tail position. | ld a, 0 | } | - | routine main + | define main routine | trashes bar, a, n, z | { | with interrupts off { @@ -2285,7 +2285,7 @@ otherwise in tail position. Can't `copy` from a memory location that isn't initialized. | byte lives - | routine main + | define main routine | inputs x | outputs lives | trashes a, z, n @@ -2295,7 +2295,7 @@ Can't `copy` from a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | outputs lives | trashes x, a, z, n | { @@ -2306,7 +2306,7 @@ Can't `copy` from a memory location that isn't initialized. Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). | byte lives - | routine main + | define main routine | trashes lives, a, z, n | { | copy 0, lives @@ -2314,7 +2314,7 @@ Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | outputs lives | trashes a, z, n | { @@ -2323,7 +2323,7 @@ Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | inputs lives | trashes a, z, n | { @@ -2336,7 +2336,7 @@ a, z, and n are trashed, and must be declared as such. (Note, both n and z are forbidden writes in this test.) | byte lives - | routine main + | define main routine | outputs lives | { | copy 0, lives @@ -2348,7 +2348,7 @@ a, z, and n are trashed, and must not be declared as outputs. (Note, both n and a are unmeaningful outputs in this test.) | byte lives - | routine main + | define main routine | outputs lives, a, z, n | { | copy 0, lives @@ -2358,7 +2358,7 @@ a, z, and n are trashed, and must not be declared as outputs. Unless of course you subsequently initialize them. | byte lives - | routine main + | define main routine | outputs lives, a, z, n | { | copy 0, lives @@ -2371,7 +2371,7 @@ Can `copy` from a `byte` to a `byte`. | byte source : 0 | byte dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2386,7 +2386,7 @@ as the destination of a `copy`. | byte source : 0 | byte dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2400,7 +2400,7 @@ Can `copy` from a `word` to a `word`. | word source : 0 | word dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2414,7 +2414,7 @@ Can't `copy` from a `byte` to a `word`. | byte source : 0 | word dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2428,7 +2428,7 @@ Can't `copy` from a `word` to a `byte`. | word source : 0 | byte dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2459,7 +2459,7 @@ Write literal through a pointer. | buffer[2048] buf | pointer ptr | - | routine main + | define main routine | inputs buf | outputs y, buf | trashes a, z, n, ptr @@ -2475,7 +2475,7 @@ It does use `y`. | buffer[2048] buf | pointer ptr | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, z, n, ptr @@ -2491,7 +2491,7 @@ Write stored value through a pointer. | pointer ptr | byte foo | - | routine main + | define main routine | inputs foo, buf | outputs y, buf | trashes a, z, n, ptr @@ -2508,7 +2508,7 @@ Read through a pointer. | pointer ptr | byte foo | - | routine main + | define main routine | inputs buf | outputs foo | trashes a, y, z, n, ptr @@ -2525,7 +2525,7 @@ Read and write through two pointers. | pointer ptra | pointer ptrb | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, y, z, n, ptra, ptrb @@ -2544,7 +2544,7 @@ not `copy`. | pointer ptr | byte foo | - | routine main + | define main routine | inputs buf | outputs a | trashes y, z, n, ptr @@ -2562,7 +2562,7 @@ not `copy`. | pointer ptr | byte foo | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, y, z, n, ptr @@ -2585,7 +2585,7 @@ as an input to, an output of, or as a trashed value of a routine. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2593,7 +2593,7 @@ as an input to, an output of, or as a trashed value of a routine. | inc x | } | - | routine main + | define main routine | inputs foo | outputs vec | trashes a, z, n @@ -2608,7 +2608,7 @@ as an input to, an output of, or as a trashed value of a routine. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2616,7 +2616,7 @@ as an input to, an output of, or as a trashed value of a routine. | inc x | } | - | routine main + | define main routine | outputs vec, foo | trashes a, z, n | { @@ -2630,7 +2630,7 @@ as an input to, an output of, or as a trashed value of a routine. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2638,7 +2638,7 @@ as an input to, an output of, or as a trashed value of a routine. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n, foo | { @@ -2663,7 +2663,7 @@ If the vector and the routine have the very same signature, that's not an error. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2672,7 +2672,7 @@ If the vector and the routine have the very same signature, that's not an error. | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2690,7 +2690,7 @@ implementation doesn't actually read it.) | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2699,7 +2699,7 @@ implementation doesn't actually read it.) | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2715,7 +2715,7 @@ If the vector fails to take an input that the routine takes, that's an error. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2724,7 +2724,7 @@ If the vector fails to take an input that the routine takes, that's an error. | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2743,7 +2743,7 @@ output.) | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2752,7 +2752,7 @@ output.) | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2768,7 +2768,7 @@ If the vector fails to produce an output that the routine produces, that's an er | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2777,7 +2777,7 @@ If the vector fails to produce an output that the routine produces, that's an er | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2793,7 +2793,7 @@ If the vector fails to trash something the routine trashes, that's an error. | trashes z | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2802,7 +2802,7 @@ If the vector fails to trash something the routine trashes, that's an error. | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2821,7 +2821,7 @@ but it doesn't know that.) | trashes a, z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2830,7 +2830,7 @@ but it doesn't know that.) | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2848,7 +2848,7 @@ Routines are read-only. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2856,7 +2856,7 @@ Routines are read-only. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2870,11 +2870,11 @@ Indirect call. | outputs x trashes z, n | foo | - | routine bar outputs x trashes z, n { + | define bar routine outputs x trashes z, n { | ld x, 200 | } | - | routine main outputs x, foo trashes a, z, n { + | define main routine outputs x, foo trashes a, z, n { | copy bar, foo | call foo | } @@ -3071,13 +3071,13 @@ vector says it does. | outputs x | trashes a, z, n foo | - | routine bar + | define bar routine | outputs x | trashes a, z, n { | ld x, 200 | } | - | routine sub + | define sub routine | outputs x | trashes foo, a, z, n { | ld x, 0 @@ -3085,7 +3085,7 @@ vector says it does. | goto foo | } | - | routine main + | define main routine | outputs a | trashes foo, x, z, n { | call sub @@ -3106,11 +3106,11 @@ A vector can be copied into a vector table. | trashes a, z, n) | table[256] many | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -3132,11 +3132,11 @@ A vector can be copied out of a vector table. | trashes a, z, n) | table[256] many | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -3154,11 +3154,11 @@ A routine can be copied into a vector table. | trashes a, z, n) | table[256] many | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -3179,7 +3179,7 @@ A vector in a vector table cannot be directly called. | ld x, 200 | } | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -3203,7 +3203,7 @@ that types have structural equivalence, not name equivalence. | | vector routine_type vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -3211,7 +3211,7 @@ that types have structural equivalence, not name equivalence. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -3234,7 +3234,7 @@ The new style routine definitions support typedefs. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { From 5e692446b8c49023ccb40adee699e83d33d311ff Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 22:49:39 +0100 Subject: [PATCH 36/45] Convert remainder of routine declarations in this file. --- tests/SixtyPical Analysis.md | 62 ++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index f992863..873227b 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1424,7 +1424,7 @@ calling it. Calling an extern is just the same as calling a defined routine with the same constraints. - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 @@ -1437,7 +1437,7 @@ same constraints. | } = ok - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 @@ -1449,7 +1449,7 @@ same constraints. | } ? UnmeaningfulReadError: a - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 @@ -2264,7 +2264,7 @@ otherwise in tail position. | inc x | } | - | routine other + | define other routine | trashes bar, a, n, z | { | ld a, 0 @@ -2884,11 +2884,11 @@ Calling the vector does indeed trash the things the vector says it does. | vector routine trashes x, z, n foo | - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main outputs x, foo trashes z, n { + | define main routine outputs x, foo trashes z, n { | ld x, 0 | copy bar, foo | call foo @@ -2897,31 +2897,31 @@ Calling the vector does indeed trash the things the vector says it does. `goto`, if present, must be in tail position (the final instruction in a routine.) - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | goto bar | } = ok - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | goto bar | ld x, 0 | } ? IllegalJumpError - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2930,11 +2930,11 @@ Calling the vector does indeed trash the things the vector says it does. | } = ok - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2944,11 +2944,11 @@ Calling the vector does indeed trash the things the vector says it does. | } ? IllegalJumpError - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2960,11 +2960,11 @@ Calling the vector does indeed trash the things the vector says it does. | } = ok - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2977,11 +2977,11 @@ Calling the vector does indeed trash the things the vector says it does. For the purposes of `goto`, the end of a loop is never tail position. - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | repeat { | inc x @@ -2992,22 +2992,22 @@ For the purposes of `goto`, the end of a loop is never tail position. Can't `goto` a routine that outputs or trashes more than the current routine. - | routine bar trashes x, y, z, n { + | define bar routine trashes x, y, z, n { | ld x, 200 | ld y, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | goto bar | } ? IncompatibleConstraintsError - | routine bar outputs y trashes z, n { + | define bar routine outputs y trashes z, n { | ld y, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | goto bar | } @@ -3015,11 +3015,11 @@ Can't `goto` a routine that outputs or trashes more than the current routine. Can `goto` a routine that outputs or trashes less than the current routine. - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 1 | } | - | routine main trashes a, x, z, n { + | define main routine trashes a, x, z, n { | ld a, 0 | ld x, 0 | goto bar @@ -3030,11 +3030,11 @@ Indirect goto. | vector routine outputs x trashes a, z, n foo | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main outputs x trashes foo, a, z, n { + | define main routine outputs x trashes foo, a, z, n { | copy bar, foo | goto foo | } @@ -3047,19 +3047,19 @@ vector says it does. | trashes a, x, z, n | foo | - | routine bar + | define bar routine | trashes a, x, z, n { | ld x, 200 | } | - | routine sub + | define sub routine | trashes foo, a, x, z, n { | ld x, 0 | copy bar, foo | goto foo | } | - | routine main + | define main routine | outputs a | trashes foo, x, z, n { | call sub From 38119dbe297d793e8c16bcb8852dcc2de20cf160 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 22:55:54 +0100 Subject: [PATCH 37/45] Convert all tests to new syntax. --- tests/SixtyPical Compilation.md | 124 ++++++++++++++++---------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index ee0da72..87984df 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -13,14 +13,14 @@ SixtyPical to 6502 machine code. Null program. - | routine main + | define main routine | { | } = $080D RTS `nop` program. - | routine main + | define main routine | { | nop | } @@ -29,7 +29,7 @@ Null program. Rudimentary program. - | routine main + | define main routine | inputs a | outputs a | trashes c, z, n, v @@ -43,12 +43,12 @@ Rudimentary program. Call extern. - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 | - | routine main + | define main routine | inputs a | trashes a, z, n | { @@ -61,7 +61,7 @@ Call extern. Call defined routine. - | routine foo + | define foo routine | outputs a, x, y | trashes z, n | { @@ -70,7 +70,7 @@ Call defined routine. | ld y, 0 | } | - | routine main + | define main routine | trashes a, x, y, z, n | { | call foo @@ -86,7 +86,7 @@ Access a defined memory location. | byte foo | - | routine main + | define main routine | trashes a, y, z, n, foo | { | ld y, 0 @@ -102,7 +102,7 @@ Memory location with explicit address. | byte screen @ 1024 | - | routine main + | define main routine | trashes a, z, n, screen | { | ld a, 100 @@ -118,7 +118,7 @@ and `and`, `or`, and `xor` use zero-page addressing. | byte zp @ $00 | byte screen @ 100 | - | routine main + | define main routine | inputs screen, zp | outputs screen, zp | trashes a, z, n @@ -144,7 +144,7 @@ Memory location with initial value. | byte lives : 3 | - | routine main + | define main routine | inputs lives | trashes a, z, n | { @@ -159,7 +159,7 @@ Word memory locations with explicit address, initial value. | word w1 @ 60001 | word w2 : 3003 | - | routine main + | define main routine | inputs w1 | outputs w2 | trashes a, z, n @@ -178,7 +178,7 @@ Initialized byte table, initialized with ASCII string. Bytes allocated, but bey | byte table[8] message : "WHAT?" | - | routine main + | define main routine | inputs message | outputs x, a, z, n | { @@ -200,7 +200,7 @@ Initialized byte table, initialized with list of byte values. | byte table[8] message : 255, 0, 129, 128, 127 | - | routine main + | define main routine | inputs message | outputs x, a, z, n | { @@ -222,7 +222,7 @@ Initialized word table, initialized with list of word values. | word table[4] message : 65535, 0, 127, 127 | - | routine main + | define main routine | { | } = $080D RTS @@ -239,7 +239,7 @@ Some instructions. | byte foo | - | routine main + | define main routine | trashes a, x, y, z, n, c, v, foo | { | ld a, 0 @@ -317,7 +317,7 @@ Some instructions on tables. (1/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z, v @@ -341,7 +341,7 @@ Some instructions on tables. (2/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z @@ -363,7 +363,7 @@ Some instructions on tables. (3/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z @@ -387,7 +387,7 @@ Some instructions on tables. (3/3) Compiling `if`. - | routine main + | define main routine | trashes a, x, y, z, n, c, v | { | ld a, 0 @@ -406,7 +406,7 @@ Compiling `if`. Compiling `if not`. - | routine main + | define main routine | trashes a, x, y, z, n, c, v | { | ld a, 0 @@ -425,7 +425,7 @@ Compiling `if not`. Compiling `if` without `else`. - | routine main + | define main routine | trashes a, x, y, z, n, c, v | { | ld a, 0 @@ -442,7 +442,7 @@ Compiling `if` without `else`. Compiling `repeat ... until z`. - | routine main + | define main routine | trashes a, y, z, n, c | { | ld y, 65 @@ -461,7 +461,7 @@ Compiling `repeat ... until z`. Compiling `repeat ... until not z`. - | routine main + | define main routine | trashes a, y, z, n, c | { | ld y, 65 @@ -480,7 +480,7 @@ Compiling `repeat ... until not z`. Compiling `repeat ... until n`. - | routine main + | define main routine | trashes a, y, z, n, c | { | ld y, 65 @@ -497,7 +497,7 @@ Compiling `repeat ... until n`. Compiling `repeat ... until not n`. - | routine main + | define main routine | trashes a, y, z, n, c | { | ld y, 199 @@ -514,7 +514,7 @@ Compiling `repeat ... until not n`. Compiling `repeat forever`. - | routine main + | define main routine | trashes a, y, z, n, c | { | ld y, 65 @@ -529,7 +529,7 @@ Compiling `repeat forever`. The body of `repeat forever` can be empty. - | routine main + | define main routine | { | repeat { | } forever @@ -579,7 +579,7 @@ Compiling `for ... down to`. Compiling `save`. - | routine main + | define main routine | inputs a | outputs a | trashes z, n @@ -603,7 +603,7 @@ Compiling `save`. Compiling `save` with shortcut syntax. - | routine main + | define main routine | inputs a | outputs a | trashes z, n @@ -626,7 +626,7 @@ Compiling `save` with shortcut syntax. Compiling `save` on a user-defined location. | byte foo - | routine main + | define main routine | trashes a, z, n | { | save foo { @@ -647,7 +647,7 @@ Indexed access. | byte one | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -667,7 +667,7 @@ Byte tables take up, at most, 256 bytes in memory. | byte table[256] tab1 | byte table[256] tab2 | - | routine main + | define main routine | inputs tab1 | outputs tab2 | trashes a, x, n, z @@ -686,7 +686,7 @@ Byte storage locations take up only 1 byte in memory. | byte one | byte two | - | routine main + | define main routine | outputs one, two | trashes a, x, n, z | { @@ -704,7 +704,7 @@ Copy byte to byte. | byte bar | byte baz | - | routine main + | define main routine | inputs baz | outputs bar | trashes a, n, z @@ -720,7 +720,7 @@ Copy word to word. | word bar | word baz | - | routine main + | define main routine | inputs baz | outputs bar | trashes a, n, z @@ -737,7 +737,7 @@ Copy literal word to word. | word bar | - | routine main + | define main routine | outputs bar | trashes a, n, z | { @@ -753,7 +753,7 @@ You can also copy a literal word to a word table. | word table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -773,7 +773,7 @@ Copy vector to vector. | vector routine bar | vector routine baz | - | routine main + | define main routine | inputs baz | outputs bar | trashes a, n, z @@ -794,7 +794,7 @@ Copy routine to vector, inside an `interrupts off` block. | trashes z, n | bar | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -802,7 +802,7 @@ Copy routine to vector, inside an `interrupts off` block. | inc x | } | - | routine main + | define main routine | outputs bar | trashes a, n, z | { @@ -828,14 +828,14 @@ Copy routine (by forward reference) to vector. | trashes z, n | bar | - | routine main + | define main routine | outputs bar | trashes a, n, z | { | copy foo, bar | } | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -855,7 +855,7 @@ Copy word to word table and back, with both `x` and `y` as indexes. | word one | word table[256] many | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, y, n, z @@ -899,14 +899,14 @@ Indirect call. | trashes z, n | foo | - | routine bar + | define bar routine | outputs x | trashes z, n | { | ld x, 200 | } | - | routine main + | define main routine | outputs x, foo | trashes a, z, n | { @@ -926,7 +926,7 @@ Indirect call. Compiling `goto`. Note that no `RTS` is emitted after the `JMP`. - | routine bar + | define bar routine | inputs y | outputs x, y | trashes z, n @@ -934,7 +934,7 @@ Compiling `goto`. Note that no `RTS` is emitted after the `JMP`. | ld x, 200 | } | - | routine main + | define main routine | outputs x, y | trashes a, z, n | { @@ -959,11 +959,11 @@ Copying to and from a vector table. | trashes a, z, n | table[256] many | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -1004,7 +1004,7 @@ Copying to and from a vector table. Adding a constant word to a word memory location. | word score - | routine main + | define main routine | inputs score | outputs score | trashes a, c, z, v, n @@ -1025,7 +1025,7 @@ Adding a word memory location to another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes a, c, z, v, n @@ -1045,7 +1045,7 @@ Adding a word memory location to another word memory location. Subtracting a constant word from a word memory location. | word score - | routine main + | define main routine | inputs score | outputs score | trashes a, c, z, v, n @@ -1066,7 +1066,7 @@ Subtracting a word memory location from another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes a, c, z, v, n @@ -1090,7 +1090,7 @@ Load address into pointer. | buffer[2048] buf | pointer ptr @ 254 | - | routine main + | define main routine | inputs buf | outputs buf, y | trashes a, z, n, ptr @@ -1110,7 +1110,7 @@ Write literal through a pointer. | buffer[2048] buf | pointer ptr @ 254 | - | routine main + | define main routine | inputs buf | outputs buf, y | trashes a, z, n, ptr @@ -1134,7 +1134,7 @@ Write stored value through a pointer. | pointer ptr @ 254 | byte foo | - | routine main + | define main routine | inputs foo, buf | outputs y, buf | trashes a, z, n, ptr @@ -1158,7 +1158,7 @@ Read through a pointer, into a byte storage location, or the `a` register. | pointer ptr @ 254 | byte foo | - | routine main + | define main routine | inputs buf | outputs y, foo | trashes a, z, n, ptr @@ -1184,7 +1184,7 @@ Read and write through two pointers. | pointer ptra @ 252 | pointer ptrb @ 254 | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, y, z, n, ptra, ptrb @@ -1213,7 +1213,7 @@ Write the `a` register through a pointer. | pointer ptr @ 254 | byte foo | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, y, z, n, ptr @@ -1240,7 +1240,7 @@ Note that this is *not* range-checked. (Yet.) | byte foo | word delta | - | routine main + | define main routine | inputs buf | outputs y, foo, delta | trashes a, c, v, z, n, ptr @@ -1283,7 +1283,7 @@ Note that this is *not* range-checked. (Yet.) Trash does nothing except indicate that we do not care about the value anymore. - | routine main + | define main routine | inputs a | outputs x | trashes a, z, n From f81757fd762fbcee4560400318ed706954f6e86a Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 7 Sep 2018 23:00:29 +0100 Subject: [PATCH 38/45] Partial conversion of example programs. --- eg/rudiments/add-fail.60p | 2 +- eg/rudiments/add-pass.60p | 2 +- eg/rudiments/conditional.60p | 4 ++-- eg/rudiments/copy.60p | 2 +- eg/rudiments/example.60p | 2 +- eg/rudiments/vector-inc.60p | 4 ++-- eg/rudiments/vector-main.60p | 2 +- eg/rudiments/word-table.60p | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/eg/rudiments/add-fail.60p b/eg/rudiments/add-fail.60p index ee9e034..ba653a4 100644 --- a/eg/rudiments/add-fail.60p +++ b/eg/rudiments/add-fail.60p @@ -1,4 +1,4 @@ -routine add_four +define add_four routine inputs a outputs a { diff --git a/eg/rudiments/add-pass.60p b/eg/rudiments/add-pass.60p index 65c69f1..4427cbc 100644 --- a/eg/rudiments/add-pass.60p +++ b/eg/rudiments/add-pass.60p @@ -1,4 +1,4 @@ -routine main +define main routine inputs a outputs a trashes c, z, n, v diff --git a/eg/rudiments/conditional.60p b/eg/rudiments/conditional.60p index eb5711c..3cdb1fb 100644 --- a/eg/rudiments/conditional.60p +++ b/eg/rudiments/conditional.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine trashes a, x, y, z, n, c, v { ld a, 0 diff --git a/eg/rudiments/copy.60p b/eg/rudiments/copy.60p index ffc07d1..d31fd6b 100644 --- a/eg/rudiments/copy.60p +++ b/eg/rudiments/copy.60p @@ -1,7 +1,7 @@ byte bar byte baz -routine main +define main routine inputs baz outputs bar trashes a, n, z diff --git a/eg/rudiments/example.60p b/eg/rudiments/example.60p index d2b9e7a..2845c60 100644 --- a/eg/rudiments/example.60p +++ b/eg/rudiments/example.60p @@ -1,6 +1,6 @@ byte lives -routine main +define main routine inputs lives outputs lives trashes a, x diff --git a/eg/rudiments/vector-inc.60p b/eg/rudiments/vector-inc.60p index 69a69c7..ca15901 100644 --- a/eg/rudiments/vector-inc.60p +++ b/eg/rudiments/vector-inc.60p @@ -6,14 +6,14 @@ routine chrout trashes a @ 65490 -routine printa +define printa routine trashes a, z, n { ld a, 65 call chrout } -routine printb +define printb routine trashes a, z, n { ld a, 66 diff --git a/eg/rudiments/vector-main.60p b/eg/rudiments/vector-main.60p index b6e45e9..f8df898 100644 --- a/eg/rudiments/vector-main.60p +++ b/eg/rudiments/vector-main.60p @@ -12,7 +12,7 @@ vector routine // call chrout // } -routine main +define main routine trashes print, a, z, n { copy printa, print diff --git a/eg/rudiments/word-table.60p b/eg/rudiments/word-table.60p index ef40dd8..ffad04a 100644 --- a/eg/rudiments/word-table.60p +++ b/eg/rudiments/word-table.60p @@ -1,7 +1,7 @@ word one word table[256] many -routine main +define main routine inputs one, many outputs one, many trashes a, x, y, n, z From 5549b8379fbdadbb520e3ec62271a2b93f7b10e9 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Sun, 9 Sep 2018 14:01:38 +0100 Subject: [PATCH 39/45] More conversion. --- eg/apple2/lores.60p | 2 +- eg/apple2/print.60p | 5 ++--- eg/c64/demo-game/demo-game.60p | 8 ++++---- eg/c64/hearts.60p | 2 +- eg/c64/intr1.60p | 4 ++-- eg/c64/joystick.60p | 4 ++-- eg/c64/screen1.60p | 2 +- 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/eg/apple2/lores.60p b/eg/apple2/lores.60p index ac88a4c..c68e6dd 100644 --- a/eg/apple2/lores.60p +++ b/eg/apple2/lores.60p @@ -7,7 +7,7 @@ byte ds_page2 @ $C055 byte ds_lores @ $C056 byte ds_hires @ $C057 -routine main +define main routine inputs a outputs ds_lores, ds_page1, ds_split, ds_graphics trashes a, z, n diff --git a/eg/apple2/print.60p b/eg/apple2/print.60p index aa72441..9dd0b5e 100644 --- a/eg/apple2/print.60p +++ b/eg/apple2/print.60p @@ -1,12 +1,11 @@ // Write ">AB>" to "standard output" -routine cout +define cout routine inputs a trashes a @ $FDED -routine main - inputs a +define main routine trashes a, z, n { ld a, 62 diff --git a/eg/c64/demo-game/demo-game.60p b/eg/c64/demo-game/demo-game.60p index a521e30..69ade0d 100644 --- a/eg/c64/demo-game/demo-game.60p +++ b/eg/c64/demo-game/demo-game.60p @@ -121,7 +121,7 @@ vector game_state_routine // Utility Routines // ---------------------------------------------------------------- -routine read_stick +define read_stick routine inputs joy2 outputs delta trashes a, x, z, n @@ -186,7 +186,7 @@ define check_button routine } } -routine clear_screen +define clear_screen routine outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 trashes a, y, c, n, z { @@ -209,7 +209,7 @@ routine clear_screen } until z } -routine calculate_new_position +define calculate_new_position routine inputs pos, delta outputs new_pos trashes a, c, n, z, v @@ -243,7 +243,7 @@ define check_new_position_in_bounds routine } } -routine init_game +define init_game routine inputs actor_pos, actor_delta, actor_logic outputs actor_pos, actor_delta, actor_logic, player_died trashes pos, a, y, z, n, c, v diff --git a/eg/c64/hearts.60p b/eg/c64/hearts.60p index 43df36d..3a69adf 100644 --- a/eg/c64/hearts.60p +++ b/eg/c64/hearts.60p @@ -3,7 +3,7 @@ // Define where the screen starts in memory: byte table[256] screen @ 1024 -routine main +define main routine // These are the values that will be written to by this routine: trashes a, x, z, n, screen { diff --git a/eg/c64/intr1.60p b/eg/c64/intr1.60p index 68cad5c..b817d18 100644 --- a/eg/c64/intr1.60p +++ b/eg/c64/intr1.60p @@ -24,7 +24,7 @@ vector routine trashes z, n save_cinv -routine our_cinv +define our_cinv routine inputs vic_border outputs vic_border trashes z, n @@ -33,7 +33,7 @@ routine our_cinv goto save_cinv } -routine main +define main routine inputs cinv outputs cinv, save_cinv trashes a, n, z diff --git a/eg/c64/joystick.60p b/eg/c64/joystick.60p index 8783bd7..fdbaa83 100644 --- a/eg/c64/joystick.60p +++ b/eg/c64/joystick.60p @@ -3,7 +3,7 @@ byte joy2 @ $dc00 word delta -routine read_stick +define read_stick routine inputs joy2 outputs delta trashes a, x, z, n @@ -36,7 +36,7 @@ routine read_stick } } -routine main +define main routine inputs joy2 outputs delta trashes a, x, z, n, screen diff --git a/eg/c64/screen1.60p b/eg/c64/screen1.60p index 5a96691..a855d51 100644 --- a/eg/c64/screen1.60p +++ b/eg/c64/screen1.60p @@ -1,6 +1,6 @@ byte screen @ 1024 -routine main +define main routine trashes a, z, n, screen { ld a, 83 From 7d32277e2c6920922b253be9177eea53376d7a3a Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Sun, 9 Sep 2018 15:03:43 +0100 Subject: [PATCH 40/45] More conversion. --- eg/c64/demo-game/demo-game.60p | 2 +- eg/c64/ribos/ribos2.60p | 2 +- eg/rudiments/add-word.60p | 2 +- eg/rudiments/buffer.60p | 2 +- eg/rudiments/call.60p | 6 +++--- eg/rudiments/forever.60p | 2 +- eg/rudiments/goto.60p | 6 +++--- eg/rudiments/if.60p | 2 +- eg/rudiments/loop.60p | 4 ++-- eg/rudiments/memloc.60p | 6 +++--- eg/rudiments/print.60p | 4 ++-- eg/vic20/hearts.60p | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/eg/c64/demo-game/demo-game.60p b/eg/c64/demo-game/demo-game.60p index 69ade0d..4097f70 100644 --- a/eg/c64/demo-game/demo-game.60p +++ b/eg/c64/demo-game/demo-game.60p @@ -457,7 +457,7 @@ define our_cinv game_state_routine goto dispatch_game_state } -routine main +define main routine inputs cinv outputs cinv, save_cinv, pos, dispatch_game_state, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 diff --git a/eg/c64/ribos/ribos2.60p b/eg/c64/ribos/ribos2.60p index b9d9877..ee86006 100644 --- a/eg/c64/ribos/ribos2.60p +++ b/eg/c64/ribos/ribos2.60p @@ -49,7 +49,7 @@ byte scanline : 85 // %01010101 // generating them as part of a SixtyPical program would not // be practical. So we just jump to this location instead. -routine pla_tay_pla_tax_pla_rti +define pla_tay_pla_tax_pla_rti routine inputs a trashes a @ $EA81 diff --git a/eg/rudiments/add-word.60p b/eg/rudiments/add-word.60p index ccf4d7a..a3c1d86 100644 --- a/eg/rudiments/add-word.60p +++ b/eg/rudiments/add-word.60p @@ -1,5 +1,5 @@ word score -routine main +define main routine inputs score outputs score trashes a, c, z, v, n diff --git a/eg/rudiments/buffer.60p b/eg/rudiments/buffer.60p index ce4dfbe..79bc512 100644 --- a/eg/rudiments/buffer.60p +++ b/eg/rudiments/buffer.60p @@ -2,7 +2,7 @@ buffer[2048] buf pointer ptr @ 254 byte foo -routine main +define main routine inputs buf outputs buf, y, foo trashes a, z, n, ptr diff --git a/eg/rudiments/call.60p b/eg/rudiments/call.60p index bfa0b89..a7fc616 100644 --- a/eg/rudiments/call.60p +++ b/eg/rudiments/call.60p @@ -1,16 +1,16 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine print +define print routine trashes a, z, n { ld a, 65 call chrout } -routine main +define main routine trashes a, z, n { call print diff --git a/eg/rudiments/forever.60p b/eg/rudiments/forever.60p index e8b1c61..a9968cf 100644 --- a/eg/rudiments/forever.60p +++ b/eg/rudiments/forever.60p @@ -1,4 +1,4 @@ -routine main +define main routine trashes a, y, z, n, c { ld y, 65 diff --git a/eg/rudiments/goto.60p b/eg/rudiments/goto.60p index 5c8c0f8..6aafcbb 100644 --- a/eg/rudiments/goto.60p +++ b/eg/rudiments/goto.60p @@ -1,14 +1,14 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine bar trashes a, z, n { +define bar routine trashes a, z, n { ld a, 66 call chrout } -routine main trashes a, z, n { +define main routine trashes a, z, n { ld a, 65 call chrout goto bar diff --git a/eg/rudiments/if.60p b/eg/rudiments/if.60p index 64c63ae..3900724 100644 --- a/eg/rudiments/if.60p +++ b/eg/rudiments/if.60p @@ -1,4 +1,4 @@ -routine foo +define main routine inputs a outputs a { diff --git a/eg/rudiments/loop.60p b/eg/rudiments/loop.60p index 7de6f09..2f3d95b 100644 --- a/eg/rudiments/loop.60p +++ b/eg/rudiments/loop.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine trashes a, y, z, n, c { ld y, 65 diff --git a/eg/rudiments/memloc.60p b/eg/rudiments/memloc.60p index f8886ce..d6e49d1 100644 --- a/eg/rudiments/memloc.60p +++ b/eg/rudiments/memloc.60p @@ -1,11 +1,11 @@ byte foo -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine print +define print routine inputs foo trashes a, z, n { @@ -13,7 +13,7 @@ routine print call chrout } -routine main +define main routine trashes a, y, z, n, foo { ld y, 65 diff --git a/eg/rudiments/print.60p b/eg/rudiments/print.60p index c1c29c3..d060ebc 100644 --- a/eg/rudiments/print.60p +++ b/eg/rudiments/print.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine inputs a trashes a, z, n { diff --git a/eg/vic20/hearts.60p b/eg/vic20/hearts.60p index e4a615b..ffc4369 100644 --- a/eg/vic20/hearts.60p +++ b/eg/vic20/hearts.60p @@ -3,7 +3,7 @@ // Define where the screen starts in memory: byte table[256] screen @ 7680 -routine main +define main routine // These are the values that will be written to by this routine: trashes a, x, z, n, screen { From 7680f09850afed826adca25b9dbb3fcf973f95d5 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Sun, 9 Sep 2018 15:31:35 +0100 Subject: [PATCH 41/45] More conversion. I think that's the last of it. --- eg/rudiments/conditional2.60p | 4 ++-- eg/rudiments/example.60p | 2 +- eg/rudiments/if.60p | 1 + eg/rudiments/range-error.60p | 2 +- eg/rudiments/vector-inc.60p | 2 +- eg/rudiments/vector-table.60p | 8 ++++---- eg/rudiments/vector.60p | 8 ++++---- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/eg/rudiments/conditional2.60p b/eg/rudiments/conditional2.60p index 444de48..6483cfd 100644 --- a/eg/rudiments/conditional2.60p +++ b/eg/rudiments/conditional2.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine trashes a, x, y, z, n, c, v { ld a, 0 diff --git a/eg/rudiments/example.60p b/eg/rudiments/example.60p index 2845c60..c4475da 100644 --- a/eg/rudiments/example.60p +++ b/eg/rudiments/example.60p @@ -3,7 +3,7 @@ byte lives define main routine inputs lives outputs lives - trashes a, x + trashes a, x, z, n, c, v { ld a, 0 st a, lives diff --git a/eg/rudiments/if.60p b/eg/rudiments/if.60p index 3900724..9b0b968 100644 --- a/eg/rudiments/if.60p +++ b/eg/rudiments/if.60p @@ -1,6 +1,7 @@ define main routine inputs a outputs a + trashes z, n, c { cmp a, 42 if z { diff --git a/eg/rudiments/range-error.60p b/eg/rudiments/range-error.60p index 32b61a1..87e1095 100644 --- a/eg/rudiments/range-error.60p +++ b/eg/rudiments/range-error.60p @@ -1,6 +1,6 @@ byte table[8] message : "WHAT?" -routine main +define main routine inputs message outputs x, a, z, n { diff --git a/eg/rudiments/vector-inc.60p b/eg/rudiments/vector-inc.60p index ca15901..1e2bd8e 100644 --- a/eg/rudiments/vector-inc.60p +++ b/eg/rudiments/vector-inc.60p @@ -1,7 +1,7 @@ // This will not compile on its own, because there is no `main`. // But this and `vector-main.60p` together will compile. -routine chrout +define chrout routine inputs a trashes a @ 65490 diff --git a/eg/rudiments/vector-table.60p b/eg/rudiments/vector-table.60p index d1fd0d0..4362012 100644 --- a/eg/rudiments/vector-table.60p +++ b/eg/rudiments/vector-table.60p @@ -11,26 +11,26 @@ vector (routine trashes a, z, n) table[32] vectors -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine printa +define printa routine trashes a, z, n { ld a, 65 call chrout } -routine printb +define printb routine trashes a, z, n { ld a, 66 call chrout } -routine main +define main routine inputs vectors outputs vectors trashes print, a, x, z, n, c diff --git a/eg/rudiments/vector.60p b/eg/rudiments/vector.60p index fd86d93..7783165 100644 --- a/eg/rudiments/vector.60p +++ b/eg/rudiments/vector.60p @@ -2,26 +2,26 @@ vector routine trashes a, z, n print -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine printa +define printa routine trashes a, z, n { ld a, 65 call chrout } -routine printb +define printb routine trashes a, z, n { ld a, 66 call chrout } -routine main +define main routine trashes print, a, z, n { copy printa, print From 6e9b15e0176e384ddd35ec51becb298886f4c2ae Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 14 Sep 2018 13:14:32 +0100 Subject: [PATCH 42/45] Small documentation updates. --- HISTORY.md | 1 + README.md | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e77b673..63e1128 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ History of SixtyPical 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. +* Deprecated `routine foo ...` syntax has been removed. * 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/README.md b/README.md index 779f8bd..48c4471 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,16 @@ _Version 0.17. Work-in-progress, everything is subject to change._ **SixtyPical** is a low-level programming language with advanced static analysis. Many of its primitive instructions resemble those of the 6502 CPU — in fact it is intended to be compiled to -6502 machine code — but along with these are constructs which -ease structuring and analysizing the code. - -SixtyPical aims to fill this niche: +6502 machine code — but along with these instructions are +constructs which ease structuring and analyzing the code. The +language aims to fill this niche: * You'd use assembly, but you don't want to spend hours debugging (say) a memory overrun that happened because of a ridiculous silly error. -* You'd use C, but you don't want the overhead of compiler-added - code to manage the stack and registers. +* You'd use C or some other "high-level" language, but you don't + want the extra overhead added by the compiler to manage the + stack and registers. SixtyPical gives the programmer a coding regimen on par with assembly language in terms of size and hands-on-ness, but also able to catch @@ -43,8 +43,8 @@ machine-language programming idioms, such as SixtyPical is defined by a specification document, a set of test cases, and a reference implementation written in Python 2. The reference -implementation can analyze and compile SixtyPical programs to 6502 machine code, -which can be run on several 6502-based 8-bit architectures: +implementation can analyze and compile SixtyPical programs to 6502 machine +code, which can be run on several 6502-based 8-bit architectures: * Commodore 64 * Commodore VIC-20 From dd86015cd59cb1b2e0ba339c9e640d0b5a7479cf Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Sun, 16 Sep 2018 18:45:09 +0100 Subject: [PATCH 43/45] Add another test for `for`. There's another one I want to add too. --- tests/SixtyPical Analysis.md | 88 ++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 873227b..1bfebe5 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1772,7 +1772,10 @@ While `repeat` is most often used with `z`, it can also be used with `n`. Basic "open-faced for" loop. We'll start with the "upto" variant. -In a "for" loop, we know the exact range the loop variable takes on. +#### upward-counting variant + +Even though we do not give the starting value in the "for" construct, +we know the exact range the loop variable takes on. | byte table[16] tab | @@ -1805,6 +1808,19 @@ You need to initialize the loop variable before the loop. | } ? UnmeaningfulReadError +Because routines current do not express range constraints, It may not do to take the loop variable as an input. (?) + + | byte table[16] tab + | + | define foo routine + | inputs tab, x + | trashes a, x, c, z, v, n { + | for x up to 15 { + | ld a, 0 + | } + | } + ? RangeExceededError + You cannot modify the loop variable in a "for" loop. | byte table[16] tab @@ -1875,6 +1891,42 @@ If the range isn't known to be smaller than the final value, you can't go up to | } ? RangeExceededError +You can initialize something inside the loop that was uninitialized outside. + + | define main routine + | outputs x, y, n, z + | trashes c + | { + | ld x, 0 + | for x up to 15 { + | ld y, 15 + | } + | } + = ok + +But you can't UNinitialize something at the end of the loop that you need +initialized at the start of that loop. + + | define foo routine + | trashes y + | { + | } + | + | define main routine + | outputs x, y, n, z + | trashes c + | { + | ld x, 0 + | ld y, 15 + | for x up to 15 { + | inc y + | call foo + | } + | } + ? UnmeaningfulReadError: y + +#### downward-counting variant + In a "for" loop (downward-counting variant), we know the exact range the loop variable takes on. | byte table[16] tab @@ -1932,40 +1984,6 @@ If the range isn't known to be larger than the final value, you can't go down to | } ? RangeExceededError -You can initialize something inside the loop that was uninitialized outside. - - | define main routine - | outputs x, y, n, z - | trashes c - | { - | ld x, 0 - | for x up to 15 { - | ld y, 15 - | } - | } - = ok - -But you can't UNinitialize something at the end of the loop that you need -initialized at the start of that loop. - - | define foo routine - | trashes y - | { - | } - | - | define main routine - | outputs x, y, n, z - | trashes c - | { - | ld x, 0 - | ld y, 15 - | for x up to 15 { - | inc y - | call foo - | } - | } - ? UnmeaningfulReadError: y - ### save ### Basic neutral test, where the `save` makes no difference. From 92db4ac3f00ca804dfde46f5712fd6f2da5647b4 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Sun, 16 Sep 2018 21:30:53 +0100 Subject: [PATCH 44/45] More tests for "for" loops. --- tests/SixtyPical Analysis.md | 62 +++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 1bfebe5..b393e07 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1808,7 +1808,9 @@ You need to initialize the loop variable before the loop. | } ? UnmeaningfulReadError -Because routines current do not express range constraints, It may not do to take the loop variable as an input. (?) +Because routines currently do not include range constraints, +the loop variable may not be useful as an input (the location +is assumed to have the maximum range.) | byte table[16] tab | @@ -1925,6 +1927,35 @@ initialized at the start of that loop. | } ? UnmeaningfulReadError: y +The "for" loop does not preserve the `z` or `n` registers. + + | define foo routine trashes x { + | ld x, 0 + | for x up to 15 { + | } + | } + ? ForbiddenWriteError + +But it does preserve the other registers, such as `c`. + + | define foo routine trashes x, z, n { + | ld x, 0 + | for x up to 15 { + | } + | } + = ok + +In fact it does not strictly trash `z` and `n`, as they are +always set to known values after the loop. TODO: document +what these known values are! + + | define foo routine outputs z, n trashes x { + | ld x, 0 + | for x up to 15 { + | } + | } + = ok + #### downward-counting variant In a "for" loop (downward-counting variant), we know the exact range the loop variable takes on. @@ -1984,6 +2015,35 @@ If the range isn't known to be larger than the final value, you can't go down to | } ? RangeExceededError +The "for" loop does not preserve the `z` or `n` registers. + + | define foo routine trashes x { + | ld x, 15 + | for x down to 0 { + | } + | } + ? ForbiddenWriteError + +But it does preserve the other registers, such as `c`. + + | define foo routine trashes x, z, n { + | ld x, 15 + | for x down to 0 { + | } + | } + = ok + +In fact it does not strictly trash `z` and `n`, as they are +always set to known values after the loop. TODO: document +what these known values are! + + | define foo routine outputs z, n trashes x { + | ld x, 15 + | for x down to 0 { + | } + | } + = ok + ### save ### Basic neutral test, where the `save` makes no difference. From 23936333ec6eaf7db37faa8743a96faf6b0ba57c Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 16 Nov 2018 11:41:41 +0000 Subject: [PATCH 45/45] These notes have been incorporated in the loadngo.sh script. --- doc/Apple II Notes.md | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 doc/Apple II Notes.md diff --git a/doc/Apple II Notes.md b/doc/Apple II Notes.md deleted file mode 100644 index b965c61..0000000 --- a/doc/Apple II Notes.md +++ /dev/null @@ -1,14 +0,0 @@ -Notes for building SixtyPical programs for Apple II -=================================================== - -And running them on `linapple`. - -We'll do `eg/rudiments/add-pass.60p`. It does nothing. - - bin/sixtypical --origin=0x2000 --output-format=raw eg/rudiments/add-pass.60p > add-pass.bin - cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk - a2in B sixtypical.dsk ADD-PASS add-pass.bin - a2ls sixtypical.dsk - linapple -d1 sixtypical.dsk -autoboot - -Next... we should do one that does something.