From ef07f46f418abb2c14ba1d6b315dbabf27b62fa3 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 17 Nov 2017 16:12:59 +0000 Subject: [PATCH 01/24] Start a development branch. --- README.markdown | 2 +- doc/SixtyPical.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 7f58c06..c535e6d 100644 --- a/README.markdown +++ b/README.markdown @@ -24,7 +24,7 @@ programs to 6502 machine code. It is a **work in progress**, currently at the **proof-of-concept** stage. -The current released version of SixtyPical is 0.7. +The current development version of SixtyPical is 0.8-PRE. Documentation ------------- diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index a118a73..9fcd9eb 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -1,7 +1,7 @@ SixtyPical ========== -This document describes the SixtyPical programming language version 0.7, +This document describes the SixtyPical programming language version 0.8-PRE, both its execution aspect and its static analysis aspect (even though these are, technically speaking, separate concepts.) From f2f716de86c0987cf664aab143b865c72c0fbb26 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 17 Nov 2017 16:56:52 +0000 Subject: [PATCH 02/24] Going over the spec for version 0.8-PRE. --- doc/SixtyPical.md | 121 ++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index 9fcd9eb..1e27397 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -14,20 +14,22 @@ the language. Types ----- -There are five TYPES in SixtyPical: +There are seven *types* in SixtyPical: * bit (2 possible values) * byte (256 possible values) * byte table (256 entries, each holding a byte) +* word (65536 possible values) +* word table (256 entries, each holding a word) * routine (code stored somewhere in memory, read-only) * vector (address of a routine) Memory locations ---------------- -A primary concept in SixtyPical is the MEMORY LOCATION. At any given point -in time during execution, each memory location is either UNINITIALIZED or -INITIALIZED. At any given point in the program text, too, each memory +A primary concept in SixtyPical is the *memory location*. At any given point +in time during execution, each memory location is either *uninitialized* or +*initialized*. At any given point in the program text, too, each memory location is either uninitialized or initialized. Where-ever it is one or the other during execution, it is the same in the corresponding place in the program text; thus, it is a static property. @@ -64,17 +66,27 @@ They come in bit and byte types. There are two bit constants, off on -and two-hundred and fifty-six byte constants, +two hundred and fifty-six byte constants, 0 1 ... 255 +and sixty-five thousand five hundred and thirty-six word constants, + + 0 + 1 + ... + 65535 + +Note that all byte constants serve double duty as word constants in that +context. + ### User-defined ### There may be any number of user-defined memory locations. They are defined -by giving the type, which must be `byte`, `byte table`, or `vector`, and the +by giving the type (which may be any type except `bit` and `routine`) and the name. byte pos @@ -88,10 +100,10 @@ case, an explicit address in memory cannot be given. byte pos : 0 -A user-defined vector memory location is decorated with READS and WRITES lists -like a routine (see below), and it may only hold addresses of routines which -are compatible. (Meaning, the routine's inputs (resp. outputs, trashes) -must be a subset of the vector's inputs (resp. outputs, trashes.)) +A user-defined vector memory location is decorated with `inputs`, `outputs` +and `trashes` lists like a routine (see below), and it may only hold addresses +of routines which are compatible. (Meaning, the routine's inputs (resp. outputs, +trashes) must be a subset of the vector's inputs (resp. outputs, trashes.)) vector actor_logic inputs a, score @@ -102,10 +114,16 @@ must be a subset of the vector's inputs (resp. outputs, trashes.)) Routines -------- -Every routine must list all the memory locations it READS from, i.e. its -INPUTS, and all the memory locations it WRITES to, whether they are OUTPUTS -or merely TRASHED. Every memory location that is not written to by the -routine (or any routines that the routine calls) is PRESERVED by the routine. +Every routine must list all the memory locations it *reads from*, which we +call its `inputs`, and all the memory locations it *writes to*. The latter +we divide into two groups: its `outputs` which it intentionally initializes, +and its `trashes`, which it does not care about, and leaves uninitialized. +For example, if it uses a register to temporarily store an intermediate +value used in a multiplication, that register has no meaning outside of +the multiplication, and is one of the routine's `trashes`. + +It is common to say that the `trashes` are the memory locations that are +*not preserved* by the routine. routine foo inputs a, score @@ -114,6 +132,9 @@ routine (or any routines that the routine calls) is PRESERVED by the routine. ... } +The `inputs` are sometimes called the routine's READS set, while the +`outputs` and `trashes` are collectively called the WRITES set. + Routines may call only routines previously defined in the program source. Thus, directly recursive routines are not allowed. (However, routines may also call routines via vectors, which are dynamically assigned. In this @@ -122,12 +143,12 @@ case, there is, for the time being, no check for recursive calls.) For a SixtyPical program to be run, there must be one routine called `main`. This routine is executed when the program is run. -The memory locations given given as inputs are considered to be initialized +The memory locations given as inputs to a routine are considered to be initialized at the beginning of the routine. Various instructions cause memory locations to be initialized after they are executed. Calling a routine which trashes some memory locations causes those memory locations to be uninitialized after that routine is called. At the end of a routine, all memory locations listed -as outputs must be initialised. +as outputs must be initialized. A routine can also be declared as "external", in which case its body need not be defined but an absolute address must be given for where the routine @@ -141,6 +162,13 @@ is located in memory. Instructions ------------ +Instructions are inspired by, and in many cases closely resemble, the 6502 +instruction set. However, in many cases they do not map 1:1 to 6502 instructions. +If a SixtyPical instruction cannot be translated validly to one more more 6502 +instructions while retaining all the stated constraints, that's a static error +in a SixtyPical program, and technically any implementation of SixtyPical, even +an interpreter, should flag it up. + ### ld ### ld , [+ ] @@ -148,13 +176,12 @@ Instructions Reads from src and writes to dest. * It is illegal if dest is not a register. -* It is illegal if dest does not occur in the WRITES lists of the current - routine. +* It is illegal if dest does not occur in the WRITES of the current routine. * It is illegal if src is not of same type as dest (i.e., is not a byte.) * It is illegal if src is uninitialized. After execution, dest is considered initialized. The flags `z` and `n` may be -changed by this instruction; they must be named in the WRITES lists, and they +changed by this instruction; they must be named in the WRITES, and they are considered initialized after it has executed. If and only if src is a byte table, the index-memory-location must be given. @@ -169,8 +196,7 @@ underlying opcodes. Reads from src and writes to dest. * It is illegal if dest is a register or if dest is read-only. -* It is illegal if dest does not occur in the WRITES lists of the current - routine. +* It is illegal if dest does not occur in the WRITES of the current routine. * It is illegal if src is not of same type as dest. * It is illegal if src is uninitialized. @@ -187,10 +213,9 @@ Adds the contents of src to dest and stores the result in dest. * It is illegal if src OR dest OR c is uninitialized. * It is illegal if dest is read-only. -* It is illegal if dest does not occur in the WRITES lists - of the current routine. +* It is illegal if dest does not occur in the WRITES of the current routine. -Affects n, z, c, and v flags, requiring that they be in the WRITES lists, +Affects n, z, c, and v flags, requiring that they be in the WRITES, and initializing them afterwards. dest and src continue to be initialized afterwards. @@ -203,10 +228,9 @@ Increments the value in dest. Does not honour carry. * It is illegal if dest is uninitialized. * It is illegal if dest is read-only. -* It is illegal if dest does not occur in the WRITES lists - of the current routine. +* It is illegal if dest does not occur in the WRITES of the current routine. -Affects n and z flags, requiring that they be in the WRITES lists, +Affects n and z flags, requiring that they be in the WRITES, and initializing them afterwards. ### sub ### @@ -217,10 +241,9 @@ Subtracts the contents of src from dest and stores the result in dest. * It is illegal if src OR dest OR c is uninitialized. * It is illegal if dest is read-only. -* It is illegal if dest does not occur in the WRITES lists - of the current routine. +* It is illegal if dest does not occur in the WRITES of the current routine. -Affects n, z, c, and v flags, requiring that they be in the WRITES lists, +Affects n, z, c, and v flags, requiring that they be in the WRITES, and initializing them afterwards. dest and src continue to be initialized afterwards. @@ -233,10 +256,9 @@ Decrements the value in dest. Does not honour carry. * It is illegal if dest is uninitialized. * It is illegal if dest is read-only. -* It is illegal if dest does not occur in the WRITES lists - of the current routine. +* It is illegal if dest does not occur in the WRITES of the current routine. -Affects n and z flags, requiring that they be in the WRITES lists, +Affects n and z flags, requiring that they be in the WRITES, and initializing them afterwards. ### cmp ### @@ -248,7 +270,7 @@ does not store the result anywhere, only sets the resulting flags. * It is illegal if src OR dest is uninitialized. -Affects n, z, and c flags, requiring that they be in the WRITES lists, +Affects n, z, and c flags, requiring that they be in the WRITES, and initializing them afterwards. ### and, or, xor ### @@ -262,10 +284,9 @@ the result in dest. * It is illegal if src OR dest OR is uninitialized. * It is illegal if dest is read-only. -* It is illegal if dest does not occur in the WRITES lists - of the current routine. +* It is illegal if dest does not occur in the WRITES of the current routine. -Affects n and z flags, requiring that they be in the WRITES lists of the +Affects n and z flags, requiring that they be in the WRITES of the current routine, and sets them as initialized afterwards. dest and src continue to be initialized afterwards. @@ -284,10 +305,9 @@ and `c` becomes the bit that was shifted off the right. * It is illegal if dest is a register besides `a`. * It is illegal if dest is read-only. * It is illegal if dest OR c is uninitialized. -* It is illegal if dest does not occur in the WRITES lists - of the current routine. +* It is illegal if dest does not occur in the WRITES of the current routine. -Affects the c flag, requiring that it be in the WRITES lists of the +Affects the c flag, requiring that it be in the WRITES of the current routine, and it continues to be initialized afterwards. ### call ### @@ -299,17 +319,15 @@ defined routine, or a vector location which contains the address of a routine which will be called indirectly. Execution will be transferred back to the current routine, when execution of the executable is finished. -Just before the call, - -* It is illegal if any of the memory locations in the target executable's - READS list is uninitialized. +* It is illegal if any of the memory locations listed in the called routine's + `inputs` are uninitialized immediately before the call. Just after the call, -* All memory locations listed as TRASHED in the called routine's WRITES - list are considered uninitialized. -* All memory locations listed as TRASHED in the called routine's OUTPUTS - list are considered initialized. +* All memory locations listed in the called routine's `trashes` are considered + to now be uninitialized. +* All memory locations listed in the called routine's `outputs` are considered + to not be initialized. ### goto ### @@ -330,8 +348,8 @@ Just before the goto, In addition, -* The target executable's WRITES lists must not include any locations - that are not already included in the current routine's WRITES lists. +* The target executable's WRITES must not include any locations + that are not already included in the current routine's WRITES. ### if ### @@ -383,8 +401,7 @@ copy more general types of data (for example, vectors,) and it trashes the `z` and `n` flags and the `a` register. * It is illegal if dest is read-only. -* It is illegal if dest does not occur in the WRITES lists of the current - routine. +* It is illegal if dest does not occur in the WRITES of the current routine. * It is illegal if src is not of same type as dest. * It is illegal if src is uninitialized. From c79bc563d3f33245652d56d229b873162312f06f Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 20 Nov 2017 13:25:09 +0000 Subject: [PATCH 03/24] Small edits to spec. --- doc/SixtyPical.md | 56 +++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index 1e27397..017267b 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -132,8 +132,8 @@ It is common to say that the `trashes` are the memory locations that are ... } -The `inputs` are sometimes called the routine's READS set, while the -`outputs` and `trashes` are collectively called the WRITES set. +The union of the `outputs` and `trashes` is sometimes collectively called +"the WRITES" of the routine, for historical reasons and as shorthand. Routines may call only routines previously defined in the program source. Thus, directly recursive routines are not allowed. (However, routines may @@ -150,9 +150,9 @@ some memory locations causes those memory locations to be uninitialized after that routine is called. At the end of a routine, all memory locations listed as outputs must be initialized. -A routine can also be declared as "external", in which case its body need -not be defined but an absolute address must be given for where the routine -is located in memory. +A literal word can given instead of the body of the routine. This word is the +absolute address of an "external" routine located in memory but not defined by +the SixtyPical program. routine chrout inputs a @@ -205,6 +205,22 @@ changed by this instruction (unless of course dest is a flag.) If and only if dest is a byte table, the index-memory-location must be given. +### copy ### + + copy , + +Reads from src and writes to dest. Differs from `st` in that is able to +copy more general types of data (for example, vectors,) and it trashes the +`z` and `n` flags and the `a` register. + +* It is illegal if dest is read-only. +* It is illegal if dest does not occur in the WRITES of the current routine. +* It is illegal if src is not of same type as dest. +* It is illegal if src is uninitialized. + +After execution, dest is considered initialized, and `z` and `n`, and +`a` are considered uninitialized. + ### add dest, src ### add , @@ -343,8 +359,8 @@ must be the final instruction in the routine. Just before the goto, -* It is illegal if any of the memory locations in the target executable's - READS list is uninitialized. +* It is illegal if any of the memory locations in the target routine's + `inputs` list is uninitialized. In addition, @@ -390,23 +406,21 @@ To simulate a "while" loop, use an `if` internal to the block, like } } until z -"until" is optional, but if omitted, must be replaced with "forever". +"until" is optional, but if omitted, must be replaced with "forever": -### copy ### + repeat { + cmp y, 25 + if z { + } + } forever - copy , +The sense of the test can be inverted with `not`. -Reads from src and writes to dest. Differs from `st` in that is able to -copy more general types of data (for example, vectors,) and it trashes the -`z` and `n` flags and the `a` register. - -* It is illegal if dest is read-only. -* It is illegal if dest does not occur in the WRITES of the current routine. -* It is illegal if src is not of same type as dest. -* It is illegal if src is uninitialized. - -After execution, dest is considered initialized, and `z` and `n`, and -`a` are considered uninitialized. + repeat { + cmp y, 25 + if z { + } + } until not z Grammar ------- From feb5729ab92544e3df5d6186813cad1cb5e925bd Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 20 Nov 2017 14:10:43 +0000 Subject: [PATCH 04/24] Minor changes to docs. --- doc/SixtyPical.md | 9 +++++++++ tests/SixtyPical Compilation.md | 8 ++++---- tests/SixtyPical Execution.md | 6 +++--- tests/SixtyPical Syntax.md | 8 ++++---- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index 017267b..d9d34ba 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -111,6 +111,13 @@ trashes) must be a subset of the vector's inputs (resp. outputs, trashes.)) trashes y @ $c000 +> TODO: need to confirm this, but, the rule is: +> +> * If it is NAMED, it is a memory address. +> * If it is a LITERAL INTEGER, it is an immediate value. +> +> However, this really needs a review, deep in the code, for how this is implemented. + Routines -------- @@ -384,6 +391,8 @@ it is treated like an empty block. * It is illegal if any location initialized at the end of the true-branch is not initialized at the end of the false-branch, and vice versa. +The sense of the test can be inverted with `not`. + ### repeat ### repeat { diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 949b73d..64a330d 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -1,15 +1,15 @@ -Sixtypical Compilation +SixtyPical Compilation ====================== This is a test suite, written in [Falderal][] format, for compiling -Sixtypical to 6502 machine code. +SixtyPical to 6502 machine code. [Falderal]: http://catseye.tc/node/Falderal - -> Functionality "Compile Sixtypical program" is implemented by + -> Functionality "Compile SixtyPical program" is implemented by -> shell command "bin/sixtypical --compile %(test-body-file) | fa-bin-to-hex" - -> Tests for functionality "Compile Sixtypical program" + -> Tests for functionality "Compile SixtyPical program" Null program. diff --git a/tests/SixtyPical Execution.md b/tests/SixtyPical Execution.md index 696a8dc..f7019f7 100644 --- a/tests/SixtyPical Execution.md +++ b/tests/SixtyPical Execution.md @@ -1,4 +1,4 @@ -Sixtypical Execution +SixtyPical Execution ==================== This is a test suite, written in [Falderal][] format, for the dynamic @@ -6,10 +6,10 @@ execution behaviour of the Sixtypical language, disgregarding static analysis. [Falderal]: http://catseye.tc/node/Falderal - -> Functionality "Execute Sixtypical program" is implemented by + -> Functionality "Execute SixtyPical program" is implemented by -> shell command "bin/sixtypical --execute %(test-body-file)" - -> Tests for functionality "Execute Sixtypical program" + -> Tests for functionality "Execute SixtyPical program" Rudimentary program. diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index ad68ae7..c653eb3 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -1,5 +1,5 @@ -Sixtypical Execution -==================== +SixtyPical Syntax +================= This is a test suite, written in [Falderal][] format, for the syntax of the Sixtypical language, disgregarding execution, static analysis, etc. @@ -9,10 +9,10 @@ but not necessarily sensible programs. [Falderal]: http://catseye.tc/node/Falderal - -> Functionality "Check syntax of Sixtypical program" is implemented by + -> Functionality "Check syntax of SixtyPical program" is implemented by -> shell command "bin/sixtypical %(test-body-file) && echo ok" - -> Tests for functionality "Check syntax of Sixtypical program" + -> Tests for functionality "Check syntax of SixtyPical program" Rudimentary program. From 22cc7bfc11090a755d2d800e19119fd8bc256777 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 20 Nov 2017 15:18:21 +0000 Subject: [PATCH 05/24] Support copy'ing a word constant to a word location. Joystick eg. --- doc/SixtyPical.md | 10 ++++---- eg/joystick.60p | 49 ++++++++++++++++++++++++++++++++++++++ src/sixtypical/analyzer.py | 12 ++++++++-- src/sixtypical/compiler.py | 32 +++++++++++++++++-------- src/sixtypical/parser.py | 12 ++++++++-- 5 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 eg/joystick.60p diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index d9d34ba..86c3ec3 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -75,13 +75,13 @@ two hundred and fifty-six byte constants, and sixty-five thousand five hundred and thirty-six word constants, - 0 - 1 + word 0 + word 1 ... - 65535 + word 65535 -Note that all byte constants serve double duty as word constants in that -context. +Note that if a word constant is between 256 and 65535, the leading `word` +token can be omitted. ### User-defined ### diff --git a/eg/joystick.60p b/eg/joystick.60p new file mode 100644 index 0000000..8783bd7 --- /dev/null +++ b/eg/joystick.60p @@ -0,0 +1,49 @@ +word screen @ 1024 +byte joy2 @ $dc00 + +word delta + +routine read_stick + inputs joy2 + outputs delta + trashes a, x, z, n +{ + ld x, joy2 + ld a, x + and a, 1 // up + if z { + copy $ffd8, delta // -40 + } else { + ld a, x + and a, 2 // down + if z { + copy word 40, delta + } else { + ld a, x + and a, 4 // left + if z { + copy $ffff, delta // -1 + } else { + ld a, x + and a, 8 // right + if z { + copy word 1, delta + } else { + copy word 0, delta + } + } + } + } +} + +routine main + inputs joy2 + outputs delta + trashes a, x, z, n, screen +{ + repeat { + call read_stick + copy delta, screen + ld a, 1 + } until z +} diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 5ac2aad..f8ac112 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -119,6 +119,8 @@ class Context(object): elif isinstance(ref, LocationRef): if ref not in self._meaningful: message = '%s in %s' % (ref.name, self.routine.name) + if kwargs.get('message'): + message += ' (%s)' % kwargs['message'] raise exception_class(message) else: raise NotImplementedError(ref) @@ -128,6 +130,8 @@ class Context(object): for ref in refs: if ref not in self._writeable: message = '%s in %s' % (ref.name, self.routine.name) + if kwargs.get('message'): + message += ' (%s)' % kwargs['message'] raise exception_class(message) def set_touched(self, *refs): @@ -270,9 +274,13 @@ class Analyzer(object): # probably not; if it wasn't meaningful in the first place, it # doesn't really matter if you modified it or not, coming out. for ref in context1.each_meaningful(): - context2.assert_meaningful(ref, exception_class=InconsistentInitializationError) + context2.assert_meaningful( + ref, exception_class=InconsistentInitializationError, message='initialized in block 1 but not in block 2' + ) for ref in context2.each_meaningful(): - context1.assert_meaningful(ref, exception_class=InconsistentInitializationError) + context1.assert_meaningful( + ref, exception_class=InconsistentInitializationError, message='initialized in block 2 but not in block 1' + ) context.set_from(context1) elif opcode == 'repeat': # it will always be executed at least once, so analyze it having diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index fdaee3b..838da57 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -276,17 +276,29 @@ class Compiler(object): self.emitter.emit(CLI()) elif opcode == 'copy': if src.type == TYPE_BYTE and dest.type == TYPE_BYTE: - src_label = self.labels[src.name] - dest_label = self.labels[dest.name] - self.emitter.emit(LDA(Absolute(src_label))) - self.emitter.emit(STA(Absolute(dest_label))) + if isinstance(src, ConstantRef): + raise NotImplementedError + else: + src_label = self.labels[src.name] + dest_label = self.labels[dest.name] + self.emitter.emit(LDA(Absolute(src_label))) + self.emitter.emit(STA(Absolute(dest_label))) elif src.type == TYPE_WORD and dest.type == TYPE_WORD: - src_label = self.labels[src.name] - dest_label = self.labels[dest.name] - self.emitter.emit(LDA(Absolute(src_label))) - self.emitter.emit(STA(Absolute(dest_label))) - self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) - self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) + if isinstance(src, ConstantRef): + dest_label = self.labels[dest.name] + hi = (src.value >> 8) & 255 + lo = src.value & 255 + self.emitter.emit(LDA(Immediate(Byte(hi)))) + self.emitter.emit(STA(Absolute(dest_label))) + self.emitter.emit(LDA(Immediate(Byte(lo)))) + self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) + else: + src_label = self.labels[src.name] + dest_label = self.labels[dest.name] + self.emitter.emit(LDA(Absolute(src_label))) + self.emitter.emit(STA(Absolute(dest_label))) + self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) + self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif isinstance(src.type, VectorType) and isinstance(dest.type, VectorType): src_label = self.labels[src.name] dest_label = self.labels[dest.name] diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index a2b3b9f..c46dbeb 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -138,7 +138,13 @@ class Parser(object): self.scanner.scan() return loc elif self.scanner.on_type('integer literal'): - loc = ConstantRef(TYPE_BYTE, int(self.scanner.token)) + value = int(self.scanner.token) + type_ = TYPE_WORD if value > 255 else TYPE_BYTE + loc = ConstantRef(type_, value) + self.scanner.scan() + return loc + elif self.scanner.consume('word'): + loc = ConstantRef(TYPE_WORD, int(self.scanner.token)) self.scanner.scan() return loc else: @@ -219,7 +225,9 @@ class Parser(object): src = self.locexpr() self.scanner.expect(',') dest = self.locexpr() - return Instr(opcode=opcode, dest=dest, src=src) + i = Instr(opcode=opcode, dest=dest, src=src) + #print repr(i) + return i elif self.scanner.consume("with"): self.scanner.expect("interrupts") self.scanner.expect("off") From b2b2582e0f8f0e054b9233f9e3e1936b25f93343 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 20 Nov 2017 15:53:34 +0000 Subject: [PATCH 06/24] Add some tests for copying literal words to word memory locations. --- tests/SixtyPical Compilation.md | 14 +++++++++++++- tests/SixtyPical Execution.md | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 64a330d..2864447 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -267,6 +267,18 @@ Copy word to word. | } = 00c0ad0fc08d0dc0ad10c08d0ec060 +Copy literal word to word. + + | word bar + | + | routine main + | outputs bar + | trashes a, n, z + | { + | copy 65535, bar + | } + = 00c0a9ff8d0bc0a9ff8d0cc060 + Copy vector to vector. | vector bar @@ -281,7 +293,7 @@ Copy vector to vector. | } = 00c0ad0fc08d0dc0ad10c08d0ec060 -Copy instruction inside an `interrupts off` block. +Copy routine to vector, inside an `interrupts off` block. | vector bar | diff --git a/tests/SixtyPical Execution.md b/tests/SixtyPical Execution.md index f7019f7..50e3d7e 100644 --- a/tests/SixtyPical Execution.md +++ b/tests/SixtyPical Execution.md @@ -435,6 +435,22 @@ Copy word to word. = y: 0 = z: 0 +Copy literal word to word. + + | word bar + | + | routine main { + | copy word 2000, bar + | } + = a: 0 + = bar: 2000 + = c: 0 + = n: 0 + = v: 0 + = x: 0 + = y: 0 + = z: 0 + Indirect call. | vector foo outputs x trashes z, n From 0e9a887ac5cbf569994468e821df8637a5b2f7ea Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 20 Nov 2017 16:14:17 +0000 Subject: [PATCH 07/24] Update the spec re what literal integers in the code mean. --- doc/SixtyPical.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index 86c3ec3..1199ab9 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -14,16 +14,20 @@ the language. Types ----- -There are seven *types* in SixtyPical: +There are five *primitive types* in SixtyPical: * bit (2 possible values) * byte (256 possible values) -* byte table (256 entries, each holding a byte) * word (65536 possible values) -* word table (256 entries, each holding a word) * routine (code stored somewhere in memory, read-only) * vector (address of a routine) +There is also one *type constructor*: + +* X table (256 entries, each holding a value of type X) + +This constructor can only be applied to bytes or words. + Memory locations ---------------- @@ -111,12 +115,17 @@ trashes) must be a subset of the vector's inputs (resp. outputs, trashes.)) trashes y @ $c000 -> TODO: need to confirm this, but, the rule is: -> -> * If it is NAMED, it is a memory address. -> * If it is a LITERAL INTEGER, it is an immediate value. -> -> However, this really needs a review, deep in the code, for how this is implemented. +Note that in the code of a routine, if a memory location is named by a +user-defined symbol, it is an address in memory, and can be read and written. +But if it is named by a literal integer, either decimal or hexadecimal, it +is a constant and can only be read (and when read always yields that constant +value. So, for instance, to read the value at `screen` above, in the code, +you would need to reference the symbol `screen`; attempting to read 1024 +would not work. + +This is actually useful, at least at this point, as you can rely on the fact +that literal integers in the code are always immediate values. (But this +may change at some point.) Routines -------- From c33e6ef0e9be6277f38681efcfade2dad01efc6c Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 20 Nov 2017 16:39:39 +0000 Subject: [PATCH 08/24] Assert that the loop variable is meaningful in repeat. Unit test. --- src/sixtypical/analyzer.py | 7 ++++--- tests/SixtyPical Analysis.md | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index f8ac112..d2d01f2 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -286,12 +286,13 @@ class Analyzer(object): # it will always be executed at least once, so analyze it having # been executed the first time. self.analyze_block(instr.block, context) - + context.assert_meaningful(src) + # now analyze it having been executed a second time, with the context # of it having already been executed. self.analyze_block(instr.block, context) - - # NB I *think* that's enough... but it might not be? + context.assert_meaningful(src) + elif opcode == 'copy': # check that their types are basically compatible if src.type == dest.type: diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 7eaff5a..82d08d6 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1017,6 +1017,23 @@ initialized at the start. | } ? UnmeaningfulReadError: y in main +And if you trash the test expression (i.e. `z` in the below) inside the loop, +this is an error too. + + | word one : 0 + | word two : 0 + | + | routine main + | inputs one, two + | outputs two + | trashes a, z, n + | { + | repeat { + | copy one, two + | } until z + | } + ? UnmeaningfulReadError: z in main + ### copy ### Can't `copy` from a memory location that isn't initialized. From b638671eaf83b5c6ca5e942d18765b9f9502db73 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Tue, 21 Nov 2017 11:13:21 +0000 Subject: [PATCH 09/24] Add draft of Design Goals document. --- HISTORY.markdown | 6 ++++++ README.markdown | 4 ++-- doc/Design Goals.md | 27 +++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 doc/Design Goals.md diff --git a/HISTORY.markdown b/HISTORY.markdown index 316e86a..a1a376a 100644 --- a/HISTORY.markdown +++ b/HISTORY.markdown @@ -1,6 +1,12 @@ History of SixtyPical ===================== +0.8 +--- + +* Explicit word literals prefixed with `word` token. +* Can `copy` literals into user-defined destinations. + 0.7 --- diff --git a/README.markdown b/README.markdown index c535e6d..edb6d1e 100644 --- a/README.markdown +++ b/README.markdown @@ -29,9 +29,9 @@ The current development version of SixtyPical is 0.8-PRE. Documentation ------------- -* Design Goals — coming soon. +* [Design Goals](doc/Design%20Goals.md) * [SixtyPical specification](doc/SixtyPical.md) -* [SixtyPical history](HISTORY.md) +* [SixtyPical revision history](HISTORY.md) * [Literate test suite for SixtyPical syntax](tests/SixtyPical%20Syntax.md) * [Literate test suite for SixtyPical execution](tests/SixtyPical%20Execution.md) * [Literate test suite for SixtyPical analysis](tests/SixtyPical%20Analysis.md) diff --git a/doc/Design Goals.md b/doc/Design Goals.md new file mode 100644 index 0000000..2da807b --- /dev/null +++ b/doc/Design Goals.md @@ -0,0 +1,27 @@ +Design Goals for SixtyPical +=========================== + +(draft) + +The intent of SixtyPical is to have a very low-level language that +benefits from abstract interpretation. + +"Very low-level" means, on a comparable level of abstraction as +assembly language. + +In the original vision for SixtyPical, SixtyPical instructions mapped +nearly 1:1 to 6502 instructions. However, many times when programming +in 6502 you're using idioms (e.g. adding a 16-bit constant to a 16-bit +value stored in 2 bytes) and it's just massively easier to analyze such +actions when they are represented by a single instruction. + +So SixtyPical instructions are similar to, inspired by, and have +analogous restrictions as 6502 instructions, but in many ways, they +are more abstract. For example, `copy`. + +The intent is that programming in SixtyPical is a lot like programming +in 6052 assembler, but it's harder to make a stupid error that you have +to spend a lot of time debugging. + +The intent is not to make it absolutely impossible to make such errors, +just harder. From b47cfc7b9165613f0b675db2994c267a6932c8be Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Tue, 21 Nov 2017 12:10:31 +0000 Subject: [PATCH 10/24] Add some notes to the TODO section of the README. --- README.markdown | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index edb6d1e..d291ba8 100644 --- a/README.markdown +++ b/README.markdown @@ -41,14 +41,22 @@ Documentation TODO ---- -* `word table` type. -* `vector table` type. -* zero-page memory locations. -* indirect addressing. -* `low` and `high` address operators (turn `word` type into `byte`.) Possibly. -* save registers on stack or in memory (this preserves them = not trashed) +`byte buffer` and `pointer` types. Basically, a `buffer` is a table that can +be longer than 256 bytes, and a `pointer` is an address within a buffer. +A `pointer` is implemented as a zero-page memory location, and accessing the +buffer pointed to is implemented with indirect addressing. We will likely +need a new instruction for this, or at least a mode, and it will likely +trash the `x` register, and it will likely be unchecked, at least to start. +Basically, this is to allow us to write to the `byte buffer[2048]` known as +"the screen". -At some point... +`word table` and `vector table` types. + +`low` and `high` address operators (turn `word` type into `byte`.) Possibly. + +Save registers on stack or in memory (this preserves them = not trashed). + +And at some point... * initialized `byte table` memory locations * always analyze before executing or compiling, unless told not to From ff5d635307c2cf5ed642433ef546f9945334f5f2 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Thu, 23 Nov 2017 17:08:40 +0000 Subject: [PATCH 11/24] Expand on a note in the TODO. --- README.markdown | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/README.markdown b/README.markdown index d291ba8..e350f47 100644 --- a/README.markdown +++ b/README.markdown @@ -41,22 +41,38 @@ Documentation TODO ---- -`byte buffer` and `pointer` types. Basically, a `buffer` is a table that can +### `byte buffer` and `pointer` types + +Basically, a `buffer` is a table that can be longer than 256 bytes, and a `pointer` is an address within a buffer. A `pointer` is implemented as a zero-page memory location, and accessing the -buffer pointed to is implemented with indirect addressing. We will likely -need a new instruction for this, or at least a mode, and it will likely -trash the `x` register, and it will likely be unchecked, at least to start. -Basically, this is to allow us to write to the `byte buffer[2048]` known as -"the screen". +buffer pointed to is implemented with "indirect indexed" addressing, as in -`word table` and `vector table` types. + LDA ($02), Y -`low` and `high` address operators (turn `word` type into `byte`.) Possibly. +We will likely have a new mode of `copy` for this, like -Save registers on stack or in memory (this preserves them = not trashed). + copy 100, p + y -And at some point... +where `p` is a user-defined storage location of `pointer` type, and `+ y` +is mandatory (and you can/should set it to zero yourself if you want.) + +This instruction will likely be unchecked, at least to start. Basically, +this is to allow us to write to the `byte buffer[2048]` known as "the screen", +(and doing that is valuable enough that we can sacrifice checking, for now.) + +### `word table` and `vector table` types + +### `low` and `high` address operators + +To turn `word` type into `byte`. + +### save registers on stack + +This preserves them, so semantically, they can be used even though they +are trashed inside the block. + +### And at some point... * initialized `byte table` memory locations * always analyze before executing or compiling, unless told not to From 42438dd97f14add5c53f1f548774ec0ccd2c1549 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 24 Nov 2017 11:30:20 +0000 Subject: [PATCH 12/24] Beginning of: buffers and pointers. --- doc/SixtyPical.md | 2 +- src/sixtypical/model.py | 11 +++++++++ src/sixtypical/parser.py | 46 ++++++++++++++++++++++++-------------- src/sixtypical/scanner.py | 2 +- tests/SixtyPical Syntax.md | 24 ++++++++++++++++++++ 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index 1199ab9..a6ac3e5 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -26,7 +26,7 @@ There is also one *type constructor*: * X table (256 entries, each holding a value of type X) -This constructor can only be applied to bytes or words. +This constructor can only be applied to one type, `byte`. Memory locations ---------------- diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index e509e95..b8419b7 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -58,6 +58,17 @@ class VectorType(ExecutableType): super(VectorType, self).__init__('vector', **kwargs) +class BufferType(Type): + def __init__(self, size): + self.size = size + self.name = 'buffer[%s]' % self.size + + +class PointerType(Type): + def __init__(self): + self.name = 'pointer' + + class Ref(object): def is_constant(self): """read-only means that the program cannot change the value diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index c46dbeb..85efc4d 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -3,7 +3,7 @@ from sixtypical.ast import Program, Defn, Routine, Block, Instr from sixtypical.model import ( TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE, - RoutineType, VectorType, ExecutableType, + RoutineType, VectorType, ExecutableType, BufferType, PointerType, LocationRef, ConstantRef ) from sixtypical.scanner import Scanner @@ -32,7 +32,7 @@ class Parser(object): def program(self): defns = [] routines = [] - while self.scanner.on('byte', 'word', 'vector'): + while self.scanner.on('byte', 'word', 'vector', 'buffer', 'pointer'): defn = self.defn() name = defn.name if name in self.symbols: @@ -50,25 +50,15 @@ class Parser(object): return Program(defns=defns, routines=routines) def defn(self): - type = None - if self.scanner.consume('byte'): - type = TYPE_BYTE - if self.scanner.consume('table'): - type = TYPE_BYTE_TABLE - elif self.scanner.consume('word'): - type = TYPE_WORD - if self.scanner.consume('table'): - type = TYPE_WORD_TABLE - else: - self.scanner.expect('vector') - type = 'vector' # will be resolved to a Type below + type_ = self.defn_type() + self.scanner.check_type('identifier') name = self.scanner.token self.scanner.scan() (inputs, outputs, trashes) = self.constraints() - if type == 'vector': - type = VectorType(inputs=inputs, outputs=outputs, trashes=trashes) + if type_ == 'vector': + type_ = VectorType(inputs=inputs, outputs=outputs, trashes=trashes) elif inputs or outputs or trashes: raise SyntaxError("Cannot apply constraints to non-vector type") @@ -87,10 +77,32 @@ class Parser(object): if initial is not None and addr is not None: raise SyntaxError("Definition cannot have both initial value and explicit address") - location = LocationRef(type, name) + location = LocationRef(type_, name) return Defn(name=name, addr=addr, initial=initial, location=location) + def defn_type(self): + if self.scanner.consume('byte'): + if self.scanner.consume('table'): + return TYPE_BYTE_TABLE + return TYPE_BYTE + elif self.scanner.consume('word'): + if self.scanner.consume('table'): + return TYPE_WORD_TABLE + return TYPE_WORD + elif self.scanner.consume('vector'): + return 'vector' # will be resolved to a Type by caller + elif self.scanner.consume('buffer'): + self.scanner.expect('[') + self.scanner.check_type('integer literal') + size = int(self.scanner.token) + self.scanner.scan() + self.scanner.expect(']') + return BufferType(size) + else: + self.scanner.expect('pointer') + return PointerType() + def constraints(self): inputs = set() outputs = set() diff --git a/src/sixtypical/scanner.py b/src/sixtypical/scanner.py index c538c3e..3663e9c 100644 --- a/src/sixtypical/scanner.py +++ b/src/sixtypical/scanner.py @@ -29,7 +29,7 @@ class Scanner(object): self.token = None self.type = 'EOF' return - if self.scan_pattern(r'\,|\@|\+|\:|\<|\>|\{|\}', 'operator'): + if self.scan_pattern(r'\,|\@|\+|\:|\<|\>|\{|\}|\[|\]', 'operator'): return if self.scan_pattern(r'\d+', 'integer literal'): return diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index c653eb3..8528e5b 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -123,6 +123,30 @@ Repeat with not | } = ok +User-defined memory addresses of different types. + + | byte byt + | word wor + | vector vec + | byte table tab + | + | routine main { + | } + = ok + +Buffer and pointer types. + + | byte byt + | word wor + | vector vec + | byte table tab + | buffer[2048] buf + | pointer ptr + | + | routine main { + | } + = ok + Explicit memory address. | byte screen @ 1024 From c91574186b06f169b94b86bf98fba478744781b2 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 24 Nov 2017 12:35:36 +0000 Subject: [PATCH 13/24] First pass of buffer, pointer, copy b, [p] + y, indirect indexed. --- README.markdown | 4 ++-- src/sixtypical/analyzer.py | 27 +++++++++++++++++++++------ src/sixtypical/compiler.py | 7 ++++--- src/sixtypical/evaluator.py | 9 +++++++++ src/sixtypical/gen6502.py | 14 ++++++++++++++ src/sixtypical/parser.py | 9 ++++++++- tests/SixtyPical Analysis.md | 36 ++++++++++++++++++++++++++++++++++++ tests/SixtyPical Syntax.md | 22 +++++++++++----------- 8 files changed, 105 insertions(+), 23 deletions(-) diff --git a/README.markdown b/README.markdown index e350f47..60d8a55 100644 --- a/README.markdown +++ b/README.markdown @@ -52,9 +52,9 @@ buffer pointed to is implemented with "indirect indexed" addressing, as in We will likely have a new mode of `copy` for this, like - copy 100, p + y + copy 100, [ptr] + y -where `p` is a user-defined storage location of `pointer` type, and `+ y` +where `ptr` is a user-defined storage location of `pointer` type, and `+ y` is mandatory (and you can/should set it to zero yourself if you want.) This instruction will likely be unchecked, at least to start. Basically, diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index d2d01f2..16c83dc 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -2,10 +2,9 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( - TYPE_BYTE, TYPE_BYTE_TABLE, - VectorType, ExecutableType, + TYPE_BYTE, TYPE_BYTE_TABLE, BufferType, PointerType, VectorType, ExecutableType, ConstantRef, LocationRef, - REG_A, FLAG_Z, FLAG_N, FLAG_V, FLAG_C + REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C ) @@ -294,15 +293,18 @@ class Analyzer(object): context.assert_meaningful(src) elif opcode == 'copy': - # check that their types are basically compatible + # check that their types are compatible if src.type == dest.type: pass elif isinstance(src.type, ExecutableType) and \ isinstance(dest.type, VectorType): pass + elif isinstance(src.type, BufferType) and \ + isinstance(dest.type, PointerType): + pass else: raise TypeMismatchError((src, dest)) - + # if dealing with routines and vectors, # check that they're not incompatible if isinstance(src.type, ExecutableType) and \ @@ -313,11 +315,24 @@ class Analyzer(object): raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs) if not (src.type.trashes <= dest.type.trashes): raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes) - + context.assert_meaningful(src) context.set_written(dest) context.set_touched(REG_A, FLAG_Z, FLAG_N) context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N) + + elif opcode == 'copy[]+y': + # check that their types are compatible + if src.type == TYPE_BYTE and isinstance(dest.type, PointerType): + pass + else: + raise TypeMismatchError((src, dest)) + + context.assert_meaningful(src, REG_Y) + context.set_written(dest) + context.set_touched(REG_A, FLAG_Z, FLAG_N) + context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N) + elif opcode == 'with-sei': self.analyze_block(instr.block, context) elif opcode == 'goto': diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 838da57..049504e 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -3,13 +3,12 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( ConstantRef, - TYPE_BIT, TYPE_BYTE, TYPE_WORD, - RoutineType, VectorType, + TYPE_BIT, TYPE_BYTE, TYPE_WORD, BufferType, PointerType, RoutineType, VectorType, REG_A, REG_X, REG_Y, FLAG_C ) from sixtypical.emitter import Byte, Label, Offset, LowAddressByte, HighAddressByte from sixtypical.gen6502 import ( - Immediate, Absolute, AbsoluteX, AbsoluteY, Indirect, Relative, + Immediate, Absolute, AbsoluteX, AbsoluteY, Indirect, IndirectY, Relative, LDA, LDX, LDY, STA, STX, STY, TAX, TAY, TXA, TYA, CLC, SEC, ADC, SBC, ROL, ROR, @@ -299,6 +298,8 @@ class Compiler(object): self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) + elif isinstance(src.type, BufferType) and isinstance(dest.type, PointerType): + raise NotImplementedError('zeropage') elif isinstance(src.type, VectorType) and isinstance(dest.type, VectorType): src_label = self.labels[src.name] dest_label = self.labels[dest.name] diff --git a/src/sixtypical/evaluator.py b/src/sixtypical/evaluator.py index f50e6a7..b0b7a42 100644 --- a/src/sixtypical/evaluator.py +++ b/src/sixtypical/evaluator.py @@ -196,6 +196,15 @@ class Evaluator(object): context.set(REG_A, 0) context.set(FLAG_Z, 0) context.set(FLAG_N, 0) + elif opcode == 'copy[]': + addr = context.get(dest) + # memloc = memory[addr + context.get(REG_Y)] + # context.set(memloc, context.get(src)) + context.set(dest, context.get(src)) + # these are trashed; so could be anything really + context.set(REG_A, 0) + context.set(FLAG_Z, 0) + context.set(FLAG_N, 0) elif opcode == 'with-sei': self.eval_block(instr.block) else: diff --git a/src/sixtypical/gen6502.py b/src/sixtypical/gen6502.py index e0be7a4..cad4be0 100644 --- a/src/sixtypical/gen6502.py +++ b/src/sixtypical/gen6502.py @@ -70,6 +70,18 @@ class Indirect(AddressingMode): return self.value.serialize() +class IndirectY(AddressingMode): + def __init__(self, value): + assert isinstance(value, Label) + self.value = value + + def size(self): + return 2 + + def serialize(self, addr=None): + return self.value.serialize() + + class Relative(AddressingMode): def __init__(self, value): assert isinstance(value, Label) @@ -244,6 +256,7 @@ class LDA(Instruction): Absolute: 0xad, AbsoluteX: 0xbd, AbsoluteY: 0xb9, + IndirectY: 0xb1, } @@ -320,6 +333,7 @@ class STA(Instruction): Absolute: 0x8d, AbsoluteX: 0x9d, AbsoluteY: 0x99, + IndirectY: 0x91, } diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 85efc4d..68f01d6 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -236,7 +236,14 @@ class Parser(object): self.scanner.scan() src = self.locexpr() self.scanner.expect(',') - dest = self.locexpr() + if self.scanner.consume('['): + dest = self.locexpr() + self.scanner.expect(']') + self.scanner.expect('+') + self.scanner.expect('y') + opcode = 'copy[]+y' + else: + dest = self.locexpr() i = Instr(opcode=opcode, dest=dest, src=src) #print repr(i) return i diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 82d08d6..4b48df9 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1172,6 +1172,42 @@ Can't `copy` from a `word` to a `byte`. | } ? TypeMismatchError +### copy[] ### + +Buffers and pointers. + +Note that `copy buf, ptr` should probably be `copy ^buf, ptr` and `^buf` should not +be considered "reading" buf and should not require it in `inputs` for that reason. + + | buffer[2048] buf + | pointer ptr + | + | routine main + | inputs buf + | outputs buf, y + | trashes a, z, n, ptr + | { + | ld y, 0 + | copy buf, ptr + | copy 123, [ptr] + y + | } + = ok + +It does use `y`. + + | buffer[2048] buf + | pointer ptr + | + | routine main + | inputs buf + | outputs buf + | trashes a, z, n, ptr + | { + | copy buf, ptr + | copy 123, [ptr] + y + | } + ? UnmeaningfulReadError + ### routines ### Routines are constants. You need not, and in fact cannot, specify a constant diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index 8528e5b..66c761f 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -125,17 +125,6 @@ Repeat with not User-defined memory addresses of different types. - | byte byt - | word wor - | vector vec - | byte table tab - | - | routine main { - | } - = ok - -Buffer and pointer types. - | byte byt | word wor | vector vec @@ -332,3 +321,14 @@ goto. | goto foo | } ? SyntaxError + +Buffers and pointers. + + | buffer[2048] buf + | pointer ptr + | + | routine main { + | copy buf, ptr + | copy 123, [ptr] + y + | } + = ok From e41dd1aa0198e0aeaa495d710937e70b191d605e Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 24 Nov 2017 13:09:10 +0000 Subject: [PATCH 14/24] Attempt to add Zero Page addressing; the emitter may need rethink. --- README.markdown | 11 ++++++++--- src/sixtypical/compiler.py | 11 +++++++++-- src/sixtypical/emitter.py | 4 ++++ src/sixtypical/gen6502.py | 16 +++++++++++++++- tests/SixtyPical Compilation.md | 16 ++++++++++++++++ 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index 60d8a55..b38da83 100644 --- a/README.markdown +++ b/README.markdown @@ -49,13 +49,18 @@ A `pointer` is implemented as a zero-page memory location, and accessing the buffer pointed to is implemented with "indirect indexed" addressing, as in LDA ($02), Y + STA ($02), Y We will likely have a new mode of `copy` for this, like - copy 100, [ptr] + y + copy ^buf, ptr // this is the only way to initialize a pointer + add ptr, 4 // ok, but only if it does not exceed buffer's size + ld y, 0 // you must set this to something yourself + copy [ptr] + y, byt // read memory through pointer, into byte + copy 100, [ptr] + y // write memory through pointer (still trashes a) -where `ptr` is a user-defined storage location of `pointer` type, and `+ y` -is mandatory (and you can/should set it to zero yourself if you want.) +where `ptr` is a user-defined storage location of `pointer` type, and the +`+ y` part is mandatory. This instruction will likely be unchecked, at least to start. Basically, this is to allow us to write to the `byte buffer[2048]` known as "the screen", diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 049504e..6f58e90 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -8,7 +8,7 @@ from sixtypical.model import ( ) from sixtypical.emitter import Byte, Label, Offset, LowAddressByte, HighAddressByte from sixtypical.gen6502 import ( - Immediate, Absolute, AbsoluteX, AbsoluteY, Indirect, IndirectY, Relative, + Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative, LDA, LDX, LDY, STA, STX, STY, TAX, TAY, TXA, TYA, CLC, SEC, ADC, SBC, ROL, ROR, @@ -299,7 +299,14 @@ class Compiler(object): self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif isinstance(src.type, BufferType) and isinstance(dest.type, PointerType): - raise NotImplementedError('zeropage') + # TODO: this maybe should not be the 'copy' opcode at all that means this + src_label = self.labels[src.name] + dest_label = self.labels[dest.name] + self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) + self.emitter.emit(STA(ZeroPage(dest_label))) + self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) + #self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) + self.emitter.emit(STA(ZeroPage(dest_label))) elif isinstance(src.type, VectorType) and isinstance(dest.type, VectorType): src_label = self.labels[src.name] dest_label = self.labels[dest.name] diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index 5ae2225..651d15e 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -61,6 +61,10 @@ class Label(Emittable): assert self.addr is not None, "unresolved label: %s" % self.name return Byte(self.addr - (addr + 2)).serialize() + def serialize_as_zero_page(self, offset=0): + assert self.addr is not None, "unresolved label: %s" % self.name + return Byte(self.addr + offset).serialize() + def __repr__(self): addrs = ', addr=%r' % self.addr if self.addr is not None else '' return "%s(%r%s)" % (self.__class__.__name__, self.name, addrs) diff --git a/src/sixtypical/gen6502.py b/src/sixtypical/gen6502.py index cad4be0..b72471e 100644 --- a/src/sixtypical/gen6502.py +++ b/src/sixtypical/gen6502.py @@ -58,6 +58,18 @@ class AbsoluteY(Absolute): pass +class ZeroPage(AddressingMode): + def __init__(self, value): + assert isinstance(value, (Label, Offset)) + self.value = value + + def size(self): + return 1 + + def serialize(self, addr=None): + return self.value.serialize_as_zero_page() + + class Indirect(AddressingMode): def __init__(self, value): assert isinstance(value, Label) @@ -76,7 +88,7 @@ class IndirectY(AddressingMode): self.value = value def size(self): - return 2 + return 1 def serialize(self, addr=None): return self.value.serialize() @@ -257,6 +269,7 @@ class LDA(Instruction): AbsoluteX: 0xbd, AbsoluteY: 0xb9, IndirectY: 0xb1, + ZeroPage: 0xa5, } @@ -334,6 +347,7 @@ class STA(Instruction): AbsoluteX: 0x9d, AbsoluteY: 0x99, IndirectY: 0x91, + ZeroPage: 0x85, } diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 2864447..b0e5398 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -341,3 +341,19 @@ goto. | goto bar | } = 00c0a0c84c06c060a2c860 + +Buffers and pointers. + + > | buffer[2048] buf + > | pointer ptr : 254 + > | + > | routine main + > | inputs buf + > | outputs buf, y + > | trashes a, z, n, ptr + > | { + > | ld y, 0 + > | copy buf, ptr + > | // copy 123, [ptr] + y + > | } + > = 00c0a000a90b850dc0a9c0850ec060 From c8c69a2a7deec0ca5bc71e0b4daaf4acdc991c68 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 24 Nov 2017 13:42:14 +0000 Subject: [PATCH 15/24] Fix problem with test case, and with serializing as zero page. --- eg/buffer.60p | 12 ++++++++++++ src/sixtypical/compiler.py | 5 ++--- src/sixtypical/emitter.py | 3 +++ tests/SixtyPical Compilation.md | 26 +++++++++++++------------- 4 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 eg/buffer.60p diff --git a/eg/buffer.60p b/eg/buffer.60p new file mode 100644 index 0000000..84cb784 --- /dev/null +++ b/eg/buffer.60p @@ -0,0 +1,12 @@ +buffer[2048] buf +pointer ptr @ 254 + +routine main + inputs buf + outputs buf, y + trashes a, z, n, ptr +{ + ld y, 0 + copy buf, ptr + // copy 123, [ptr] + y +} diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 6f58e90..a649b29 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -301,12 +301,11 @@ class Compiler(object): elif isinstance(src.type, BufferType) and isinstance(dest.type, PointerType): # TODO: this maybe should not be the 'copy' opcode at all that means this src_label = self.labels[src.name] - dest_label = self.labels[dest.name] + dest_label = self.labels[dest.name] self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) self.emitter.emit(STA(ZeroPage(dest_label))) self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) - #self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) - self.emitter.emit(STA(ZeroPage(dest_label))) + self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) elif isinstance(src.type, VectorType) and isinstance(dest.type, VectorType): src_label = self.labels[src.name] dest_label = self.labels[dest.name] diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index 651d15e..82531d9 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -82,6 +82,9 @@ class Offset(Emittable): def serialize(self, addr=None): return self.label.serialize(offset=self.offset) + def serialize_as_zero_page(self, offset=0): + return self.label.serialize_as_zero_page(offset=self.offset) + def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset) diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index b0e5398..b001f53 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -344,16 +344,16 @@ goto. Buffers and pointers. - > | buffer[2048] buf - > | pointer ptr : 254 - > | - > | routine main - > | inputs buf - > | outputs buf, y - > | trashes a, z, n, ptr - > | { - > | ld y, 0 - > | copy buf, ptr - > | // copy 123, [ptr] + y - > | } - > = 00c0a000a90b850dc0a9c0850ec060 + | buffer[2048] buf + | pointer ptr @ 254 + | + | routine main + | inputs buf + | outputs buf, y + | trashes a, z, n, ptr + | { + | ld y, 0 + | copy buf, ptr + | // copy 123, [ptr] + y + | } + = 00c0a000a90b85fea9c085ff60 From 19a196f765d3dcd4603cfb83cffc1d6abc1c3f39 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 24 Nov 2017 16:56:55 +0000 Subject: [PATCH 16/24] Compile copy[]+y. --- eg/buffer.60p | 2 +- src/sixtypical/compiler.py | 10 ++++++++++ src/sixtypical/gen6502.py | 12 ++---------- tests/SixtyPical Compilation.md | 17 ++++++++++++++++- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/eg/buffer.60p b/eg/buffer.60p index 84cb784..5b63103 100644 --- a/eg/buffer.60p +++ b/eg/buffer.60p @@ -8,5 +8,5 @@ routine main { ld y, 0 copy buf, ptr - // copy 123, [ptr] + y + copy 123, [ptr] + y } diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index a649b29..192f81f 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -322,5 +322,15 @@ class Compiler(object): self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) else: raise NotImplementedError(src.type) + elif opcode == 'copy[]+y': + if src.type == TYPE_BYTE and isinstance(dest.type, PointerType): + if isinstance(src, ConstantRef): + dest_label = self.labels[dest.name] + self.emitter.emit(LDA(Immediate(Byte(src.value)))) + self.emitter.emit(STA(IndirectY(dest_label))) + else: + raise NotImplementedError((src.type, dest.type)) + else: + raise NotImplementedError((src.type, dest.type)) else: raise NotImplementedError(opcode) diff --git a/src/sixtypical/gen6502.py b/src/sixtypical/gen6502.py index b72471e..8659ab6 100644 --- a/src/sixtypical/gen6502.py +++ b/src/sixtypical/gen6502.py @@ -82,16 +82,8 @@ class Indirect(AddressingMode): return self.value.serialize() -class IndirectY(AddressingMode): - def __init__(self, value): - assert isinstance(value, Label) - self.value = value - - def size(self): - return 1 - - def serialize(self, addr=None): - return self.value.serialize() +class IndirectY(ZeroPage): + pass class Relative(AddressingMode): diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index b001f53..5e2df28 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -354,6 +354,21 @@ Buffers and pointers. | { | ld y, 0 | copy buf, ptr - | // copy 123, [ptr] + y | } = 00c0a000a90b85fea9c085ff60 + +Writing through a pointer. + + | buffer[2048] buf + | pointer ptr @ 254 + | + | routine main + | inputs buf + | outputs buf, y + | trashes a, z, n, ptr + | { + | ld y, 0 + | copy buf, ptr + | copy 123, [ptr] + y + | } + = 00c0a000a90f85fea9c085ffa97b91fe60 From a95cbb0f47131d2c1085637f0c49827026ec21ce Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 1 Dec 2017 11:44:40 +0000 Subject: [PATCH 17/24] Introduce IndirectRef and use it instead of adhoc 'copy[]+y' opcode. --- src/sixtypical/analyzer.py | 28 ++++++++++++++-------------- src/sixtypical/compiler.py | 24 ++++++++++++------------ src/sixtypical/evaluator.py | 17 +++++++---------- src/sixtypical/model.py | 23 ++++++++++++++++++++++- src/sixtypical/parser.py | 27 ++++++++++++++------------- 5 files changed, 69 insertions(+), 50 deletions(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 16c83dc..60e2718 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -3,7 +3,7 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( TYPE_BYTE, TYPE_BYTE_TABLE, BufferType, PointerType, VectorType, ExecutableType, - ConstantRef, LocationRef, + ConstantRef, LocationRef, IndirectRef, REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C ) @@ -294,7 +294,13 @@ class Analyzer(object): elif opcode == 'copy': # check that their types are compatible - if src.type == dest.type: + + if isinstance(dest, IndirectRef): + if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): + pass + else: + raise TypeMismatchError((src, dest)) + elif src.type == dest.type: pass elif isinstance(src.type, ExecutableType) and \ isinstance(dest.type, VectorType): @@ -316,20 +322,14 @@ class Analyzer(object): if not (src.type.trashes <= dest.type.trashes): raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes) - context.assert_meaningful(src) - context.set_written(dest) - context.set_touched(REG_A, FLAG_Z, FLAG_N) - context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N) - - elif opcode == 'copy[]+y': - # check that their types are compatible - if src.type == TYPE_BYTE and isinstance(dest.type, PointerType): - pass + if isinstance(dest, IndirectRef): + context.assert_meaningful(src, REG_Y) + # TODO this will need to be more sophisticated. it's the thing ref points to that is written, not ref itself. + context.set_written(dest.ref) else: - raise TypeMismatchError((src, dest)) + context.assert_meaningful(src) + context.set_written(dest) - context.assert_meaningful(src, REG_Y) - context.set_written(dest) context.set_touched(REG_A, FLAG_Z, FLAG_N) context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N) diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 192f81f..eb5aee5 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -2,7 +2,7 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( - ConstantRef, + ConstantRef, IndirectRef, TYPE_BIT, TYPE_BYTE, TYPE_WORD, BufferType, PointerType, RoutineType, VectorType, REG_A, REG_X, REG_Y, FLAG_C ) @@ -274,7 +274,17 @@ class Compiler(object): self.compile_block(instr.block) self.emitter.emit(CLI()) elif opcode == 'copy': - if src.type == TYPE_BYTE and dest.type == TYPE_BYTE: + if isinstance(dest, IndirectRef): + if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): + if isinstance(src, ConstantRef): + dest_label = self.labels[dest.ref.name] + self.emitter.emit(LDA(Immediate(Byte(src.value)))) + self.emitter.emit(STA(IndirectY(dest_label))) + else: + raise NotImplementedError((src, dest)) + else: + raise NotImplementedError((src, dest)) + elif src.type == TYPE_BYTE and dest.type == TYPE_BYTE: if isinstance(src, ConstantRef): raise NotImplementedError else: @@ -322,15 +332,5 @@ class Compiler(object): self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) else: raise NotImplementedError(src.type) - elif opcode == 'copy[]+y': - if src.type == TYPE_BYTE and isinstance(dest.type, PointerType): - if isinstance(src, ConstantRef): - dest_label = self.labels[dest.name] - self.emitter.emit(LDA(Immediate(Byte(src.value)))) - self.emitter.emit(STA(IndirectY(dest_label))) - else: - raise NotImplementedError((src.type, dest.type)) - else: - raise NotImplementedError((src.type, dest.type)) else: raise NotImplementedError(opcode) diff --git a/src/sixtypical/evaluator.py b/src/sixtypical/evaluator.py index b0b7a42..65feb88 100644 --- a/src/sixtypical/evaluator.py +++ b/src/sixtypical/evaluator.py @@ -2,7 +2,7 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( - ConstantRef, LocationRef, PartRef, + ConstantRef, LocationRef, PartRef, IndirectRef, REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C ) @@ -191,15 +191,12 @@ class Evaluator(object): while context.get(src) == 0: self.eval_block(instr.block, context) elif opcode == 'copy': - context.set(dest, context.get(src)) - # these are trashed; so could be anything really - context.set(REG_A, 0) - context.set(FLAG_Z, 0) - context.set(FLAG_N, 0) - elif opcode == 'copy[]': - addr = context.get(dest) - # memloc = memory[addr + context.get(REG_Y)] - # context.set(memloc, context.get(src)) + if isinstance(src, IndirectRef): + raise NotImplementedError("this doesn't actually work") + src = src.ref + if isinstance(dest, IndirectRef): + raise NotImplementedError("this doesn't actually work") + dest = dest.ref context.set(dest, context.get(src)) # these are trashed; so could be anything really context.set(REG_A, 0) diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index b8419b7..b8b636a 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -87,7 +87,7 @@ class LocationRef(Ref): # but because we store the type in here and we want to treat # these objects as immutable, we compare the types, too. # Not sure if very wise. - return isinstance(other, LocationRef) and ( + return isinstance(other, self.__class__) and ( other.name == self.name and other.type == self.type ) @@ -101,6 +101,27 @@ class LocationRef(Ref): return isinstance(self.type, RoutineType) +class IndirectRef(Ref): + def __init__(self, ref): + self.ref = ref + + def __eq__(self, other): + return self.ref == other.ref + + def __hash__(self): + return hash(hash('[]') ^ hash(self.ref)) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, self.ref) + + @property + def name(self): + return '[{}]+y'.format(self.ref.name) + + def is_constant(self): + return False + + class PartRef(Ref): """For 'low byte of' location and 'high byte of' location modifiers. diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 68f01d6..341c8d6 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -4,7 +4,7 @@ from sixtypical.ast import Program, Defn, Routine, Block, Instr from sixtypical.model import ( TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE, RoutineType, VectorType, ExecutableType, BufferType, PointerType, - LocationRef, ConstantRef + LocationRef, ConstantRef, IndirectRef, ) from sixtypical.scanner import Scanner @@ -164,6 +164,16 @@ class Parser(object): self.scanner.scan() return loc + def indlocexpr(self): + if self.scanner.consume('['): + loc = self.locexpr() + self.scanner.expect(']') + self.scanner.expect('+') + self.scanner.expect('y') + return IndirectRef(loc) + else: + return self.locexpr() + def block(self): instrs = [] self.scanner.expect('{') @@ -234,19 +244,10 @@ class Parser(object): elif self.scanner.token in ("copy",): opcode = self.scanner.token self.scanner.scan() - src = self.locexpr() + src = self.indlocexpr() self.scanner.expect(',') - if self.scanner.consume('['): - dest = self.locexpr() - self.scanner.expect(']') - self.scanner.expect('+') - self.scanner.expect('y') - opcode = 'copy[]+y' - else: - dest = self.locexpr() - i = Instr(opcode=opcode, dest=dest, src=src) - #print repr(i) - return i + dest = self.indlocexpr() + return Instr(opcode=opcode, dest=dest, src=src) elif self.scanner.consume("with"): self.scanner.expect("interrupts") self.scanner.expect("off") From 32389e442262104205d92f0342332e8d7f34c856 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 1 Dec 2017 12:36:58 +0000 Subject: [PATCH 18/24] Require that the program does ^buf to get at the address of buf. --- eg/buffer.60p | 2 +- src/sixtypical/analyzer.py | 46 ++++++++++++++++++--------------- src/sixtypical/compiler.py | 20 +++++++------- src/sixtypical/model.py | 23 ++++++++++++++++- src/sixtypical/parser.py | 5 +++- src/sixtypical/scanner.py | 2 +- tests/SixtyPical Analysis.md | 13 ++++------ tests/SixtyPical Compilation.md | 5 ++-- tests/SixtyPical Syntax.md | 2 +- 9 files changed, 72 insertions(+), 46 deletions(-) diff --git a/eg/buffer.60p b/eg/buffer.60p index 5b63103..8783a11 100644 --- a/eg/buffer.60p +++ b/eg/buffer.60p @@ -7,6 +7,6 @@ routine main trashes a, z, n, ptr { ld y, 0 - copy buf, ptr + copy ^buf, ptr copy 123, [ptr] + y } diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 60e2718..7f61bd5 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -3,7 +3,7 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( TYPE_BYTE, TYPE_BYTE_TABLE, BufferType, PointerType, VectorType, ExecutableType, - ConstantRef, LocationRef, IndirectRef, + ConstantRef, LocationRef, IndirectRef, AddressRef, REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C ) @@ -113,7 +113,7 @@ class Context(object): def assert_meaningful(self, *refs, **kwargs): exception_class = kwargs.get('exception_class', UnmeaningfulReadError) for ref in refs: - if isinstance(ref, ConstantRef) or ref in self.routines: + if ref.is_constant() or ref in self.routines: pass elif isinstance(ref, LocationRef): if ref not in self._meaningful: @@ -293,34 +293,38 @@ class Analyzer(object): context.assert_meaningful(src) elif opcode == 'copy': - # check that their types are compatible + # 1. check that their types are compatible if isinstance(dest, IndirectRef): if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): pass else: raise TypeMismatchError((src, dest)) - elif src.type == dest.type: - pass - elif isinstance(src.type, ExecutableType) and \ - isinstance(dest.type, VectorType): - pass - elif isinstance(src.type, BufferType) and \ - isinstance(dest.type, PointerType): - pass + elif isinstance(src, AddressRef): + if isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType): + pass + else: + raise TypeMismatchError((src, dest)) + + elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef): + if src.type == dest.type: + pass + elif isinstance(src.type, ExecutableType) and \ + isinstance(dest.type, VectorType): + # if dealing with routines and vectors, + # check that they're not incompatible + if not (src.type.inputs <= dest.type.inputs): + raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs) + if not (src.type.outputs <= dest.type.outputs): + raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs) + if not (src.type.trashes <= dest.type.trashes): + raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes) + else: + raise TypeMismatchError((src, dest)) else: raise TypeMismatchError((src, dest)) - # if dealing with routines and vectors, - # check that they're not incompatible - if isinstance(src.type, ExecutableType) and \ - isinstance(dest.type, VectorType): - if not (src.type.inputs <= dest.type.inputs): - raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs) - if not (src.type.outputs <= dest.type.outputs): - raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs) - if not (src.type.trashes <= dest.type.trashes): - raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes) + # 2. check that the context is meaningful if isinstance(dest, IndirectRef): context.assert_meaningful(src, REG_Y) diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index eb5aee5..c4f8495 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -2,7 +2,7 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( - ConstantRef, IndirectRef, + ConstantRef, LocationRef, IndirectRef, AddressRef, TYPE_BIT, TYPE_BYTE, TYPE_WORD, BufferType, PointerType, RoutineType, VectorType, REG_A, REG_X, REG_Y, FLAG_C ) @@ -284,6 +284,16 @@ class Compiler(object): raise NotImplementedError((src, dest)) else: raise NotImplementedError((src, dest)) + elif isinstance(src, AddressRef) and isinstance(dest, LocationRef) and \ + isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType): + src_label = self.labels[src.ref.name] + dest_label = self.labels[dest.name] + self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) + self.emitter.emit(STA(ZeroPage(dest_label))) + self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) + self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) + elif not isinstance(src, (ConstantRef, LocationRef)) or not isinstance(dest, LocationRef): + raise NotImplementedError((src, dest)) elif src.type == TYPE_BYTE and dest.type == TYPE_BYTE: if isinstance(src, ConstantRef): raise NotImplementedError @@ -308,14 +318,6 @@ class Compiler(object): self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) - elif isinstance(src.type, BufferType) and isinstance(dest.type, PointerType): - # TODO: this maybe should not be the 'copy' opcode at all that means this - src_label = self.labels[src.name] - dest_label = self.labels[dest.name] - self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) - self.emitter.emit(STA(ZeroPage(dest_label))) - self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) - self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) elif isinstance(src.type, VectorType) and isinstance(dest.type, VectorType): src_label = self.labels[src.name] dest_label = self.labels[dest.name] diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index b8b636a..449285b 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -109,7 +109,7 @@ class IndirectRef(Ref): return self.ref == other.ref def __hash__(self): - return hash(hash('[]') ^ hash(self.ref)) + return hash(self.__class__.name) ^ hash(self.ref) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.ref) @@ -122,6 +122,27 @@ class IndirectRef(Ref): return False +class AddressRef(Ref): + def __init__(self, ref): + self.ref = ref + + def __eq__(self, other): + return self.ref == other.ref + + def __hash__(self): + return hash(self.__class__.name) ^ hash(self.ref) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, self.ref) + + @property + def name(self): + return '^{}'.format(self.ref.name) + + def is_constant(self): + return True + + class PartRef(Ref): """For 'low byte of' location and 'high byte of' location modifiers. diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 341c8d6..872070d 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -4,7 +4,7 @@ from sixtypical.ast import Program, Defn, Routine, Block, Instr from sixtypical.model import ( TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE, RoutineType, VectorType, ExecutableType, BufferType, PointerType, - LocationRef, ConstantRef, IndirectRef, + LocationRef, ConstantRef, IndirectRef, AddressRef, ) from sixtypical.scanner import Scanner @@ -171,6 +171,9 @@ class Parser(object): self.scanner.expect('+') self.scanner.expect('y') return IndirectRef(loc) + elif self.scanner.consume('^'): + loc = self.locexpr() + return AddressRef(loc) else: return self.locexpr() diff --git a/src/sixtypical/scanner.py b/src/sixtypical/scanner.py index 3663e9c..fd48b3b 100644 --- a/src/sixtypical/scanner.py +++ b/src/sixtypical/scanner.py @@ -29,7 +29,7 @@ class Scanner(object): self.token = None self.type = 'EOF' return - if self.scan_pattern(r'\,|\@|\+|\:|\<|\>|\{|\}|\[|\]', 'operator'): + if self.scan_pattern(r'\,|\@|\+|\:|\<|\>|\{|\}|\[|\]|\^', 'operator'): return if self.scan_pattern(r'\d+', 'integer literal'): return diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 4b48df9..31b8186 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1176,19 +1176,18 @@ Can't `copy` from a `word` to a `byte`. Buffers and pointers. -Note that `copy buf, ptr` should probably be `copy ^buf, ptr` and `^buf` should not -be considered "reading" buf and should not require it in `inputs` for that reason. +Note that `^buf` is not considered "reading" buf, so does not require it in `inputs`. +TODO: It *should* require it in `outputs`. | buffer[2048] buf | pointer ptr | | routine main - | inputs buf - | outputs buf, y + | outputs y | trashes a, z, n, ptr | { | ld y, 0 - | copy buf, ptr + | copy ^buf, ptr | copy 123, [ptr] + y | } = ok @@ -1199,11 +1198,9 @@ It does use `y`. | pointer ptr | | routine main - | inputs buf - | outputs buf | trashes a, z, n, ptr | { - | copy buf, ptr + | copy ^buf, ptr | copy 123, [ptr] + y | } ? UnmeaningfulReadError diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 5e2df28..95d6785 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -353,7 +353,7 @@ Buffers and pointers. | trashes a, z, n, ptr | { | ld y, 0 - | copy buf, ptr + | copy ^buf, ptr | } = 00c0a000a90b85fea9c085ff60 @@ -363,12 +363,11 @@ Writing through a pointer. | pointer ptr @ 254 | | routine main - | inputs buf | outputs buf, y | trashes a, z, n, ptr | { | ld y, 0 - | copy buf, ptr + | copy ^buf, ptr | copy 123, [ptr] + y | } = 00c0a000a90f85fea9c085ffa97b91fe60 diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index 66c761f..b6f4fc0 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -328,7 +328,7 @@ Buffers and pointers. | pointer ptr | | routine main { - | copy buf, ptr + | copy ^buf, ptr | copy 123, [ptr] + y | } = ok From 9874b11639a57eb10a15eca3a8584cc4dd990da3 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 1 Dec 2017 13:09:25 +0000 Subject: [PATCH 19/24] Update documentation. --- HISTORY.markdown | 5 ++++ README.markdown | 25 ------------------- doc/SixtyPical.md | 62 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/HISTORY.markdown b/HISTORY.markdown index a1a376a..564decd 100644 --- a/HISTORY.markdown +++ b/HISTORY.markdown @@ -6,6 +6,11 @@ History of SixtyPical * Explicit word literals prefixed with `word` token. * Can `copy` literals into user-defined destinations. +* `buffer` and `pointer` types. +* `copy ^` syntax to load the addr of a buffer into a pointer. +* `copy []+y` syntax to write a value into memory through a pointer. +* TODO: read through pointer. +* TODO: insist the buffer being read or written to through pointer, appears in approporiate set. 0.7 --- diff --git a/README.markdown b/README.markdown index b38da83..982d137 100644 --- a/README.markdown +++ b/README.markdown @@ -41,31 +41,6 @@ Documentation TODO ---- -### `byte buffer` and `pointer` types - -Basically, a `buffer` is a table that can -be longer than 256 bytes, and a `pointer` is an address within a buffer. -A `pointer` is implemented as a zero-page memory location, and accessing the -buffer pointed to is implemented with "indirect indexed" addressing, as in - - LDA ($02), Y - STA ($02), Y - -We will likely have a new mode of `copy` for this, like - - copy ^buf, ptr // this is the only way to initialize a pointer - add ptr, 4 // ok, but only if it does not exceed buffer's size - ld y, 0 // you must set this to something yourself - copy [ptr] + y, byt // read memory through pointer, into byte - copy 100, [ptr] + y // write memory through pointer (still trashes a) - -where `ptr` is a user-defined storage location of `pointer` type, and the -`+ y` part is mandatory. - -This instruction will likely be unchecked, at least to start. Basically, -this is to allow us to write to the `byte buffer[2048]` known as "the screen", -(and doing that is valuable enough that we can sacrifice checking, for now.) - ### `word table` and `vector table` types ### `low` and `high` address operators diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index a6ac3e5..b4315ca 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -14,19 +14,19 @@ the language. Types ----- -There are five *primitive types* in SixtyPical: +There are six *primitive types* in SixtyPical: * bit (2 possible values) * byte (256 possible values) * word (65536 possible values) * routine (code stored somewhere in memory, read-only) * vector (address of a routine) +* pointer (address of a byte in a buffer) -There is also one *type constructor*: +There are also two *type constructors*: -* X table (256 entries, each holding a value of type X) - -This constructor can only be applied to one type, `byte`. +* X table (256 entries, each holding a value of type X, where X is `byte`) +* buffer[N] (N entries; each entry is a byte; N is a power of 2, ≤ 64K) Memory locations ---------------- @@ -127,6 +127,29 @@ This is actually useful, at least at this point, as you can rely on the fact that literal integers in the code are always immediate values. (But this may change at some point.) +### Buffers and Pointers ### + +Roughly speaking, a `buffer` is a table that can be longer than 256 bytes, +and a `pointer` is an address within a buffer. + +A `pointer` is implemented as a zero-page memory location, and accessing the +buffer pointed to is implemented with "indirect indexed" addressing, as in + + LDA ($02), Y + STA ($02), Y + +There are extended modes of `copy` for using these types of memory location. +See `copy` below, but here is some illustrative example code: + + copy ^buf, ptr // this is the only way to initialize a pointer + add ptr, 4 // ok, but only if it does not exceed buffer's size + ld y, 0 // you must set this to something yourself + copy [ptr] + y, byt // read memory through pointer, into byte + copy 100, [ptr] + y // write memory through pointer (still trashes a) + +where `ptr` is a user-defined storage location of `pointer` type, and the +`+ y` part is mandatory. + Routines -------- @@ -237,6 +260,33 @@ copy more general types of data (for example, vectors,) and it trashes the After execution, dest is considered initialized, and `z` and `n`, and `a` are considered uninitialized. +There are two extra modes that this instruction can be used in. The first is +to load an address into a pointer: + + copy ^, + +This copies the address of src into dest. In this case, src must be +of type buffer, and dest must be of type pointer. src will not be +considered a memory location that is read, since it is only its address +that is being retrieved. + +The second is to read or write indirectly through a pointer. + + copy [] + y, + copy , [] + y + +In both of these, the memory location in the `[]+y` syntax must be +a pointer. + +The first copies the contents of memory at the pointer (offset by the `y` +register) into a byte memory location. + +The second copies a literal byte, or a byte memory location, into +the contents of memory at the pointer (offset by the `y` register). + +In addition to the constraints above, `y` must be initialized before +this mode is used. + ### add dest, src ### add , @@ -359,7 +409,7 @@ Just after the call, * All memory locations listed in the called routine's `trashes` are considered to now be uninitialized. * All memory locations listed in the called routine's `outputs` are considered - to not be initialized. + to now be initialized. ### goto ### From d84566a880ad3384687ae844259b4a7e651799da Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 1 Dec 2017 13:52:56 +0000 Subject: [PATCH 20/24] Write stored values, and read values, through pointers. --- HISTORY.markdown | 4 +--- README.markdown | 4 ++++ eg/buffer.60p | 5 +++- src/sixtypical/analyzer.py | 25 +++++++++++++------- src/sixtypical/compiler.py | 15 +++++++++++- tests/SixtyPical Analysis.md | 42 +++++++++++++++++++++++++++++++-- tests/SixtyPical Compilation.md | 36 +++++++++++++++++++++++++++- tests/SixtyPical Syntax.md | 2 ++ 8 files changed, 117 insertions(+), 16 deletions(-) diff --git a/HISTORY.markdown b/HISTORY.markdown index 564decd..b0477b2 100644 --- a/HISTORY.markdown +++ b/HISTORY.markdown @@ -8,9 +8,7 @@ History of SixtyPical * Can `copy` literals into user-defined destinations. * `buffer` and `pointer` types. * `copy ^` syntax to load the addr of a buffer into a pointer. -* `copy []+y` syntax to write a value into memory through a pointer. -* TODO: read through pointer. -* TODO: insist the buffer being read or written to through pointer, appears in approporiate set. +* `copy []+y` syntax to read and write values to and from memory through a pointer. 0.7 --- diff --git a/README.markdown b/README.markdown index 982d137..c07b0e2 100644 --- a/README.markdown +++ b/README.markdown @@ -41,6 +41,10 @@ Documentation TODO ---- +Insist the buffer being read or written to through pointer, appears in approporiate set. + +Add to pointer. + ### `word table` and `vector table` types ### `low` and `high` address operators diff --git a/eg/buffer.60p b/eg/buffer.60p index 8783a11..ce4dfbe 100644 --- a/eg/buffer.60p +++ b/eg/buffer.60p @@ -1,12 +1,15 @@ buffer[2048] buf pointer ptr @ 254 +byte foo routine main inputs buf - outputs buf, y + outputs buf, y, foo trashes a, z, n, ptr { ld y, 0 copy ^buf, ptr copy 123, [ptr] + y + copy [ptr] + y, foo + copy foo, [ptr] + y } diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 7f61bd5..4a2b522 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -295,17 +295,21 @@ class Analyzer(object): elif opcode == 'copy': # 1. check that their types are compatible - if isinstance(dest, IndirectRef): - if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): - pass - else: - raise TypeMismatchError((src, dest)) - elif isinstance(src, AddressRef): + if isinstance(src, AddressRef) and isinstance(dest, LocationRef): if isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType): pass else: raise TypeMismatchError((src, dest)) - + elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef): + if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): + pass + else: + raise TypeMismatchError((src, dest)) + elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef): + if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE: + pass + else: + raise TypeMismatchError((src, dest)) elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef): if src.type == dest.type: pass @@ -326,10 +330,15 @@ class Analyzer(object): # 2. check that the context is meaningful - if isinstance(dest, IndirectRef): + if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef): context.assert_meaningful(src, REG_Y) # TODO this will need to be more sophisticated. it's the thing ref points to that is written, not ref itself. context.set_written(dest.ref) + elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef): + context.assert_meaningful(src.ref, REG_Y) + # TODO this will need to be more sophisticated. the thing ref points to is touched, as well. + context.set_touched(src.ref) + context.set_written(dest) else: context.assert_meaningful(src) context.set_written(dest) diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index c4f8495..7f94c2b 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -274,16 +274,29 @@ class Compiler(object): self.compile_block(instr.block) self.emitter.emit(CLI()) elif opcode == 'copy': - if isinstance(dest, IndirectRef): + if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef): if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): if isinstance(src, ConstantRef): dest_label = self.labels[dest.ref.name] self.emitter.emit(LDA(Immediate(Byte(src.value)))) self.emitter.emit(STA(IndirectY(dest_label))) + elif isinstance(src, LocationRef): + src_label = self.labels[src.name] + dest_label = self.labels[dest.ref.name] + self.emitter.emit(LDA(Absolute(src_label))) + self.emitter.emit(STA(IndirectY(dest_label))) else: raise NotImplementedError((src, dest)) else: raise NotImplementedError((src, dest)) + elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef): + if dest.type == TYPE_BYTE and isinstance(src.ref.type, PointerType): + src_label = self.labels[src.ref.name] + dest_label = self.labels[dest.name] + self.emitter.emit(LDA(IndirectY(src_label))) + self.emitter.emit(STA(Absolute(dest_label))) + else: + raise NotImplementedError((src, dest)) elif isinstance(src, AddressRef) and isinstance(dest, LocationRef) and \ isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType): src_label = self.labels[src.ref.name] diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 31b8186..b27ee2e 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1177,13 +1177,16 @@ Can't `copy` from a `word` to a `byte`. Buffers and pointers. Note that `^buf` is not considered "reading" buf, so does not require it in `inputs`. -TODO: It *should* require it in `outputs`. +TODO: If reading from it through a pointer, it *should* require it in `inputs`. +TODO: If writing to it through a pointer, it *should* require it in `outputs`. + +Write literal through a pointer. | buffer[2048] buf | pointer ptr | | routine main - | outputs y + | outputs y //, buf | trashes a, z, n, ptr | { | ld y, 0 @@ -1198,6 +1201,7 @@ It does use `y`. | pointer ptr | | routine main + | // outputs buf | trashes a, z, n, ptr | { | copy ^buf, ptr @@ -1205,6 +1209,40 @@ It does use `y`. | } ? UnmeaningfulReadError +Write stored value through a pointer. + + | buffer[2048] buf + | pointer ptr + | byte foo + | + | routine main + | inputs foo + | outputs y //, buf + | trashes a, z, n, ptr + | { + | ld y, 0 + | copy ^buf, ptr + | copy foo, [ptr] + y + | } + = ok + +Read through a pointer. + + | buffer[2048] buf + | pointer ptr + | byte foo + | + | routine main + | // inputs buf + | outputs foo + | trashes a, y, z, n, ptr + | { + | ld y, 0 + | copy ^buf, ptr + | copy [ptr] + y, foo + | } + = ok + ### routines ### Routines are constants. You need not, and in fact cannot, specify a constant diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 95d6785..a48b833 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -357,7 +357,7 @@ Buffers and pointers. | } = 00c0a000a90b85fea9c085ff60 -Writing through a pointer. +Writing literal through a pointer. | buffer[2048] buf | pointer ptr @ 254 @@ -371,3 +371,37 @@ Writing through a pointer. | copy 123, [ptr] + y | } = 00c0a000a90f85fea9c085ffa97b91fe60 + +Write stored value through a pointer. + + | buffer[2048] buf + | pointer ptr @ 254 + | byte foo + | + | routine main + | inputs foo + | outputs y //, buf + | trashes a, z, n, ptr + | { + | ld y, 0 + | copy ^buf, ptr + | copy foo, [ptr] + y + | } + = 00c0a000a91085fea9c085ffad12c091fe60 + +Reading through a pointer. + + | buffer[2048] buf + | pointer ptr @ 254 + | byte foo + | + | routine main + | // inputs buf + | outputs y, foo + | trashes a, z, n, ptr + | { + | ld y, 0 + | copy ^buf, ptr + | copy [ptr] + y, foo + | } + = 00c0a000a91085fea9c085ffb1fe8d12c060 diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index b6f4fc0..98356af 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -326,9 +326,11 @@ Buffers and pointers. | buffer[2048] buf | pointer ptr + | byte foo | | routine main { | copy ^buf, ptr | copy 123, [ptr] + y + | copy [ptr] + y, foo | } = ok From 6afbf581f7d28594f7c9936896e90206c176a370 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 1 Dec 2017 15:10:16 +0000 Subject: [PATCH 21/24] Deal with the inputs/outputs of buffers, in a weak way. --- HISTORY.markdown | 1 + README.markdown | 8 ++++++-- tests/SixtyPical Analysis.md | 29 +++++++++++++++++++++-------- tests/SixtyPical Compilation.md | 15 +++++++++------ 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/HISTORY.markdown b/HISTORY.markdown index b0477b2..fefecd8 100644 --- a/HISTORY.markdown +++ b/HISTORY.markdown @@ -6,6 +6,7 @@ History of SixtyPical * Explicit word literals prefixed with `word` token. * Can `copy` literals into user-defined destinations. +* Fixed bug where loop variable wasn't being checked at end of `repeat` loop. * `buffer` and `pointer` types. * `copy ^` syntax to load the addr of a buffer into a pointer. * `copy []+y` syntax to read and write values to and from memory through a pointer. diff --git a/README.markdown b/README.markdown index c07b0e2..f97d617 100644 --- a/README.markdown +++ b/README.markdown @@ -41,9 +41,10 @@ Documentation TODO ---- -Insist the buffer being read or written to through pointer, appears in approporiate set. +### Add to pointer. -Add to pointer. +And then write a little demo "game" where you can move a block around the screen with +the joystick. ### `word table` and `vector table` types @@ -58,6 +59,9 @@ are trashed inside the block. ### And at some point... +* `copy x, [ptr] + y` +* Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA! +* Check that the buffer being read or written to through pointer, appears in approporiate inputs or outputs set. * initialized `byte table` memory locations * always analyze before executing or compiling, unless told not to * `trash` instruction. diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index b27ee2e..c07db8a 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1176,9 +1176,20 @@ Can't `copy` from a `word` to a `byte`. Buffers and pointers. -Note that `^buf` is not considered "reading" buf, so does not require it in `inputs`. -TODO: If reading from it through a pointer, it *should* require it in `inputs`. -TODO: If writing to it through a pointer, it *should* require it in `outputs`. +Note that `^buf` is a constant value, so it by itself does not require `buf` to be +listed in any input/output sets. + +However, if the code reads from it through a pointer, it *should* be in `inputs`. + +Likewise, if the code writes to it through a pointer, it *should* be in `outputs`. + +Of course, unless you write to *all* the bytes in a buffer, some of those bytes +might not be meaningful. So how meaningful is this check? + +This is an open problem. + +For now, convention says: if it is being read, list it in `inputs`, and if it is +being modified, list it in both `inputs` and `outputs`. Write literal through a pointer. @@ -1186,7 +1197,8 @@ Write literal through a pointer. | pointer ptr | | routine main - | outputs y //, buf + | inputs buf + | outputs y, buf | trashes a, z, n, ptr | { | ld y, 0 @@ -1201,7 +1213,8 @@ It does use `y`. | pointer ptr | | routine main - | // outputs buf + | inputs buf + | outputs buf | trashes a, z, n, ptr | { | copy ^buf, ptr @@ -1216,8 +1229,8 @@ Write stored value through a pointer. | byte foo | | routine main - | inputs foo - | outputs y //, buf + | inputs foo, buf + | outputs y, buf | trashes a, z, n, ptr | { | ld y, 0 @@ -1233,7 +1246,7 @@ Read through a pointer. | byte foo | | routine main - | // inputs buf + | inputs buf | outputs foo | trashes a, y, z, n, ptr | { diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index a48b833..64e2d77 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -342,7 +342,9 @@ goto. | } = 00c0a0c84c06c060a2c860 -Buffers and pointers. +### Buffers and Pointers + +Load address into pointer. | buffer[2048] buf | pointer ptr @ 254 @@ -357,12 +359,13 @@ Buffers and pointers. | } = 00c0a000a90b85fea9c085ff60 -Writing literal through a pointer. +Write literal through a pointer. | buffer[2048] buf | pointer ptr @ 254 | | routine main + | inputs buf | outputs buf, y | trashes a, z, n, ptr | { @@ -379,8 +382,8 @@ Write stored value through a pointer. | byte foo | | routine main - | inputs foo - | outputs y //, buf + | inputs foo, buf + | outputs y, buf | trashes a, z, n, ptr | { | ld y, 0 @@ -389,14 +392,14 @@ Write stored value through a pointer. | } = 00c0a000a91085fea9c085ffad12c091fe60 -Reading through a pointer. +Read through a pointer. | buffer[2048] buf | pointer ptr @ 254 | byte foo | | routine main - | // inputs buf + | inputs buf | outputs y, foo | trashes a, z, n, ptr | { From e9322c8f93fab358c9e9aacadea7431675e4a5b9 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 1 Dec 2017 16:46:14 +0000 Subject: [PATCH 22/24] A tiny edit to the TODOs. --- README.markdown | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index f97d617..4aaeaf5 100644 --- a/README.markdown +++ b/README.markdown @@ -41,7 +41,11 @@ Documentation TODO ---- -### Add to pointer. +### Add 16 bit values. + +I guess this means making `add` a bit more like `copy`. + +And then: add to pointer. (Not necessarily range-checked yet though.) And then write a little demo "game" where you can move a block around the screen with the joystick. From 8766601786f37dbfd076970a77c6e9386db29eaa Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Fri, 1 Dec 2017 17:23:09 +0000 Subject: [PATCH 23/24] A little note on the history. --- doc/Design Goals.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/Design Goals.md b/doc/Design Goals.md index 2da807b..874a44c 100644 --- a/doc/Design Goals.md +++ b/doc/Design Goals.md @@ -25,3 +25,28 @@ to spend a lot of time debugging. The intent is not to make it absolutely impossible to make such errors, just harder. + +### Some Background ### + +The ideas in SixtyPical came from a couple of places. + +One major impetus was when I was working on [Shelta][], trying to cram +all that code for that compiler into 512 bytes. This involved looking +at the x86 registers and thinking hard about which ones were preserved +when (and which ones weren't) and making the best use of that. And +while doing that, one thing that came to mind was: I Bet The Assembler +Could Track This. + +Another influence was around 2007 when "Typed Assembly Language" (and +"Proof Carrying Code") were all the rage. I haven't heard about them +in a while, so I guess they turned out to be research fads? But for a +while there, it was all Necula, Necula, Necula. Anyway, I remember at +the time looking into TAL and expecting to find something that matched +the impression I had pre-formulated about what a "Typed Assembly" +might be like. And finding that it didn't match my vision very well. + +I don't actually remember what TAL seemed like to me at the time, but +what I had in mind was more like SixtyPical. + +(I'll also write something about abstract interpretation here at some +point, hopefully.) From 92f212ddfcd0e4b2aa6ef65b6d61bcdd8f8c1f32 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 4 Dec 2017 13:02:26 +0000 Subject: [PATCH 24/24] Prep for release of 0.8. --- README.markdown | 2 +- doc/SixtyPical.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 4aaeaf5..c356b91 100644 --- a/README.markdown +++ b/README.markdown @@ -24,7 +24,7 @@ programs to 6502 machine code. It is a **work in progress**, currently at the **proof-of-concept** stage. -The current development version of SixtyPical is 0.8-PRE. +The current development version of SixtyPical is 0.8. Documentation ------------- diff --git a/doc/SixtyPical.md b/doc/SixtyPical.md index b4315ca..e85c778 100644 --- a/doc/SixtyPical.md +++ b/doc/SixtyPical.md @@ -1,7 +1,7 @@ SixtyPical ========== -This document describes the SixtyPical programming language version 0.8-PRE, +This document describes the SixtyPical programming language version 0.8, both its execution aspect and its static analysis aspect (even though these are, technically speaking, separate concepts.)