mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-02-10 08:30:38 +00:00
commit
f87c07e52a
16
HISTORY.md
16
HISTORY.md
@ -1,6 +1,21 @@
|
||||
History of SixtyPical
|
||||
=====================
|
||||
|
||||
0.11
|
||||
----
|
||||
|
||||
* Each table has a specified size now (although, bounds checking is not performed.)
|
||||
* Initialized `byte table` values need not have all 256 bytes initialized.
|
||||
* Syntax for types has changed. `routine` (with constraints) is a type, while
|
||||
`vector` is now a type constructor (taking `routine`s only) and `table` is
|
||||
also a type constructor. This permits a new `vector table` type.
|
||||
* Added `typedef`, allowing the user to define type aliases for readability.
|
||||
* Added `define name routine {...}` syntax; `routine name {...}` is now legacy.
|
||||
* Ability to copy vectors and routines into vector tables, and vectors out of same.
|
||||
* Removed the evaluator. The reference implementation only analyzes and compiles.
|
||||
* Fixed bug where index register wasn't required to be initialized before table access.
|
||||
* Fixed bug where trampolines for indirect calls weren't including a final `RTS`.
|
||||
|
||||
0.10
|
||||
----
|
||||
|
||||
@ -12,6 +27,7 @@ History of SixtyPical
|
||||
* Subtract word (constant or memory location) from word memory location.
|
||||
* `trash` instruction explicitly indicates a value is no longer considered meaningful.
|
||||
* `copy []+y, a` can indirectly read a byte value into the `a` register.
|
||||
* Initialized `byte table` memory locations.
|
||||
* Fixed bug which was preventing `if` branches to diverge in what they initialized,
|
||||
if it was already initialized when going into the `if`.
|
||||
* Fixed a bug which was making it crash when trying to analyze `repeat forever` loops.
|
||||
|
24
README.md
24
README.md
@ -1,6 +1,8 @@
|
||||
SixtyPical
|
||||
==========
|
||||
|
||||
_Version 0.11. Work-in-progress, everything is subject to change._
|
||||
|
||||
SixtyPical is a very low-level programming language, similar to 6502 assembly,
|
||||
with static analysis through abstract interpretation.
|
||||
|
||||
@ -19,11 +21,8 @@ based on common machine-language programming idioms, such as
|
||||
* explicit tail calls
|
||||
* indirect subroutine calls
|
||||
|
||||
The reference implementation can execute, analyze, and compile SixtyPical
|
||||
programs to 6502 machine code.
|
||||
|
||||
SixtyPical is a work in progress. The current released version of SixtyPical
|
||||
is 0.10.
|
||||
The reference implementation can analyze and compile SixtyPical programs to
|
||||
6502 machine code.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@ -40,14 +39,6 @@ Documentation
|
||||
TODO
|
||||
----
|
||||
|
||||
### Demo game
|
||||
|
||||
Finish the little demo "game" where you can move a block around the screen with
|
||||
the joystick (i.e. bring it up to par with the original demo game that was written
|
||||
for SixtyPical)
|
||||
|
||||
### `vector table` type
|
||||
|
||||
### `low` and `high` address operators
|
||||
|
||||
To turn `word` type into `byte`.
|
||||
@ -80,11 +71,14 @@ assumed to be meaningful.
|
||||
* `copy x, [ptr] + y`
|
||||
* Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA!
|
||||
|
||||
### Union rule for trashes in `if`
|
||||
|
||||
If one branch trashes {`a`} and the other branch trashes {`b`} then the whole
|
||||
`if` statement trashes {`a`, `b`}.
|
||||
|
||||
### And at some point...
|
||||
|
||||
* Check that the buffer being read or written to through pointer, appears in approporiate inputs or outputs set.
|
||||
* `byte table` and `word table` of sizes other than 256
|
||||
* always analyze before executing or compiling, unless told not to
|
||||
* `interrupt` routines -- to indicate that "the supervisor" has stored values on the stack, so we can trash them.
|
||||
* error messages that include the line number of the source code
|
||||
* add absolute addressing in shl/shr, absolute-indexed for add, sub, etc.
|
||||
|
107
bin/sixtypical
107
bin/sixtypical
@ -2,7 +2,7 @@
|
||||
|
||||
"""Usage: sixtypical [OPTIONS] FILES
|
||||
|
||||
Analyzes and/or executes and/or compiles a Sixtypical program.
|
||||
Analyzes and compiles a Sixtypical program.
|
||||
"""
|
||||
|
||||
from os.path import realpath, dirname, join
|
||||
@ -19,7 +19,6 @@ import sys
|
||||
import traceback
|
||||
|
||||
from sixtypical.parser import Parser
|
||||
from sixtypical.evaluator import Evaluator
|
||||
from sixtypical.analyzer import Analyzer
|
||||
from sixtypical.emitter import Emitter, Byte, Word
|
||||
from sixtypical.compiler import Compiler
|
||||
@ -28,67 +27,73 @@ from sixtypical.compiler import Compiler
|
||||
if __name__ == '__main__':
|
||||
optparser = OptionParser(__doc__.strip())
|
||||
|
||||
optparser.add_option("--analyze",
|
||||
optparser.add_option("--analyze-only",
|
||||
action="store_true",
|
||||
help="")
|
||||
help="Only parse and analyze the program; do not compile it.")
|
||||
optparser.add_option("--basic-prelude",
|
||||
action="store_true",
|
||||
help="")
|
||||
optparser.add_option("--compile",
|
||||
action="store_true",
|
||||
help="")
|
||||
help="Insert a Commodore BASIC 2.0 snippet before the program "
|
||||
"so that it can be LOADed and RUN on Commodore platforms.")
|
||||
optparser.add_option("--debug",
|
||||
action="store_true",
|
||||
help="")
|
||||
help="Display debugging information when analyzing and compiling.")
|
||||
optparser.add_option("--parse-only",
|
||||
action="store_true",
|
||||
help="Only parse the program; do not analyze or compile it.")
|
||||
optparser.add_option("--traceback",
|
||||
action="store_true",
|
||||
help="")
|
||||
optparser.add_option("--execute",
|
||||
action="store_true",
|
||||
help="")
|
||||
help="When an error occurs, display a full Python traceback.")
|
||||
|
||||
(options, args) = optparser.parse_args(sys.argv[1:])
|
||||
|
||||
for filename in args:
|
||||
text = open(filename).read()
|
||||
parser = Parser(text)
|
||||
program = parser.program()
|
||||
|
||||
if options.analyze:
|
||||
try:
|
||||
analyzer = Analyzer(debug=options.debug)
|
||||
analyzer.analyze_program(program)
|
||||
except Exception as e:
|
||||
if options.traceback:
|
||||
raise
|
||||
else:
|
||||
traceback.print_exception(e.__class__, e, None)
|
||||
sys.exit(1)
|
||||
|
||||
if options.compile:
|
||||
fh = sys.stdout
|
||||
start_addr = 0xc000
|
||||
prelude = []
|
||||
if options.basic_prelude:
|
||||
start_addr = 0x0801
|
||||
prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
|
||||
0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
|
||||
|
||||
# we are outputting a .PRG, so we output the load address first
|
||||
# we don't use the Emitter for this b/c not part of addr space
|
||||
if not options.debug:
|
||||
fh.write(Word(start_addr).serialize(0))
|
||||
|
||||
emitter = Emitter(start_addr)
|
||||
for byte in prelude:
|
||||
emitter.emit(Byte(byte))
|
||||
compiler = Compiler(emitter)
|
||||
compiler.compile_program(program)
|
||||
if options.debug:
|
||||
pprint(emitter.accum)
|
||||
try:
|
||||
parser = Parser(text)
|
||||
program = parser.program()
|
||||
except Exception as e:
|
||||
if options.traceback:
|
||||
raise
|
||||
else:
|
||||
emitter.serialize(fh)
|
||||
traceback.print_exception(e.__class__, e, None)
|
||||
sys.exit(1)
|
||||
|
||||
if options.execute:
|
||||
context = Evaluator().eval_program(program)
|
||||
print str(context)
|
||||
if options.parse_only:
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
analyzer = Analyzer(debug=options.debug)
|
||||
analyzer.analyze_program(program)
|
||||
except Exception as e:
|
||||
if options.traceback:
|
||||
raise
|
||||
else:
|
||||
traceback.print_exception(e.__class__, e, None)
|
||||
sys.exit(1)
|
||||
|
||||
if options.analyze_only:
|
||||
sys.exit(0)
|
||||
|
||||
fh = sys.stdout
|
||||
start_addr = 0xc000
|
||||
prelude = []
|
||||
if options.basic_prelude:
|
||||
start_addr = 0x0801
|
||||
prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
|
||||
0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
|
||||
|
||||
# we are outputting a .PRG, so we output the load address first
|
||||
# we don't use the Emitter for this b/c not part of addr space
|
||||
if not options.debug:
|
||||
fh.write(Word(start_addr).serialize(0))
|
||||
|
||||
emitter = Emitter(start_addr)
|
||||
for byte in prelude:
|
||||
emitter.emit(Byte(byte))
|
||||
compiler = Compiler(emitter)
|
||||
compiler.compile_program(program)
|
||||
if options.debug:
|
||||
pprint(emitter.accum)
|
||||
else:
|
||||
emitter.serialize(fh)
|
||||
|
@ -1,9 +1,10 @@
|
||||
SixtyPical
|
||||
==========
|
||||
|
||||
This document describes the SixtyPical programming language version 0.10,
|
||||
both its execution aspect and its static analysis aspect (even though
|
||||
these are, technically speaking, separate concepts.)
|
||||
This document describes the SixtyPical programming language version 0.11,
|
||||
both its static semantics (the capabilities and limits of the static
|
||||
analyses it defines) and its runtime semantics (with reference to the
|
||||
semantics of 6502 machine code.)
|
||||
|
||||
This document is nominally normative, but the tests in the `tests` directory
|
||||
are even more normative.
|
||||
@ -14,20 +15,28 @@ the language.
|
||||
Types
|
||||
-----
|
||||
|
||||
There are six *primitive types* in SixtyPical:
|
||||
There are five *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 are also two *type constructors*:
|
||||
There are also three *type constructors*:
|
||||
|
||||
* T table (256 entries, each holding a value of type T, where T is either
|
||||
`byte` or `word`)
|
||||
* T table[N] (N is a power of 2, 1 ≤ N ≤ 256; each entry holds a value
|
||||
of type T, where T is `byte`, `word`, or `vector`)
|
||||
* buffer[N] (N entries; each entry is a byte; N is a power of 2, ≤ 64K)
|
||||
* vector T (address of a value of type T; T must be a routine type)
|
||||
|
||||
### User-defined ###
|
||||
|
||||
A program may define its own types using the `typedef` feature. Typedefs
|
||||
must occur before everything else in the program. A typedef takes a
|
||||
type expression and an identifier which has not previously been used in
|
||||
the program. It associates that identifer with that type. This is merely
|
||||
a type alias; two types with different names will compare as equal.
|
||||
|
||||
Memory locations
|
||||
----------------
|
||||
@ -110,11 +119,11 @@ 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
|
||||
outputs x
|
||||
trashes y
|
||||
@ $c000
|
||||
vector routine
|
||||
inputs a, score
|
||||
outputs x
|
||||
trashes y
|
||||
actor_logic @ $c000
|
||||
|
||||
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.
|
||||
@ -500,11 +509,22 @@ The sense of the test can be inverted with `not`.
|
||||
Grammar
|
||||
-------
|
||||
|
||||
Program ::= {Defn} {Routine}.
|
||||
Program ::= {TypeDefn} {Defn} {Routine}.
|
||||
TypeDefn::= "typedef" Type Ident<new>.
|
||||
Defn ::= Type Ident<new> [Constraints] (":" Literal | "@" LitWord).
|
||||
Type ::= "byte" ["table"] | "vector"
|
||||
Type ::= "(" Type ")" | TypeExpr ["table" TypeSize].
|
||||
TypeExpr::= "byte"
|
||||
| "word"
|
||||
| "buffer" TypeSize
|
||||
| "pointer"
|
||||
| "vector" Type
|
||||
| "routine" Constraints
|
||||
.
|
||||
TypeSize::= "[" LitWord "]".
|
||||
Constrnt::= ["inputs" LocExprs] ["outputs" LocExprs] ["trashes" LocExprs].
|
||||
Routine ::= "routine" Ident<new> Constraints (Block | "@" LitWord).
|
||||
Routine ::= "define" Ident<new> Type (Block | "@" LitWord).
|
||||
| "routine" Ident<new> Constraints (Block | "@" LitWord)
|
||||
.
|
||||
LocExprs::= LocExpr {"," LocExpr}.
|
||||
LocExpr ::= Register | Flag | Literal | Ident.
|
||||
Register::= "a" | "x" | "y".
|
||||
|
@ -2,6 +2,58 @@
|
||||
// * Demo Game for SixtyPical *
|
||||
// ****************************
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Type Definitions
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Type of routines (and vectors to those routines) which are called on each frame
|
||||
// to implement a certain state of the game (title screen, in play, game over, etc.)
|
||||
//
|
||||
// This type is also used as the type for the interrupt vector, even though
|
||||
// the interrupt routine saves and restores everything before being called and
|
||||
// thus clearly does not actually trash all the registers. It is declared this
|
||||
// way so that the game state routines, which do trash these registers, can be
|
||||
// assigned to it.
|
||||
//
|
||||
// This type is also used as the type for the location the old interrupt vector
|
||||
// is backed up to, because all the game state routines `goto` the old handler
|
||||
// and the end of their own routines, so the type needs to be compatible.
|
||||
// (In a good sense, it is a continuation.)
|
||||
//
|
||||
// Further,
|
||||
//
|
||||
// It's very arguable that screen1/2/3/4 and colormap1/2/3/4 are not REALLY inputs.
|
||||
// They're only there to support the fact that game states sometimes clear the
|
||||
// screen, and sometimes don't. When they don't, they preserve the screen, and
|
||||
// currently the way to say "we preserve the screen" is to have it as both input
|
||||
// and output. There is probably a better way to do this, but it needs thought.
|
||||
//
|
||||
|
||||
typedef routine
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta, actor_logic,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta, actor_logic,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target, dispatch_logic
|
||||
game_state_routine
|
||||
|
||||
//
|
||||
// Routines that are called to get the new state of each actor (player, enemy, etc.)
|
||||
//
|
||||
// Routines that conform to this type also follow this convention:
|
||||
//
|
||||
// Set carry if the player perished. Carry clear otherwise.
|
||||
//
|
||||
|
||||
typedef routine
|
||||
inputs pos, delta, joy2, screen
|
||||
outputs pos, delta, new_pos, screen, c
|
||||
trashes a, x, y, z, n, v, ptr, compare_target
|
||||
logic_routine
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// System Locations
|
||||
// ----------------------------------------------------------------
|
||||
@ -9,15 +61,15 @@
|
||||
byte vic_border @ 53280
|
||||
byte vic_bg @ 53281
|
||||
|
||||
byte table screen1 @ 1024
|
||||
byte table screen2 @ 1274
|
||||
byte table screen3 @ 1524
|
||||
byte table screen4 @ 1774
|
||||
byte table[256] screen1 @ 1024
|
||||
byte table[256] screen2 @ 1274
|
||||
byte table[256] screen3 @ 1524
|
||||
byte table[256] screen4 @ 1774
|
||||
|
||||
byte table colormap1 @ 55296
|
||||
byte table colormap2 @ 55546
|
||||
byte table colormap3 @ 55796
|
||||
byte table colormap4 @ 56046
|
||||
byte table[256] colormap1 @ 55296
|
||||
byte table[256] colormap2 @ 55546
|
||||
byte table[256] colormap3 @ 55796
|
||||
byte table[256] colormap4 @ 56046
|
||||
|
||||
buffer[2048] screen @ 1024
|
||||
byte joy2 @ $dc00
|
||||
@ -28,15 +80,18 @@ byte joy2 @ $dc00
|
||||
|
||||
pointer ptr @ 254
|
||||
|
||||
word table actor_pos
|
||||
word table[256] actor_pos
|
||||
word pos
|
||||
word new_pos
|
||||
|
||||
word table actor_delta
|
||||
word table[256] actor_delta
|
||||
word delta
|
||||
|
||||
vector (logic_routine) table[256] actor_logic
|
||||
vector logic_routine dispatch_logic
|
||||
|
||||
byte button_down : 0 // effectively static-local to check_button
|
||||
byte table press_fire_msg: "PRESS`FIRE`TO`PLAY"
|
||||
byte table[32] press_fire_msg: "PRESS`FIRE`TO`PLAY"
|
||||
|
||||
byte save_x
|
||||
word compare_target
|
||||
@ -44,52 +99,23 @@ word compare_target
|
||||
//
|
||||
// Points to the routine that implements the current game state.
|
||||
//
|
||||
// It's very arguable that screen1/2/3/4 and colormap1/2/3/4 are not REALLY inputs.
|
||||
// They're only there to support the fact that game states sometimes clear the
|
||||
// screen, and sometimes don't. When they don't, they preserve the screen, and
|
||||
// currently the way to say "we preserve the screen" is to have it as both input
|
||||
// and output. There is probably a better way to do this, but it needs thought.
|
||||
//
|
||||
|
||||
vector dispatch_game_state
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
|
||||
vector game_state_routine
|
||||
dispatch_game_state
|
||||
|
||||
//
|
||||
// The constraints on these 2 vectors are kind-of sort-of big fibs.
|
||||
// They're only written this way so they can be compatible with our
|
||||
// routine. In fact, CINV is an interrupt routine where it doesn't
|
||||
// really matter what you trash anyway, because all registers were
|
||||
/// saved by the caller (the KERNAL) and will be restored by the end
|
||||
// of the code of the saved origin cinv routine that we goto.
|
||||
//
|
||||
// I wonder if this could be arranged somehow to be less fibby, in
|
||||
// a future version of SixtyPical.
|
||||
// Interrupt vector. Has same type as game states (see above.)
|
||||
//
|
||||
|
||||
vector cinv
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
|
||||
@ 788
|
||||
vector game_state_routine
|
||||
cinv @ 788
|
||||
|
||||
vector save_cinv
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
|
||||
//
|
||||
// Location to which the old interrupt vector is saved before replacement.
|
||||
//
|
||||
|
||||
vector game_state_routine
|
||||
save_cinv
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Utility Routines
|
||||
@ -216,8 +242,8 @@ routine check_new_position_in_bounds
|
||||
}
|
||||
|
||||
routine init_game
|
||||
inputs actor_pos, actor_delta
|
||||
outputs actor_pos, actor_delta, pos
|
||||
inputs actor_pos, actor_delta, actor_logic
|
||||
outputs actor_pos, actor_delta, pos, actor_logic
|
||||
trashes a, y, z, n, c, v
|
||||
{
|
||||
ld y, 0
|
||||
@ -225,6 +251,7 @@ routine init_game
|
||||
repeat {
|
||||
copy pos, actor_pos + y
|
||||
copy word 40, actor_delta + y
|
||||
copy forward enemy_logic, actor_logic + y
|
||||
|
||||
st off, c
|
||||
add pos, word 7
|
||||
@ -236,20 +263,14 @@ routine init_game
|
||||
ld y, 0
|
||||
copy word 0, actor_pos + y
|
||||
copy word 0, actor_delta + y
|
||||
copy forward player_logic, actor_logic + y
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Actor Logics
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Sets carry if the player perished. Carry clear otherwise.
|
||||
//
|
||||
|
||||
routine player_logic
|
||||
inputs pos, delta, joy2, screen
|
||||
outputs pos, delta, new_pos, screen, c
|
||||
trashes a, x, y, z, n, v, ptr, compare_target
|
||||
define player_logic logic_routine
|
||||
{
|
||||
call read_stick
|
||||
|
||||
@ -304,14 +325,7 @@ routine player_logic
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Sets carry if the player perished. Carry clear otherwise.
|
||||
//
|
||||
|
||||
routine enemy_logic
|
||||
inputs pos, delta, screen
|
||||
outputs pos, delta, new_pos, screen, c
|
||||
trashes a, x, y, z, n, v, ptr, compare_target
|
||||
define enemy_logic logic_routine
|
||||
{
|
||||
call calculate_new_position
|
||||
call check_new_position_in_bounds
|
||||
@ -377,18 +391,7 @@ routine enemy_logic
|
||||
// Game States
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Because these all `goto save_cinv` at the end, they must have the same signature as that routine.
|
||||
//
|
||||
|
||||
routine game_state_title_screen
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
|
||||
define game_state_title_screen game_state_routine
|
||||
{
|
||||
ld y, 0
|
||||
repeat {
|
||||
@ -426,14 +429,7 @@ routine game_state_title_screen
|
||||
goto save_cinv
|
||||
}
|
||||
|
||||
routine game_state_play
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
|
||||
define game_state_play game_state_routine
|
||||
{
|
||||
ld x, 0
|
||||
repeat {
|
||||
@ -442,33 +438,19 @@ routine game_state_play
|
||||
|
||||
st x, save_x
|
||||
|
||||
// FIXME need VECTOR TABLEs to make this happen:
|
||||
// copy actor_logic, x dispatch_logic
|
||||
// call indirect_jsr_logic
|
||||
// For now, just check the actor ID to see what type it is, and go from there.
|
||||
|
||||
cmp x, 0
|
||||
if z {
|
||||
call player_logic
|
||||
} else {
|
||||
call enemy_logic
|
||||
}
|
||||
copy actor_logic + x, dispatch_logic
|
||||
call dispatch_logic
|
||||
|
||||
if c {
|
||||
// Player died! Want no dead! Break out of the loop (this is a bit awkward.)
|
||||
call clear_screen
|
||||
copy forward game_state_game_over, dispatch_game_state
|
||||
ld x, 15
|
||||
st x, save_x
|
||||
trash n
|
||||
trash z
|
||||
trash x
|
||||
} else {
|
||||
ld x, save_x
|
||||
trash c
|
||||
}
|
||||
|
||||
ld x, save_x
|
||||
|
||||
copy pos, actor_pos + x
|
||||
copy delta, actor_delta + x
|
||||
|
||||
@ -479,14 +461,7 @@ routine game_state_play
|
||||
goto save_cinv
|
||||
}
|
||||
|
||||
routine game_state_game_over
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
|
||||
define game_state_game_over game_state_routine
|
||||
{
|
||||
st off, c
|
||||
call check_button
|
||||
@ -516,14 +491,7 @@ routine game_state_game_over
|
||||
// * Main Game Loop Driver *
|
||||
// *************************
|
||||
|
||||
routine our_cinv
|
||||
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs button_down, dispatch_game_state,
|
||||
actor_pos, pos, new_pos, actor_delta, delta,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
|
||||
define our_cinv game_state_routine
|
||||
{
|
||||
goto dispatch_game_state
|
||||
}
|
||||
|
58
eg/vector-table.60p
Normal file
58
eg/vector-table.60p
Normal file
@ -0,0 +1,58 @@
|
||||
vector routine
|
||||
trashes a, z, n
|
||||
print
|
||||
|
||||
vector (routine
|
||||
trashes a, z, n)
|
||||
table[32] vectors
|
||||
|
||||
routine chrout
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine printa
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 65
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine printb
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 66
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine main
|
||||
inputs vectors
|
||||
outputs vectors
|
||||
trashes print, a, x, z, n, c
|
||||
{
|
||||
ld x, 0
|
||||
copy printa, print
|
||||
copy print, vectors + x
|
||||
inc x
|
||||
copy printa, print
|
||||
copy print, vectors + x
|
||||
inc x
|
||||
copy printb, print
|
||||
copy print, vectors + x
|
||||
inc x
|
||||
copy printa, print
|
||||
copy print, vectors + x
|
||||
inc x
|
||||
copy printb, print
|
||||
copy print, vectors + x
|
||||
|
||||
copy printa, print
|
||||
|
||||
ld x, 0
|
||||
repeat {
|
||||
copy vectors + x, print
|
||||
call print
|
||||
inc x
|
||||
cmp x, 5
|
||||
} until z
|
||||
}
|
51
eg/vector-table2.60p
Normal file
51
eg/vector-table2.60p
Normal file
@ -0,0 +1,51 @@
|
||||
vector routine
|
||||
trashes a, z, n
|
||||
print
|
||||
|
||||
vector (routine
|
||||
trashes a, z, n)
|
||||
table[32] vectors
|
||||
|
||||
routine chrout
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine printa
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 65
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine printb
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 66
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine main
|
||||
inputs vectors
|
||||
outputs vectors
|
||||
trashes print, a, x, z, n, c
|
||||
{
|
||||
ld x, 0
|
||||
copy printa, vectors + x
|
||||
inc x
|
||||
copy printa, vectors + x
|
||||
inc x
|
||||
copy printb, vectors + x
|
||||
inc x
|
||||
copy printa, vectors + x
|
||||
inc x
|
||||
copy printb, vectors + x
|
||||
|
||||
ld x, 0
|
||||
repeat {
|
||||
copy vectors + x, print
|
||||
call print
|
||||
inc x
|
||||
cmp x, 5
|
||||
} until z
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
vector print
|
||||
vector routine
|
||||
trashes a, z, n
|
||||
print
|
||||
|
||||
routine chrout
|
||||
inputs a
|
||||
@ -21,7 +22,6 @@ routine printb
|
||||
}
|
||||
|
||||
routine main
|
||||
inputs printa, printb
|
||||
trashes print, a, z, n
|
||||
{
|
||||
copy printa, print
|
||||
|
@ -6,7 +6,7 @@ if [ "X$1" = "X" ]; then
|
||||
exit 1
|
||||
fi
|
||||
OUT=/tmp/a-out.prg
|
||||
bin/sixtypical --traceback --analyze --compile --basic-prelude $SRC > $OUT || exit 1
|
||||
bin/sixtypical --traceback --basic-prelude $SRC > $OUT || exit 1
|
||||
if [ -e vicerc ]; then
|
||||
x64 -config vicerc $OUT
|
||||
else
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
from sixtypical.ast import Program, Routine, Block, Instr
|
||||
from sixtypical.model import (
|
||||
TYPE_BYTE, TYPE_WORD, TYPE_BYTE_TABLE, TYPE_WORD_TABLE, BufferType, PointerType, VectorType, ExecutableType,
|
||||
TYPE_BYTE, TYPE_WORD,
|
||||
TableType, BufferType, PointerType, VectorType, RoutineType,
|
||||
ConstantRef, LocationRef, IndirectRef, IndexedRef, AddressRef,
|
||||
REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
|
||||
)
|
||||
@ -175,13 +176,18 @@ class Analyzer(object):
|
||||
(location.name, self.current_routine.name)
|
||||
)
|
||||
|
||||
def assert_affected_within(self, name, affected, limited_to):
|
||||
def assert_affected_within(self, name, affecting_type, limiting_type):
|
||||
assert name in ('inputs', 'outputs', 'trashes')
|
||||
affected = getattr(affecting_type, name)
|
||||
limited_to = getattr(limiting_type, name)
|
||||
overage = affected - limited_to
|
||||
if not overage:
|
||||
return
|
||||
message = 'in %s: %s are %s but affects %s which exceeds it by: %s ' % (
|
||||
message = 'in %s: %s for %s are %s\n\nbut %s affects %s\n\nwhich exceeds it by: %s ' % (
|
||||
self.current_routine.name, name,
|
||||
LocationRef.format_set(limited_to), LocationRef.format_set(affected), LocationRef.format_set(overage)
|
||||
limiting_type, LocationRef.format_set(limited_to),
|
||||
affecting_type, LocationRef.format_set(affected),
|
||||
LocationRef.format_set(overage)
|
||||
)
|
||||
raise IncompatibleConstraintsError(message)
|
||||
|
||||
@ -231,12 +237,13 @@ class Analyzer(object):
|
||||
|
||||
if opcode == 'ld':
|
||||
if instr.index:
|
||||
if src.type == TYPE_BYTE_TABLE and dest.type == TYPE_BYTE:
|
||||
if TableType.is_a_table_type(src.type, TYPE_BYTE) and dest.type == TYPE_BYTE:
|
||||
pass
|
||||
else:
|
||||
raise TypeMismatchError('%s and %s in %s' %
|
||||
(src.name, dest.name, self.current_routine.name)
|
||||
)
|
||||
context.assert_meaningful(instr.index)
|
||||
elif src.type != dest.type:
|
||||
raise TypeMismatchError('%s and %s in %s' %
|
||||
(src.name, dest.name, self.current_routine.name)
|
||||
@ -245,13 +252,14 @@ class Analyzer(object):
|
||||
context.set_written(dest, FLAG_Z, FLAG_N)
|
||||
elif opcode == 'st':
|
||||
if instr.index:
|
||||
if src.type == TYPE_BYTE and dest.type == TYPE_BYTE_TABLE:
|
||||
if src.type == TYPE_BYTE and TableType.is_a_table_type(dest.type, TYPE_BYTE):
|
||||
pass
|
||||
else:
|
||||
raise TypeMismatchError((src, dest))
|
||||
context.assert_meaningful(instr.index)
|
||||
elif src.type != dest.type:
|
||||
raise TypeMismatchError('%s and %s in %s' %
|
||||
(src.name, dest.name, self.current_routine.name)
|
||||
raise TypeMismatchError('%r and %r in %s' %
|
||||
(src, dest, self.current_routine.name)
|
||||
)
|
||||
context.assert_meaningful(src)
|
||||
context.set_written(dest)
|
||||
@ -300,6 +308,8 @@ class Analyzer(object):
|
||||
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C)
|
||||
elif opcode == 'call':
|
||||
type = instr.location.type
|
||||
if isinstance(type, VectorType):
|
||||
type = type.of_type
|
||||
for ref in type.inputs:
|
||||
context.assert_meaningful(ref)
|
||||
for ref in type.outputs:
|
||||
@ -361,13 +371,22 @@ class Analyzer(object):
|
||||
raise TypeMismatchError((src, dest))
|
||||
|
||||
elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndexedRef):
|
||||
if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
|
||||
if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
|
||||
pass
|
||||
elif (isinstance(src.type, VectorType) and isinstance(dest.ref.type, TableType) and
|
||||
RoutineType.executable_types_compatible(src.type.of_type, dest.ref.type.of_type)):
|
||||
pass
|
||||
elif (isinstance(src.type, RoutineType) and isinstance(dest.ref.type, TableType) and
|
||||
RoutineType.executable_types_compatible(src.type, dest.ref.type.of_type)):
|
||||
pass
|
||||
else:
|
||||
raise TypeMismatchError((src, dest))
|
||||
|
||||
elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
|
||||
if src.ref.type == TYPE_WORD_TABLE and dest.type == TYPE_WORD:
|
||||
if TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD:
|
||||
pass
|
||||
elif (isinstance(src.ref.type, TableType) and isinstance(dest.type, VectorType) and
|
||||
RoutineType.executable_types_compatible(src.ref.type.of_type, dest.type.of_type)):
|
||||
pass
|
||||
else:
|
||||
raise TypeMismatchError((src, dest))
|
||||
@ -375,10 +394,10 @@ class Analyzer(object):
|
||||
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):
|
||||
self.assert_affected_within('inputs', src.type.inputs, dest.type.inputs)
|
||||
self.assert_affected_within('outputs', src.type.outputs, dest.type.outputs)
|
||||
self.assert_affected_within('trashes', src.type.trashes, dest.type.trashes)
|
||||
elif isinstance(src.type, RoutineType) and isinstance(dest.type, VectorType):
|
||||
self.assert_affected_within('inputs', src.type, dest.type.of_type)
|
||||
self.assert_affected_within('outputs', src.type, dest.type.of_type)
|
||||
self.assert_affected_within('trashes', src.type, dest.type.of_type)
|
||||
else:
|
||||
raise TypeMismatchError((src, dest))
|
||||
else:
|
||||
@ -393,19 +412,16 @@ class Analyzer(object):
|
||||
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) # TODO and REG_Y? if not, why not?
|
||||
context.set_touched(dest)
|
||||
context.set_written(dest)
|
||||
elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
|
||||
context.assert_meaningful(src, dest.ref, dest.index)
|
||||
context.set_touched(src) # TODO and dest.index?
|
||||
context.set_written(dest.ref)
|
||||
elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef):
|
||||
context.assert_meaningful(src, dest.ref, dest.index)
|
||||
context.set_written(dest.ref)
|
||||
elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
|
||||
context.assert_meaningful(src.ref, src.index, dest)
|
||||
context.set_touched(dest) # TODO and src.index?
|
||||
context.assert_meaningful(src.ref, src.index)
|
||||
context.set_touched(dest)
|
||||
context.set_written(dest)
|
||||
else:
|
||||
context.assert_meaningful(src)
|
||||
@ -428,18 +444,20 @@ class Analyzer(object):
|
||||
location = instr.location
|
||||
type_ = location.type
|
||||
|
||||
if not isinstance(type_, ExecutableType):
|
||||
if not isinstance(type_, (RoutineType, VectorType)):
|
||||
raise TypeMismatchError(location)
|
||||
|
||||
# assert that the dest routine's inputs are all initialized
|
||||
if isinstance(type_, VectorType):
|
||||
type_ = type_.of_type
|
||||
for ref in type_.inputs:
|
||||
context.assert_meaningful(ref)
|
||||
|
||||
# and that this routine's trashes and output constraints are a
|
||||
# superset of the called routine's
|
||||
current_type = self.current_routine.location.type
|
||||
self.assert_affected_within('outputs', type_.outputs, current_type.outputs)
|
||||
self.assert_affected_within('trashes', type_.trashes, current_type.trashes)
|
||||
self.assert_affected_within('outputs', type_, current_type)
|
||||
self.assert_affected_within('trashes', type_, current_type)
|
||||
|
||||
self.has_encountered_goto = True
|
||||
elif opcode == 'trash':
|
||||
|
@ -3,7 +3,8 @@
|
||||
from sixtypical.ast import Program, Routine, Block, Instr
|
||||
from sixtypical.model import (
|
||||
ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
|
||||
TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE, BufferType, PointerType, RoutineType, VectorType,
|
||||
TYPE_BIT, TYPE_BYTE, TYPE_WORD,
|
||||
TableType, BufferType, PointerType, RoutineType, VectorType,
|
||||
REG_A, REG_X, REG_Y, FLAG_C
|
||||
)
|
||||
from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte
|
||||
@ -54,10 +55,8 @@ class Compiler(object):
|
||||
length = 1
|
||||
elif type_ == TYPE_WORD or isinstance(type_, (PointerType, VectorType)):
|
||||
length = 2
|
||||
elif type_ == TYPE_BYTE_TABLE:
|
||||
length = 256
|
||||
elif type_ == TYPE_WORD_TABLE:
|
||||
length = 512
|
||||
elif isinstance(type_, TableType):
|
||||
length = type_.size * (1 if type_.of_type == TYPE_BYTE else 2)
|
||||
elif isinstance(type_, BufferType):
|
||||
length = type_.size
|
||||
if length is None:
|
||||
@ -79,6 +78,7 @@ class Compiler(object):
|
||||
for location, label in self.trampolines.iteritems():
|
||||
self.emitter.resolve_label(label)
|
||||
self.emitter.emit(JMP(Indirect(self.labels[location.name])))
|
||||
self.emitter.emit(RTS())
|
||||
|
||||
# initialized data
|
||||
for defn in program.defns:
|
||||
@ -90,8 +90,8 @@ class Compiler(object):
|
||||
initial_data = Byte(defn.initial)
|
||||
elif type_ == TYPE_WORD:
|
||||
initial_data = Word(defn.initial)
|
||||
elif type_ == TYPE_BYTE_TABLE:
|
||||
initial_data = Table(defn.initial)
|
||||
elif TableType.is_a_table_type(type_, TYPE_BYTE):
|
||||
initial_data = Table(defn.initial, type_.size)
|
||||
else:
|
||||
raise NotImplementedError(type_)
|
||||
label.set_length(initial_data.size())
|
||||
@ -404,17 +404,32 @@ class Compiler(object):
|
||||
self.emitter.emit(LDA(Immediate(LowAddressByte(src_label))))
|
||||
self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1))))
|
||||
elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
|
||||
if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
|
||||
if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
|
||||
src_label = self.labels[src.name]
|
||||
dest_label = self.labels[dest.ref.name]
|
||||
self.emitter.emit(LDA(Absolute(src_label)))
|
||||
self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
|
||||
self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
|
||||
self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
|
||||
elif isinstance(src.type, VectorType) and isinstance(dest.ref.type, TableType) and isinstance(dest.ref.type.of_type, VectorType):
|
||||
# FIXME this is the exact same as above - can this be simplified?
|
||||
src_label = self.labels[src.name]
|
||||
dest_label = self.labels[dest.ref.name]
|
||||
self.emitter.emit(LDA(Absolute(src_label)))
|
||||
self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
|
||||
self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
|
||||
self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
|
||||
elif isinstance(src.type, RoutineType) and isinstance(dest.ref.type, TableType) and isinstance(dest.ref.type.of_type, VectorType):
|
||||
src_label = self.labels[src.name]
|
||||
dest_label = self.labels[dest.ref.name]
|
||||
self.emitter.emit(LDA(Immediate(HighAddressByte(src_label))))
|
||||
self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
|
||||
self.emitter.emit(LDA(Immediate(LowAddressByte(src_label))))
|
||||
self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
|
||||
else:
|
||||
raise NotImplementedError
|
||||
elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef):
|
||||
if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
|
||||
if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
|
||||
dest_label = self.labels[dest.ref.name]
|
||||
self.emitter.emit(LDA(Immediate(Byte(src.low_byte()))))
|
||||
self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
|
||||
@ -423,7 +438,15 @@ class Compiler(object):
|
||||
else:
|
||||
raise NotImplementedError
|
||||
elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
|
||||
if src.ref.type == TYPE_WORD_TABLE and dest.type == TYPE_WORD:
|
||||
if TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD:
|
||||
src_label = self.labels[src.ref.name]
|
||||
dest_label = self.labels[dest.name]
|
||||
self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(src_label)))
|
||||
self.emitter.emit(STA(Absolute(dest_label)))
|
||||
self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(Offset(src_label, 256))))
|
||||
self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
|
||||
elif isinstance(dest.type, VectorType) and isinstance(src.ref.type, TableType) and isinstance(src.ref.type.of_type, VectorType):
|
||||
# FIXME this is the exact same as above - can this be simplified?
|
||||
src_label = self.labels[src.ref.name]
|
||||
dest_label = self.labels[dest.name]
|
||||
self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(src_label)))
|
||||
|
@ -48,12 +48,13 @@ class Word(Emittable):
|
||||
|
||||
|
||||
class Table(Emittable):
|
||||
def __init__(self, value):
|
||||
def __init__(self, value, size):
|
||||
# TODO: range-checking
|
||||
self.value = value
|
||||
self._size = size
|
||||
|
||||
def size(self):
|
||||
return 256
|
||||
return self._size
|
||||
|
||||
def serialize(self, addr=None):
|
||||
bytes = []
|
||||
|
@ -1,208 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from sixtypical.ast import Program, Routine, Block, Instr
|
||||
from sixtypical.model import (
|
||||
ConstantRef, LocationRef, PartRef, IndirectRef,
|
||||
REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
|
||||
)
|
||||
|
||||
|
||||
class Context(object):
|
||||
def __init__(self):
|
||||
self._store = {}
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join("%s: %s" % (name, value)
|
||||
for (name, value) in sorted(self._store.iteritems())
|
||||
if not isinstance(value, Routine))
|
||||
|
||||
def get(self, ref):
|
||||
if isinstance(ref, ConstantRef):
|
||||
return ref.value
|
||||
elif isinstance(ref, LocationRef):
|
||||
return self._store[ref.name]
|
||||
elif isinstance(ref, PartRef):
|
||||
value = self.get(ref.ref)
|
||||
if ref.height == 0:
|
||||
return value & 255
|
||||
elif ref.height == 1:
|
||||
return (value >> 8) & 255
|
||||
else:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
raise ValueError(ref)
|
||||
|
||||
def set(self, ref, value):
|
||||
if isinstance(ref, PartRef):
|
||||
old = self.get(ref.ref)
|
||||
if ref.height == 0:
|
||||
value = (old & (255 << 8)) | value
|
||||
elif ref.height == 1:
|
||||
value = (value << 8) | (old & 255)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
ref = ref.ref
|
||||
assert isinstance(ref, LocationRef)
|
||||
self._store[ref.name] = value
|
||||
|
||||
|
||||
class Evaluator(object):
|
||||
|
||||
def eval_program(self, program):
|
||||
assert isinstance(program, Program)
|
||||
context = Context()
|
||||
for ref in (REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C):
|
||||
context.set(ref, 0)
|
||||
main = None
|
||||
|
||||
for defn in program.defns:
|
||||
if defn.initial is not None:
|
||||
context.set(defn.location, defn.initial)
|
||||
|
||||
for routine in program.routines:
|
||||
context.set(routine.location, routine)
|
||||
if routine.name == 'main':
|
||||
main = routine
|
||||
|
||||
self.eval_routine(main, context)
|
||||
return context
|
||||
|
||||
def eval_routine(self, routine, context):
|
||||
assert isinstance(routine, Routine)
|
||||
self.next_routine = routine
|
||||
while self.next_routine:
|
||||
routine = self.next_routine
|
||||
self.next_routine = None
|
||||
self.eval_block(routine.block, context)
|
||||
|
||||
def eval_block(self, block, context):
|
||||
assert isinstance(block, Block)
|
||||
for i in block.instrs:
|
||||
self.eval_instr(i, context)
|
||||
if self.next_routine:
|
||||
break
|
||||
|
||||
def eval_instr(self, instr, context):
|
||||
assert isinstance(instr, Instr)
|
||||
opcode = instr.opcode
|
||||
dest = instr.dest
|
||||
src = instr.src
|
||||
|
||||
if opcode == 'ld':
|
||||
result = context.get(src)
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'st':
|
||||
context.set(dest, context.get(src))
|
||||
elif opcode == 'add':
|
||||
carry = context.get(FLAG_C)
|
||||
val = context.get(src)
|
||||
now = context.get(dest)
|
||||
result = now + val + carry
|
||||
if result > 255:
|
||||
result &= 255
|
||||
context.set(FLAG_C, 1)
|
||||
else:
|
||||
context.set(FLAG_C, 0)
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'sub':
|
||||
carry = context.get(FLAG_C)
|
||||
val = context.get(src)
|
||||
now = context.get(dest)
|
||||
result = now - val - carry
|
||||
if result < 0:
|
||||
result &= 255
|
||||
context.set(FLAG_C, 1)
|
||||
else:
|
||||
context.set(FLAG_C, 0)
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'inc':
|
||||
val = context.get(dest)
|
||||
result = (val + 1) & 255
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'dec':
|
||||
val = context.get(dest)
|
||||
result = (val - 1) & 255
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'cmp':
|
||||
val = context.get(src)
|
||||
now = context.get(dest)
|
||||
result = now - val
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
if result < 0:
|
||||
result &= 255
|
||||
context.set(FLAG_C, 1)
|
||||
else:
|
||||
context.set(FLAG_C, 0)
|
||||
elif opcode == 'and':
|
||||
result = context.get(dest) & context.get(src)
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'or':
|
||||
result = context.get(dest) | context.get(src)
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'xor':
|
||||
result = context.get(dest) ^ context.get(src)
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'shl':
|
||||
val = context.get(dest)
|
||||
carry = context.get(FLAG_C)
|
||||
context.set(FLAG_C, 1 if val & 128 else 0)
|
||||
result = ((val << 1) + carry) & 255
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'shr':
|
||||
val = context.get(dest)
|
||||
carry = context.get(FLAG_C)
|
||||
context.set(FLAG_C, 1 if val & 1 else 0)
|
||||
result = (val >> 1) + (carry * 128)
|
||||
context.set(FLAG_Z, 1 if result == 0 else 0)
|
||||
context.set(FLAG_N, 1 if result & 128 else 0)
|
||||
context.set(dest, result)
|
||||
elif opcode == 'call':
|
||||
self.eval_routine(context.get(instr.location), context)
|
||||
elif opcode == 'goto':
|
||||
self.next_routine = context.get(instr.location)
|
||||
elif opcode == 'if':
|
||||
val = context.get(src)
|
||||
test = (val != 0) if not instr.inverted else (val == 0)
|
||||
if test:
|
||||
self.eval_block(instr.block1, context)
|
||||
elif instr.block2:
|
||||
self.eval_block(instr.block2, context)
|
||||
elif opcode == 'repeat':
|
||||
self.eval_block(instr.block, context)
|
||||
while context.get(src) == 0:
|
||||
self.eval_block(instr.block, context)
|
||||
elif opcode == 'copy':
|
||||
if isinstance(src, IndirectRef):
|
||||
raise NotImplementedError("this doesn't actually work")
|
||||
src = src.ref
|
||||
if isinstance(dest, IndirectRef):
|
||||
raise NotImplementedError("this doesn't actually work")
|
||||
dest = dest.ref
|
||||
context.set(dest, context.get(src))
|
||||
# these are trashed; so could be anything really
|
||||
context.set(REG_A, 0)
|
||||
context.set(FLAG_Z, 0)
|
||||
context.set(FLAG_N, 0)
|
||||
elif opcode == 'with-sei':
|
||||
self.eval_block(instr.block)
|
||||
else:
|
||||
raise NotImplementedError
|
@ -17,25 +17,38 @@ class Type(object):
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
def backpatch_constraint_labels(self, resolver):
|
||||
def resolve(w):
|
||||
if not isinstance(w, basestring):
|
||||
return w
|
||||
return resolver(w)
|
||||
if isinstance(self, TableType):
|
||||
self.of_type.backpatch_constraint_labels(resolver)
|
||||
elif isinstance(self, VectorType):
|
||||
self.of_type.backpatch_constraint_labels(resolver)
|
||||
elif isinstance(self, RoutineType):
|
||||
self.inputs = set([resolve(w) for w in self.inputs])
|
||||
self.outputs = set([resolve(w) for w in self.outputs])
|
||||
self.trashes = set([resolve(w) for w in self.trashes])
|
||||
|
||||
|
||||
TYPE_BIT = Type('bit')
|
||||
TYPE_BYTE = Type('byte')
|
||||
TYPE_BYTE_TABLE = Type('byte table')
|
||||
TYPE_WORD = Type('word')
|
||||
TYPE_WORD_TABLE = Type('word table')
|
||||
|
||||
|
||||
class ExecutableType(Type):
|
||||
"""Used for routines and vectors."""
|
||||
def __init__(self, name, inputs=None, outputs=None, trashes=None):
|
||||
self.name = name
|
||||
|
||||
class RoutineType(Type):
|
||||
"""This memory location contains the code for a routine."""
|
||||
def __init__(self, inputs=None, outputs=None, trashes=None):
|
||||
self.name = 'routine'
|
||||
self.inputs = inputs or set()
|
||||
self.outputs = outputs or set()
|
||||
self.trashes = trashes or set()
|
||||
|
||||
def __repr__(self):
|
||||
return 'RoutineType(%r, inputs=%r, outputs=%r, trashes=%r)' % (
|
||||
self.name, self.inputs, self.outputs, self.trashes
|
||||
return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % (
|
||||
self.__class__.__name__, self.name, self.inputs, self.outputs, self.trashes
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -49,17 +62,56 @@ class ExecutableType(Type):
|
||||
def __hash__(self):
|
||||
return hash(self.name) ^ hash(self.inputs) ^ hash(self.outputs) ^ hash(self.trashes)
|
||||
|
||||
|
||||
class RoutineType(ExecutableType):
|
||||
"""This memory location contains the code for a routine."""
|
||||
def __init__(self, **kwargs):
|
||||
super(RoutineType, self).__init__('routine', **kwargs)
|
||||
@classmethod
|
||||
def executable_types_compatible(cls_, src, dest):
|
||||
"""Returns True iff a value of type `src` can be assigned to a storage location of type `dest`."""
|
||||
if isinstance(src, VectorType):
|
||||
src = src.of_type
|
||||
if isinstance(dest, VectorType):
|
||||
dest = dest.of_type
|
||||
if isinstance(src, RoutineType) and isinstance(dest, RoutineType):
|
||||
# TODO: I'm sure we can replace some of these with subset-containment, but that requires thought
|
||||
return (
|
||||
src.inputs == dest.inputs and
|
||||
src.outputs == dest.outputs and
|
||||
src.trashes == dest.trashes
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class VectorType(ExecutableType):
|
||||
"""This memory location contains the address of a routine."""
|
||||
def __init__(self, **kwargs):
|
||||
super(VectorType, self).__init__('vector', **kwargs)
|
||||
class VectorType(Type):
|
||||
"""This memory location contains the address of some other type (currently, only RoutineType)."""
|
||||
def __init__(self, of_type):
|
||||
self.name = 'vector'
|
||||
self.of_type = of_type
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (
|
||||
self.__class__.__name__, self.of_type
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name and self.of_type == other.of_type
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name) ^ hash(self.of_type)
|
||||
|
||||
|
||||
class TableType(Type):
|
||||
def __init__(self, of_type, size):
|
||||
self.of_type = of_type
|
||||
self.size = size
|
||||
self.name = '{} table[{}]'.format(self.of_type.name, self.size)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %r)' % (
|
||||
self.__class__.__name__, self.of_type, self.size
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_a_table_type(cls_, x, of_type):
|
||||
return isinstance(x, TableType) and x.of_type == of_type
|
||||
|
||||
|
||||
class BufferType(Type):
|
||||
@ -108,13 +160,6 @@ class LocationRef(Ref):
|
||||
def is_constant(self):
|
||||
return isinstance(self.type, RoutineType)
|
||||
|
||||
def backpatch_vector_labels(self, resolver):
|
||||
if isinstance(self.type, ExecutableType):
|
||||
t = self.type
|
||||
t.inputs = set([resolver(w) for w in t.inputs])
|
||||
t.outputs = set([resolver(w) for w in t.outputs])
|
||||
t.trashes = set([resolver(w) for w in t.trashes])
|
||||
|
||||
@classmethod
|
||||
def format_set(cls, location_refs):
|
||||
return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)])
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
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,
|
||||
TYPE_BIT, TYPE_BYTE, TYPE_WORD,
|
||||
RoutineType, VectorType, TableType, BufferType, PointerType,
|
||||
LocationRef, ConstantRef, IndirectRef, IndexedRef, AddressRef,
|
||||
)
|
||||
from sixtypical.scanner import Scanner
|
||||
@ -19,6 +19,7 @@ class Parser(object):
|
||||
def __init__(self, text):
|
||||
self.scanner = Scanner(text)
|
||||
self.symbols = {} # token -> SymEntry
|
||||
self.typedefs = {} # token -> Type AST
|
||||
for token in ('a', 'x', 'y'):
|
||||
self.symbols[token] = SymEntry(None, LocationRef(TYPE_BYTE, token))
|
||||
for token in ('c', 'z', 'n', 'v'):
|
||||
@ -35,16 +36,25 @@ class Parser(object):
|
||||
def program(self):
|
||||
defns = []
|
||||
routines = []
|
||||
while self.scanner.on('byte', 'word', 'vector', 'buffer', 'pointer'):
|
||||
while self.scanner.on('typedef'):
|
||||
typedef = self.typedef()
|
||||
typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine',
|
||||
typenames.extend(self.typedefs.keys())
|
||||
while self.scanner.on(*typenames):
|
||||
defn = self.defn()
|
||||
name = defn.name
|
||||
if name in self.symbols:
|
||||
raise SyntaxError('Symbol "%s" already declared' % name)
|
||||
self.symbols[name] = SymEntry(defn, defn.location)
|
||||
defns.append(defn)
|
||||
while self.scanner.on('routine'):
|
||||
routine = self.routine()
|
||||
name = routine.name
|
||||
while self.scanner.on('define', 'routine'):
|
||||
if self.scanner.consume('define'):
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
routine = self.routine(name)
|
||||
else:
|
||||
routine = self.legacy_routine()
|
||||
name = routine.name
|
||||
if name in self.symbols:
|
||||
raise SyntaxError('Symbol "%s" already declared' % name)
|
||||
self.symbols[name] = SymEntry(routine, routine.location)
|
||||
@ -52,44 +62,46 @@ class Parser(object):
|
||||
self.scanner.check_type('EOF')
|
||||
|
||||
# now backpatch the executable types.
|
||||
#for type_name, type_ in self.typedefs.iteritems():
|
||||
# type_.backpatch_constraint_labels(lambda w: self.lookup(w))
|
||||
for defn in defns:
|
||||
defn.location.backpatch_vector_labels(lambda w: self.lookup(w))
|
||||
defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
|
||||
for routine in routines:
|
||||
routine.location.backpatch_vector_labels(lambda w: self.lookup(w))
|
||||
routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
|
||||
for instr in self.backpatch_instrs:
|
||||
if instr.opcode in ('call', 'goto'):
|
||||
name = instr.location
|
||||
if name not in self.symbols:
|
||||
raise SyntaxError('Undefined routine "%s"' % name)
|
||||
if not isinstance(self.symbols[name].model.type, ExecutableType):
|
||||
if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)):
|
||||
raise SyntaxError('Illegal call of non-executable "%s"' % name)
|
||||
instr.location = self.symbols[name].model
|
||||
if instr.opcode in ('copy',) and isinstance(instr.src, basestring):
|
||||
name = instr.src
|
||||
if name not in self.symbols:
|
||||
raise SyntaxError('Undefined routine "%s"' % name)
|
||||
if not isinstance(self.symbols[name].model.type, ExecutableType):
|
||||
if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)):
|
||||
raise SyntaxError('Illegal copy of non-executable "%s"' % name)
|
||||
instr.src = self.symbols[name].model
|
||||
|
||||
return Program(defns=defns, routines=routines)
|
||||
|
||||
def typedef(self):
|
||||
self.scanner.expect('typedef')
|
||||
type_ = self.defn_type()
|
||||
name = self.defn_name()
|
||||
if name in self.typedefs:
|
||||
raise SyntaxError('Type "%s" already declared' % name)
|
||||
self.typedefs[name] = type_
|
||||
return type_
|
||||
|
||||
def defn(self):
|
||||
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)
|
||||
elif inputs or outputs or trashes:
|
||||
raise SyntaxError("Cannot apply constraints to non-vector type")
|
||||
name = self.defn_name()
|
||||
|
||||
initial = None
|
||||
if self.scanner.consume(':'):
|
||||
if type_ == TYPE_BYTE_TABLE and self.scanner.on_type('string literal'):
|
||||
if isinstance(type_, TableType) and self.scanner.on_type('string literal'):
|
||||
initial = self.scanner.token
|
||||
else:
|
||||
self.scanner.check_type('integer literal')
|
||||
@ -109,27 +121,57 @@ class Parser(object):
|
||||
|
||||
return Defn(name=name, addr=addr, initial=initial, location=location)
|
||||
|
||||
def defn_size(self):
|
||||
self.scanner.expect('[')
|
||||
self.scanner.check_type('integer literal')
|
||||
size = int(self.scanner.token)
|
||||
self.scanner.scan()
|
||||
self.scanner.expect(']')
|
||||
return size
|
||||
|
||||
def defn_type(self):
|
||||
type_ = None
|
||||
|
||||
if self.scanner.consume('('):
|
||||
type_ = self.defn_type()
|
||||
self.scanner.expect(')')
|
||||
return type_
|
||||
|
||||
if self.scanner.consume('byte'):
|
||||
if self.scanner.consume('table'):
|
||||
return TYPE_BYTE_TABLE
|
||||
return TYPE_BYTE
|
||||
type_ = TYPE_BYTE
|
||||
elif self.scanner.consume('word'):
|
||||
if self.scanner.consume('table'):
|
||||
return TYPE_WORD_TABLE
|
||||
return TYPE_WORD
|
||||
type_ = TYPE_WORD
|
||||
elif self.scanner.consume('vector'):
|
||||
return 'vector' # will be resolved to a Type by caller
|
||||
type_ = self.defn_type()
|
||||
if not isinstance(type_, RoutineType):
|
||||
raise SyntaxError("Vectors can only be of a routine, not %r" % type_)
|
||||
type_ = VectorType(type_)
|
||||
elif self.scanner.consume('routine'):
|
||||
(inputs, outputs, trashes) = self.constraints()
|
||||
type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
|
||||
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)
|
||||
size = self.defn_size()
|
||||
type_ = BufferType(size)
|
||||
elif self.scanner.consume('pointer'):
|
||||
type_ = PointerType()
|
||||
else:
|
||||
self.scanner.expect('pointer')
|
||||
return PointerType()
|
||||
type_name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
if type_name not in self.typedefs:
|
||||
raise SyntaxError("Undefined type '%s'" % type_name)
|
||||
type_ = self.typedefs[type_name]
|
||||
|
||||
if self.scanner.consume('table'):
|
||||
size = self.defn_size()
|
||||
type_ = TableType(type_, size)
|
||||
|
||||
return type_
|
||||
|
||||
def defn_name(self):
|
||||
self.scanner.check_type('identifier')
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
return name
|
||||
|
||||
def constraints(self):
|
||||
inputs = set()
|
||||
@ -143,11 +185,12 @@ class Parser(object):
|
||||
trashes = set(self.labels())
|
||||
return (inputs, outputs, trashes)
|
||||
|
||||
def routine(self):
|
||||
def legacy_routine(self):
|
||||
self.scanner.expect('routine')
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
(inputs, outputs, trashes) = self.constraints()
|
||||
type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
|
||||
if self.scanner.consume('@'):
|
||||
self.scanner.check_type('integer literal')
|
||||
block = None
|
||||
@ -156,10 +199,25 @@ class Parser(object):
|
||||
else:
|
||||
block = self.block()
|
||||
addr = None
|
||||
location = LocationRef(
|
||||
RoutineType(inputs=inputs, outputs=outputs, trashes=trashes),
|
||||
name
|
||||
location = LocationRef(type_, name)
|
||||
return Routine(
|
||||
name=name, block=block, addr=addr,
|
||||
location=location
|
||||
)
|
||||
|
||||
def routine(self, name):
|
||||
type_ = self.defn_type()
|
||||
if not isinstance(type_, RoutineType):
|
||||
raise SyntaxError("Can only define a routine, not %r" % type_)
|
||||
if self.scanner.consume('@'):
|
||||
self.scanner.check_type('integer literal')
|
||||
block = None
|
||||
addr = int(self.scanner.token)
|
||||
self.scanner.scan()
|
||||
else:
|
||||
block = self.block()
|
||||
addr = None
|
||||
location = LocationRef(type_, name)
|
||||
return Routine(
|
||||
name=name, block=block, addr=addr,
|
||||
location=location
|
||||
|
1
test.sh
1
test.sh
@ -2,6 +2,5 @@
|
||||
|
||||
falderal --substring-error \
|
||||
tests/SixtyPical\ Syntax.md \
|
||||
tests/SixtyPical\ Execution.md \
|
||||
tests/SixtyPical\ Analysis.md \
|
||||
tests/SixtyPical\ Compilation.md
|
||||
|
@ -7,7 +7,7 @@ static analysis rules.
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Analyze SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --analyze --traceback %(test-body-file) && echo ok"
|
||||
-> shell command "bin/sixtypical --analyze-only --traceback %(test-body-file) && echo ok"
|
||||
|
||||
-> Tests for functionality "Analyze SixtyPical program"
|
||||
|
||||
@ -236,14 +236,14 @@ Can't `st` a `word` type.
|
||||
| ld a, 0
|
||||
| st a, foo
|
||||
| }
|
||||
? TypeMismatchError: a and foo in main
|
||||
? TypeMismatchError
|
||||
|
||||
### tables ###
|
||||
|
||||
Storing to a table, you must use an index, and vice-versa.
|
||||
Storing to a table, you must use an index.
|
||||
|
||||
| byte one
|
||||
| byte table many
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs one
|
||||
@ -256,7 +256,7 @@ Storing to a table, you must use an index, and vice-versa.
|
||||
= ok
|
||||
|
||||
| byte one
|
||||
| byte table many
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs many
|
||||
@ -269,7 +269,7 @@ Storing to a table, you must use an index, and vice-versa.
|
||||
? TypeMismatchError
|
||||
|
||||
| byte one
|
||||
| byte table many
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs one
|
||||
@ -282,7 +282,7 @@ Storing to a table, you must use an index, and vice-versa.
|
||||
? TypeMismatchError
|
||||
|
||||
| byte one
|
||||
| byte table many
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs many
|
||||
@ -294,7 +294,21 @@ Storing to a table, you must use an index, and vice-versa.
|
||||
| }
|
||||
= ok
|
||||
|
||||
Reading from a table, you must use an index, and vice-versa.
|
||||
The index must be initialized.
|
||||
|
||||
| byte one
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld a, 0
|
||||
| st a, many + x
|
||||
| }
|
||||
? UnmeaningfulReadError: x
|
||||
|
||||
Reading from a table, you must use an index.
|
||||
|
||||
| byte one
|
||||
|
|
||||
@ -320,7 +334,7 @@ Reading from a table, you must use an index, and vice-versa.
|
||||
| }
|
||||
? TypeMismatchError
|
||||
|
||||
| byte table many
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs many
|
||||
@ -333,7 +347,7 @@ Reading from a table, you must use an index, and vice-versa.
|
||||
| }
|
||||
? TypeMismatchError
|
||||
|
||||
| byte table many
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs many
|
||||
@ -346,10 +360,35 @@ Reading from a table, you must use an index, and vice-versa.
|
||||
| }
|
||||
= ok
|
||||
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld x, 0
|
||||
| ld a, many + x
|
||||
| }
|
||||
= ok
|
||||
|
||||
The index must be initialized.
|
||||
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld a, many + x
|
||||
| }
|
||||
? UnmeaningfulReadError: x
|
||||
|
||||
Copying to and from a word table.
|
||||
|
||||
| word one
|
||||
| word table many
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs one, many
|
||||
@ -363,7 +402,7 @@ Copying to and from a word table.
|
||||
= ok
|
||||
|
||||
| word one
|
||||
| word table many
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs one, many
|
||||
@ -376,7 +415,7 @@ Copying to and from a word table.
|
||||
? TypeMismatchError
|
||||
|
||||
| word one
|
||||
| word table many
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs one, many
|
||||
@ -390,7 +429,7 @@ Copying to and from a word table.
|
||||
|
||||
You can also copy a literal word to a word table.
|
||||
|
||||
| word table many
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs many
|
||||
@ -1554,10 +1593,11 @@ Read through a pointer.
|
||||
Routines are constants. You need not, and in fact cannot, specify a constant
|
||||
as an input to, an output of, or as a trashed value of a routine.
|
||||
|
||||
| vector vec
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -1576,10 +1616,11 @@ as an input to, an output of, or as a trashed value of a routine.
|
||||
| }
|
||||
? ConstantConstraintError: foo in main
|
||||
|
||||
| vector vec
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -1597,10 +1638,11 @@ as an input to, an output of, or as a trashed value of a routine.
|
||||
| }
|
||||
? ConstantConstraintError: foo in main
|
||||
|
||||
| vector vec
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -1621,10 +1663,11 @@ as an input to, an output of, or as a trashed value of a routine.
|
||||
You can copy the address of a routine into a vector, if that vector is
|
||||
declared appropriately.
|
||||
|
||||
| vector vec
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -1644,10 +1687,11 @@ declared appropriately.
|
||||
|
||||
But not if the vector is declared inappropriately.
|
||||
|
||||
| vector vec
|
||||
| vector routine
|
||||
| inputs y
|
||||
| outputs y
|
||||
| trashes z, n
|
||||
| vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -1668,10 +1712,11 @@ But not if the vector is declared inappropriately.
|
||||
"Appropriately" means, if the routine affects no more than what is named
|
||||
in the input/output sets of the vector.
|
||||
|
||||
| vector vec
|
||||
| vector routine
|
||||
| inputs a, x
|
||||
| outputs x
|
||||
| trashes a, z, n
|
||||
| vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -1691,10 +1736,11 @@ in the input/output sets of the vector.
|
||||
|
||||
Routines are read-only.
|
||||
|
||||
| vector vec
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -1714,7 +1760,9 @@ Routines are read-only.
|
||||
|
||||
Indirect call.
|
||||
|
||||
| vector foo outputs x trashes z, n
|
||||
| vector routine
|
||||
| outputs x trashes z, n
|
||||
| foo
|
||||
|
|
||||
| routine bar outputs x trashes z, n {
|
||||
| ld x, 200
|
||||
@ -1728,7 +1776,7 @@ Indirect call.
|
||||
|
||||
Calling the vector does indeed trash the things the vector says it does.
|
||||
|
||||
| vector foo trashes x, z, n
|
||||
| vector routine trashes x, z, n foo
|
||||
|
|
||||
| routine bar trashes x, z, n {
|
||||
| ld x, 200
|
||||
@ -1828,7 +1876,7 @@ Can `goto` a routine that outputs or trashes less than the current routine.
|
||||
|
||||
Indirect goto.
|
||||
|
||||
| vector foo outputs x trashes a, z, n
|
||||
| vector routine outputs x trashes a, z, n foo
|
||||
|
|
||||
| routine bar outputs x trashes a, z, n {
|
||||
| ld x, 200
|
||||
@ -1843,8 +1891,9 @@ Indirect goto.
|
||||
Jumping through the vector does indeed trash, or output, the things the
|
||||
vector says it does.
|
||||
|
||||
| vector foo
|
||||
| vector routine
|
||||
| trashes a, x, z, n
|
||||
| foo
|
||||
|
|
||||
| routine bar
|
||||
| trashes a, x, z, n {
|
||||
@ -1866,9 +1915,9 @@ vector says it does.
|
||||
| }
|
||||
? UnmeaningfulReadError: x in main
|
||||
|
||||
| vector foo
|
||||
| vector routine
|
||||
| outputs x
|
||||
| trashes a, z, n
|
||||
| trashes a, z, n foo
|
||||
|
|
||||
| routine bar
|
||||
| outputs x
|
||||
@ -1891,3 +1940,152 @@ vector says it does.
|
||||
| ld a, x
|
||||
| }
|
||||
= ok
|
||||
|
||||
### vector tables ###
|
||||
|
||||
A vector can be copied into a vector table.
|
||||
|
||||
| vector routine
|
||||
| outputs x
|
||||
| trashes a, z, n
|
||||
| one
|
||||
| vector (routine
|
||||
| outputs x
|
||||
| trashes a, z, n)
|
||||
| table[256] many
|
||||
|
|
||||
| routine bar outputs x trashes a, z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs one, many
|
||||
| outputs one, many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld x, 0
|
||||
| copy bar, one
|
||||
| copy one, many + x
|
||||
| }
|
||||
= ok
|
||||
|
||||
A vector can be copied out of a vector table.
|
||||
|
||||
| vector routine
|
||||
| outputs x
|
||||
| trashes a, z, n
|
||||
| one
|
||||
| vector (routine
|
||||
| outputs x
|
||||
| trashes a, z, n)
|
||||
| table[256] many
|
||||
|
|
||||
| routine bar outputs x trashes a, z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs one, many
|
||||
| outputs one, many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld x, 0
|
||||
| copy many + x, one
|
||||
| call one
|
||||
| }
|
||||
= ok
|
||||
|
||||
A routine can be copied into a vector table.
|
||||
|
||||
| vector (routine
|
||||
| outputs x
|
||||
| trashes a, z, n)
|
||||
| table[256] many
|
||||
|
|
||||
| routine bar outputs x trashes a, z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld x, 0
|
||||
| copy bar, many + x
|
||||
| }
|
||||
= ok
|
||||
|
||||
A vector in a vector table cannot be directly called.
|
||||
|
||||
| vector (routine
|
||||
| outputs x
|
||||
| trashes a, z, n)
|
||||
| table[256] many
|
||||
|
|
||||
| routine bar outputs x trashes a, z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld x, 0
|
||||
| copy bar, many + x
|
||||
| call many + x
|
||||
| }
|
||||
? ValueError
|
||||
|
||||
### typedef ###
|
||||
|
||||
A typedef is a more-readable alias for a type. "Alias" means
|
||||
that types have structural equivalence, not name equivalence.
|
||||
|
||||
| typedef routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| routine_type
|
||||
|
|
||||
| vector routine_type vec
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| {
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| outputs vec
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| copy foo, vec
|
||||
| }
|
||||
= ok
|
||||
|
||||
The new style routine definitions support typedefs.
|
||||
|
||||
| typedef routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| routine_type
|
||||
|
|
||||
| vector routine_type vec
|
||||
|
|
||||
| define foo routine_type
|
||||
| {
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| outputs vec
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| copy foo, vec
|
||||
| }
|
||||
= ok
|
||||
|
@ -7,7 +7,7 @@ SixtyPical to 6502 machine code.
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Compile SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --basic-prelude --compile %(test-body-file) | tests/appliances/bin/dcc6502-adapter"
|
||||
-> shell command "bin/sixtypical --basic-prelude --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
|
||||
|
||||
-> Tests for functionality "Compile SixtyPical program"
|
||||
|
||||
@ -137,9 +137,9 @@ Word memory locations with explicit address, initial value.
|
||||
= $081A .byte $BB
|
||||
= $081B .byte $0B
|
||||
|
||||
Initialized byte table.
|
||||
Initialized byte table. Bytes allocated, but beyond the string, are 0's.
|
||||
|
||||
| byte table message : "WHAT?"
|
||||
| byte table[8] message : "WHAT?"
|
||||
|
|
||||
| routine main
|
||||
| inputs message
|
||||
@ -158,254 +158,6 @@ Initialized byte table.
|
||||
= $0818 BRK
|
||||
= $0819 BRK
|
||||
= $081A BRK
|
||||
= $081B BRK
|
||||
= $081C BRK
|
||||
= $081D BRK
|
||||
= $081E BRK
|
||||
= $081F BRK
|
||||
= $0820 BRK
|
||||
= $0821 BRK
|
||||
= $0822 BRK
|
||||
= $0823 BRK
|
||||
= $0824 BRK
|
||||
= $0825 BRK
|
||||
= $0826 BRK
|
||||
= $0827 BRK
|
||||
= $0828 BRK
|
||||
= $0829 BRK
|
||||
= $082A BRK
|
||||
= $082B BRK
|
||||
= $082C BRK
|
||||
= $082D BRK
|
||||
= $082E BRK
|
||||
= $082F BRK
|
||||
= $0830 BRK
|
||||
= $0831 BRK
|
||||
= $0832 BRK
|
||||
= $0833 BRK
|
||||
= $0834 BRK
|
||||
= $0835 BRK
|
||||
= $0836 BRK
|
||||
= $0837 BRK
|
||||
= $0838 BRK
|
||||
= $0839 BRK
|
||||
= $083A BRK
|
||||
= $083B BRK
|
||||
= $083C BRK
|
||||
= $083D BRK
|
||||
= $083E BRK
|
||||
= $083F BRK
|
||||
= $0840 BRK
|
||||
= $0841 BRK
|
||||
= $0842 BRK
|
||||
= $0843 BRK
|
||||
= $0844 BRK
|
||||
= $0845 BRK
|
||||
= $0846 BRK
|
||||
= $0847 BRK
|
||||
= $0848 BRK
|
||||
= $0849 BRK
|
||||
= $084A BRK
|
||||
= $084B BRK
|
||||
= $084C BRK
|
||||
= $084D BRK
|
||||
= $084E BRK
|
||||
= $084F BRK
|
||||
= $0850 BRK
|
||||
= $0851 BRK
|
||||
= $0852 BRK
|
||||
= $0853 BRK
|
||||
= $0854 BRK
|
||||
= $0855 BRK
|
||||
= $0856 BRK
|
||||
= $0857 BRK
|
||||
= $0858 BRK
|
||||
= $0859 BRK
|
||||
= $085A BRK
|
||||
= $085B BRK
|
||||
= $085C BRK
|
||||
= $085D BRK
|
||||
= $085E BRK
|
||||
= $085F BRK
|
||||
= $0860 BRK
|
||||
= $0861 BRK
|
||||
= $0862 BRK
|
||||
= $0863 BRK
|
||||
= $0864 BRK
|
||||
= $0865 BRK
|
||||
= $0866 BRK
|
||||
= $0867 BRK
|
||||
= $0868 BRK
|
||||
= $0869 BRK
|
||||
= $086A BRK
|
||||
= $086B BRK
|
||||
= $086C BRK
|
||||
= $086D BRK
|
||||
= $086E BRK
|
||||
= $086F BRK
|
||||
= $0870 BRK
|
||||
= $0871 BRK
|
||||
= $0872 BRK
|
||||
= $0873 BRK
|
||||
= $0874 BRK
|
||||
= $0875 BRK
|
||||
= $0876 BRK
|
||||
= $0877 BRK
|
||||
= $0878 BRK
|
||||
= $0879 BRK
|
||||
= $087A BRK
|
||||
= $087B BRK
|
||||
= $087C BRK
|
||||
= $087D BRK
|
||||
= $087E BRK
|
||||
= $087F BRK
|
||||
= $0880 BRK
|
||||
= $0881 BRK
|
||||
= $0882 BRK
|
||||
= $0883 BRK
|
||||
= $0884 BRK
|
||||
= $0885 BRK
|
||||
= $0886 BRK
|
||||
= $0887 BRK
|
||||
= $0888 BRK
|
||||
= $0889 BRK
|
||||
= $088A BRK
|
||||
= $088B BRK
|
||||
= $088C BRK
|
||||
= $088D BRK
|
||||
= $088E BRK
|
||||
= $088F BRK
|
||||
= $0890 BRK
|
||||
= $0891 BRK
|
||||
= $0892 BRK
|
||||
= $0893 BRK
|
||||
= $0894 BRK
|
||||
= $0895 BRK
|
||||
= $0896 BRK
|
||||
= $0897 BRK
|
||||
= $0898 BRK
|
||||
= $0899 BRK
|
||||
= $089A BRK
|
||||
= $089B BRK
|
||||
= $089C BRK
|
||||
= $089D BRK
|
||||
= $089E BRK
|
||||
= $089F BRK
|
||||
= $08A0 BRK
|
||||
= $08A1 BRK
|
||||
= $08A2 BRK
|
||||
= $08A3 BRK
|
||||
= $08A4 BRK
|
||||
= $08A5 BRK
|
||||
= $08A6 BRK
|
||||
= $08A7 BRK
|
||||
= $08A8 BRK
|
||||
= $08A9 BRK
|
||||
= $08AA BRK
|
||||
= $08AB BRK
|
||||
= $08AC BRK
|
||||
= $08AD BRK
|
||||
= $08AE BRK
|
||||
= $08AF BRK
|
||||
= $08B0 BRK
|
||||
= $08B1 BRK
|
||||
= $08B2 BRK
|
||||
= $08B3 BRK
|
||||
= $08B4 BRK
|
||||
= $08B5 BRK
|
||||
= $08B6 BRK
|
||||
= $08B7 BRK
|
||||
= $08B8 BRK
|
||||
= $08B9 BRK
|
||||
= $08BA BRK
|
||||
= $08BB BRK
|
||||
= $08BC BRK
|
||||
= $08BD BRK
|
||||
= $08BE BRK
|
||||
= $08BF BRK
|
||||
= $08C0 BRK
|
||||
= $08C1 BRK
|
||||
= $08C2 BRK
|
||||
= $08C3 BRK
|
||||
= $08C4 BRK
|
||||
= $08C5 BRK
|
||||
= $08C6 BRK
|
||||
= $08C7 BRK
|
||||
= $08C8 BRK
|
||||
= $08C9 BRK
|
||||
= $08CA BRK
|
||||
= $08CB BRK
|
||||
= $08CC BRK
|
||||
= $08CD BRK
|
||||
= $08CE BRK
|
||||
= $08CF BRK
|
||||
= $08D0 BRK
|
||||
= $08D1 BRK
|
||||
= $08D2 BRK
|
||||
= $08D3 BRK
|
||||
= $08D4 BRK
|
||||
= $08D5 BRK
|
||||
= $08D6 BRK
|
||||
= $08D7 BRK
|
||||
= $08D8 BRK
|
||||
= $08D9 BRK
|
||||
= $08DA BRK
|
||||
= $08DB BRK
|
||||
= $08DC BRK
|
||||
= $08DD BRK
|
||||
= $08DE BRK
|
||||
= $08DF BRK
|
||||
= $08E0 BRK
|
||||
= $08E1 BRK
|
||||
= $08E2 BRK
|
||||
= $08E3 BRK
|
||||
= $08E4 BRK
|
||||
= $08E5 BRK
|
||||
= $08E6 BRK
|
||||
= $08E7 BRK
|
||||
= $08E8 BRK
|
||||
= $08E9 BRK
|
||||
= $08EA BRK
|
||||
= $08EB BRK
|
||||
= $08EC BRK
|
||||
= $08ED BRK
|
||||
= $08EE BRK
|
||||
= $08EF BRK
|
||||
= $08F0 BRK
|
||||
= $08F1 BRK
|
||||
= $08F2 BRK
|
||||
= $08F3 BRK
|
||||
= $08F4 BRK
|
||||
= $08F5 BRK
|
||||
= $08F6 BRK
|
||||
= $08F7 BRK
|
||||
= $08F8 BRK
|
||||
= $08F9 BRK
|
||||
= $08FA BRK
|
||||
= $08FB BRK
|
||||
= $08FC BRK
|
||||
= $08FD BRK
|
||||
= $08FE BRK
|
||||
= $08FF BRK
|
||||
= $0900 BRK
|
||||
= $0901 BRK
|
||||
= $0902 BRK
|
||||
= $0903 BRK
|
||||
= $0904 BRK
|
||||
= $0905 BRK
|
||||
= $0906 BRK
|
||||
= $0907 BRK
|
||||
= $0908 BRK
|
||||
= $0909 BRK
|
||||
= $090A BRK
|
||||
= $090B BRK
|
||||
= $090C BRK
|
||||
= $090D BRK
|
||||
= $090E BRK
|
||||
= $090F BRK
|
||||
= $0910 BRK
|
||||
= $0911 BRK
|
||||
= $0912 BRK
|
||||
|
||||
Some instructions.
|
||||
|
||||
@ -525,14 +277,16 @@ Compiling `if` without `else`.
|
||||
| trashes a, x, y, z, n, c, v
|
||||
| {
|
||||
| ld a, 0
|
||||
| ld y, 0
|
||||
| if z {
|
||||
| ld y, 1
|
||||
| }
|
||||
| }
|
||||
= $080D LDA #$00
|
||||
= $080F BNE $0813
|
||||
= $0811 LDY #$01
|
||||
= $0813 RTS
|
||||
= $080F LDY #$00
|
||||
= $0811 BNE $0815
|
||||
= $0813 LDY #$01
|
||||
= $0815 RTS
|
||||
|
||||
Compiling `repeat`.
|
||||
|
||||
@ -600,7 +354,7 @@ The body of `repeat forever` can be empty.
|
||||
Indexed access.
|
||||
|
||||
| byte one
|
||||
| byte table many
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| outputs many
|
||||
@ -619,8 +373,8 @@ Indexed access.
|
||||
|
||||
Byte tables take up 256 bytes in memory.
|
||||
|
||||
| byte table tab1
|
||||
| byte table tab2
|
||||
| byte table[256] tab1
|
||||
| byte table[256] tab2
|
||||
|
|
||||
| routine main
|
||||
| inputs tab1
|
||||
@ -706,7 +460,7 @@ Copy literal word to word.
|
||||
|
||||
You can also copy a literal word to a word table.
|
||||
|
||||
| word table many
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs many
|
||||
@ -725,8 +479,8 @@ You can also copy a literal word to a word table.
|
||||
|
||||
Copy vector to vector.
|
||||
|
||||
| vector bar
|
||||
| vector baz
|
||||
| vector routine bar
|
||||
| vector routine baz
|
||||
|
|
||||
| routine main
|
||||
| inputs baz
|
||||
@ -743,7 +497,11 @@ Copy vector to vector.
|
||||
|
||||
Copy routine to vector, inside an `interrupts off` block.
|
||||
|
||||
| vector bar
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| bar
|
||||
|
|
||||
| routine foo
|
||||
| inputs x
|
||||
@ -754,7 +512,6 @@ Copy routine to vector, inside an `interrupts off` block.
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs foo
|
||||
| outputs bar
|
||||
| trashes a, n, z
|
||||
| {
|
||||
@ -775,7 +532,7 @@ Copy routine to vector, inside an `interrupts off` block.
|
||||
Copy word to word table and back, with both `x` and `y` as indexes.
|
||||
|
||||
| word one
|
||||
| word table many
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| inputs one, many
|
||||
@ -816,33 +573,50 @@ Copy word to word table and back, with both `x` and `y` as indexes.
|
||||
|
||||
Indirect call.
|
||||
|
||||
| vector foo outputs x trashes z, n
|
||||
| vector routine
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| foo
|
||||
|
|
||||
| routine bar outputs x trashes z, n {
|
||||
| routine bar
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main inputs bar outputs x, foo trashes a, z, n {
|
||||
| routine main
|
||||
| outputs x, foo
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| copy bar, foo
|
||||
| call foo
|
||||
| }
|
||||
= $080D LDA #$1B
|
||||
= $080F STA $0821
|
||||
= $080F STA $0822
|
||||
= $0812 LDA #$08
|
||||
= $0814 STA $0822
|
||||
= $0814 STA $0823
|
||||
= $0817 JSR $081E
|
||||
= $081A RTS
|
||||
= $081B LDX #$C8
|
||||
= $081D RTS
|
||||
= $081E JMP ($0821)
|
||||
= $081E JMP ($0822)
|
||||
= $0821 RTS
|
||||
|
||||
goto.
|
||||
|
||||
| routine bar outputs x trashes z, n {
|
||||
| routine bar
|
||||
| inputs y
|
||||
| outputs x, y
|
||||
| trashes z, n
|
||||
| {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main outputs x trashes a, z, n {
|
||||
| routine main
|
||||
| outputs x, y
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| ld y, 200
|
||||
| goto bar
|
||||
| }
|
||||
@ -852,6 +626,59 @@ goto.
|
||||
= $0813 LDX #$C8
|
||||
= $0815 RTS
|
||||
|
||||
### Vector tables
|
||||
|
||||
Copying to and from a vector table.
|
||||
|
||||
| vector routine
|
||||
| outputs x
|
||||
| trashes a, z, n
|
||||
| one
|
||||
| vector (routine
|
||||
| outputs x
|
||||
| trashes a, z, n)
|
||||
| table[256] many
|
||||
|
|
||||
| routine bar outputs x trashes a, z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs one, many
|
||||
| outputs one, many
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
| ld x, 0
|
||||
| copy bar, one
|
||||
| copy bar, many + x
|
||||
| copy one, many + x
|
||||
| copy many + x, one
|
||||
| call one
|
||||
| }
|
||||
= $080D LDX #$00
|
||||
= $080F LDA #$3F
|
||||
= $0811 STA $0846
|
||||
= $0814 LDA #$08
|
||||
= $0816 STA $0847
|
||||
= $0819 LDA #$3F
|
||||
= $081B STA $0848,X
|
||||
= $081E LDA #$08
|
||||
= $0820 STA $0948,X
|
||||
= $0823 LDA $0846
|
||||
= $0826 STA $0848,X
|
||||
= $0829 LDA $0847
|
||||
= $082C STA $0948,X
|
||||
= $082F LDA $0848,X
|
||||
= $0832 STA $0846
|
||||
= $0835 LDA $0948,X
|
||||
= $0838 STA $0847
|
||||
= $083B JSR $0842
|
||||
= $083E RTS
|
||||
= $083F LDX #$C8
|
||||
= $0841 RTS
|
||||
= $0842 JMP ($0846)
|
||||
= $0845 RTS
|
||||
|
||||
### word operations
|
||||
|
||||
Adding a constant word to a word memory location.
|
||||
@ -1042,39 +869,41 @@ Note that this is *not* range-checked. (Yet.)
|
||||
| routine main
|
||||
| inputs buf
|
||||
| outputs y, foo, delta
|
||||
| trashes a, z, n, ptr
|
||||
| trashes a, c, v, z, n, ptr
|
||||
| {
|
||||
| copy 619, delta
|
||||
| ld y, 0
|
||||
| st off, c
|
||||
| copy ^buf, ptr
|
||||
| add ptr, delta
|
||||
| add ptr, word 1
|
||||
| copy [ptr] + y, foo
|
||||
| }
|
||||
= $080D LDA #$6B
|
||||
= $080F STA $1042
|
||||
= $080F STA $1043
|
||||
= $0812 LDA #$02
|
||||
= $0814 STA $1043
|
||||
= $0814 STA $1044
|
||||
= $0817 LDY #$00
|
||||
= $0819 LDA #$41
|
||||
= $081B STA $FE
|
||||
= $081D LDA #$08
|
||||
= $081F STA $FF
|
||||
= $0821 LDA $FE
|
||||
= $0823 ADC $1042
|
||||
= $0826 STA $FE
|
||||
= $0828 LDA $FF
|
||||
= $082A ADC $1043
|
||||
= $082D STA $FF
|
||||
= $082F LDA $FE
|
||||
= $0831 ADC #$01
|
||||
= $0833 STA $FE
|
||||
= $0835 LDA $FF
|
||||
= $0837 ADC #$00
|
||||
= $0839 STA $FF
|
||||
= $083B LDA ($FE),Y
|
||||
= $083D STA $1041
|
||||
= $0840 RTS
|
||||
= $0819 CLC
|
||||
= $081A LDA #$42
|
||||
= $081C STA $FE
|
||||
= $081E LDA #$08
|
||||
= $0820 STA $FF
|
||||
= $0822 LDA $FE
|
||||
= $0824 ADC $1043
|
||||
= $0827 STA $FE
|
||||
= $0829 LDA $FF
|
||||
= $082B ADC $1044
|
||||
= $082E STA $FF
|
||||
= $0830 LDA $FE
|
||||
= $0832 ADC #$01
|
||||
= $0834 STA $FE
|
||||
= $0836 LDA $FF
|
||||
= $0838 ADC #$00
|
||||
= $083A STA $FF
|
||||
= $083C LDA ($FE),Y
|
||||
= $083E STA $1042
|
||||
= $0841 RTS
|
||||
|
||||
### Trash
|
||||
|
||||
|
@ -1,490 +0,0 @@
|
||||
SixtyPical Execution
|
||||
====================
|
||||
|
||||
This is a test suite, written in [Falderal][] format, for the dynamic
|
||||
execution behaviour of the Sixtypical language, disgregarding static analysis.
|
||||
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Execute SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --execute %(test-body-file)"
|
||||
|
||||
-> Tests for functionality "Execute SixtyPical program"
|
||||
|
||||
Rudimentary program.
|
||||
|
||||
| routine main {
|
||||
| ld a, 0
|
||||
| add a, 1
|
||||
| }
|
||||
= a: 1
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Program accesses a memory location.
|
||||
|
||||
| byte lives
|
||||
|
|
||||
| routine main {
|
||||
| ld a, 0
|
||||
| st a, lives
|
||||
| ld x, lives
|
||||
| add x, 1
|
||||
| st x, lives
|
||||
| }
|
||||
= a: 0
|
||||
= c: 0
|
||||
= lives: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 1
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Program accesses a memory location with initial value.
|
||||
|
||||
| byte lives : 3
|
||||
|
|
||||
| routine main {
|
||||
| ld a, lives
|
||||
| }
|
||||
= a: 3
|
||||
= c: 0
|
||||
= lives: 3
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Add honours carry.
|
||||
|
||||
| routine main {
|
||||
| ld a, 255
|
||||
| st on, c
|
||||
| add a, 0
|
||||
| }
|
||||
= a: 0
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 1
|
||||
|
||||
| routine main {
|
||||
| ld a, $ff
|
||||
| st off, c
|
||||
| add a, 1
|
||||
| }
|
||||
= a: 0
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 1
|
||||
|
||||
Subtract honours carry.
|
||||
|
||||
| routine main {
|
||||
| ld a, 0
|
||||
| st on, c
|
||||
| sub a, 0
|
||||
| }
|
||||
= a: 255
|
||||
= c: 1
|
||||
= n: 1
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
| routine main {
|
||||
| ld a, 0
|
||||
| st off, c
|
||||
| sub a, 1
|
||||
| }
|
||||
= a: 255
|
||||
= c: 1
|
||||
= n: 1
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Inc and dec do not honour carry, but do set n and z.
|
||||
|
||||
| routine main {
|
||||
| ld x, 254
|
||||
| st on, c
|
||||
| inc x
|
||||
| }
|
||||
= a: 0
|
||||
= c: 1
|
||||
= n: 1
|
||||
= v: 0
|
||||
= x: 255
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
| routine main {
|
||||
| ld y, 1
|
||||
| st on, c
|
||||
| dec y
|
||||
| }
|
||||
= a: 0
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 1
|
||||
|
||||
Compare affects, but does not use, carry.
|
||||
|
||||
| routine main {
|
||||
| ld a, 1
|
||||
| st on, c
|
||||
| cmp a, 1
|
||||
| }
|
||||
= a: 1
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 1
|
||||
|
||||
| routine main {
|
||||
| ld a, 1
|
||||
| st off, c
|
||||
| cmp a, 5
|
||||
| }
|
||||
= a: 1
|
||||
= c: 1
|
||||
= n: 1
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
AND.
|
||||
|
||||
| routine main {
|
||||
| ld a, 15
|
||||
| and a, 18
|
||||
| }
|
||||
= a: 2
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
OR.
|
||||
|
||||
| routine main {
|
||||
| ld a, 34
|
||||
| or a, 18
|
||||
| }
|
||||
= a: 50
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
XOR.
|
||||
|
||||
| routine main {
|
||||
| ld a, 34
|
||||
| xor a, 18
|
||||
| }
|
||||
= a: 48
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Shift left.
|
||||
|
||||
| routine main {
|
||||
| ld a, 129
|
||||
| st off, c
|
||||
| shl a
|
||||
| }
|
||||
= a: 2
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
| routine main {
|
||||
| ld a, 0
|
||||
| st on, c
|
||||
| shl a
|
||||
| }
|
||||
= a: 1
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Shift right.
|
||||
|
||||
| routine main {
|
||||
| ld a, 129
|
||||
| st off, c
|
||||
| shr a
|
||||
| }
|
||||
= a: 64
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
| routine main {
|
||||
| ld a, 0
|
||||
| st on, c
|
||||
| shr a
|
||||
| }
|
||||
= a: 128
|
||||
= c: 0
|
||||
= n: 1
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Call routine.
|
||||
|
||||
| routine up {
|
||||
| inc x
|
||||
| inc y
|
||||
| }
|
||||
| routine main {
|
||||
| ld x, 0
|
||||
| ld y, 1
|
||||
| call up
|
||||
| call up
|
||||
| }
|
||||
= a: 0
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 2
|
||||
= y: 3
|
||||
= z: 0
|
||||
|
||||
If.
|
||||
|
||||
| routine main {
|
||||
| ld x, 40
|
||||
| cmp x, 40
|
||||
| if z {
|
||||
| ld a, 1
|
||||
| } else {
|
||||
| ld a, 8
|
||||
| }
|
||||
| ld x, 2
|
||||
| }
|
||||
= a: 1
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 2
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
| routine main {
|
||||
| ld x, 39
|
||||
| cmp x, 40
|
||||
| if z {
|
||||
| ld a, 1
|
||||
| } else {
|
||||
| ld a, 8
|
||||
| }
|
||||
| ld x, 2
|
||||
| }
|
||||
= a: 8
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 2
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
If without else.
|
||||
|
||||
| routine main {
|
||||
| ld x, 39
|
||||
| cmp x, 40
|
||||
| if z {
|
||||
| ld a, 1
|
||||
| }
|
||||
| ld x, 2
|
||||
| }
|
||||
= a: 0
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 2
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
`not` inverts the sense of the test.
|
||||
|
||||
| routine main {
|
||||
| ld x, 40
|
||||
| cmp x, 40
|
||||
| if not z {
|
||||
| ld a, 1
|
||||
| } else {
|
||||
| ld a, 8
|
||||
| }
|
||||
| ld x, 2
|
||||
| }
|
||||
= a: 8
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 2
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
| routine main {
|
||||
| ld x, 39
|
||||
| cmp x, 40
|
||||
| if not z {
|
||||
| ld a, 1
|
||||
| }
|
||||
| ld x, 2
|
||||
| }
|
||||
= a: 1
|
||||
= c: 1
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 2
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Repeat loop.
|
||||
|
||||
| routine main {
|
||||
| ld x, 0
|
||||
| ld y, 15
|
||||
| repeat {
|
||||
| inc x
|
||||
| inc y
|
||||
| cmp x, 10
|
||||
| } until z
|
||||
| }
|
||||
= a: 0
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 10
|
||||
= y: 25
|
||||
= z: 1
|
||||
|
||||
Copy instruction. Note that the state of a, z, and n are not defined
|
||||
after copy executes.
|
||||
|
||||
| routine main {
|
||||
| ld x, 5
|
||||
| copy x, y
|
||||
| }
|
||||
= a: 0
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 5
|
||||
= y: 5
|
||||
= z: 0
|
||||
|
||||
Copy word to word.
|
||||
|
||||
| word foo : 2000
|
||||
| word bar
|
||||
|
|
||||
| routine main {
|
||||
| copy foo, bar
|
||||
| }
|
||||
= a: 0
|
||||
= bar: 2000
|
||||
= c: 0
|
||||
= foo: 2000
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Copy literal word to word.
|
||||
|
||||
| word bar
|
||||
|
|
||||
| routine main {
|
||||
| copy word 2000, bar
|
||||
| }
|
||||
= a: 0
|
||||
= bar: 2000
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 0
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Indirect call.
|
||||
|
||||
| vector foo outputs x trashes z, n
|
||||
|
|
||||
| routine bar outputs x trashes z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main inputs bar outputs x, foo trashes a, z, n {
|
||||
| copy bar, foo
|
||||
| call foo
|
||||
| }
|
||||
= a: 0
|
||||
= c: 0
|
||||
= n: 1
|
||||
= v: 0
|
||||
= x: 200
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
goto.
|
||||
|
||||
| routine bar outputs x trashes z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main outputs x trashes a, z, n {
|
||||
| ld y, 200
|
||||
| goto bar
|
||||
| }
|
||||
= a: 0
|
||||
= c: 0
|
||||
= n: 1
|
||||
= v: 0
|
||||
= x: 200
|
||||
= y: 200
|
||||
= z: 0
|
@ -10,7 +10,7 @@ but not necessarily sensible programs.
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Check syntax of SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical %(test-body-file) && echo ok"
|
||||
-> shell command "bin/sixtypical --parse-only --traceback %(test-body-file) && echo ok"
|
||||
|
||||
-> Tests for functionality "Check syntax of SixtyPical program"
|
||||
|
||||
@ -135,9 +135,7 @@ User-defined memory addresses of different types.
|
||||
|
||||
| byte byt
|
||||
| word wor
|
||||
| vector vec
|
||||
| byte table tab
|
||||
| word table wtab
|
||||
| vector routine trashes a vec
|
||||
| buffer[2048] buf
|
||||
| pointer ptr
|
||||
|
|
||||
@ -145,6 +143,36 @@ User-defined memory addresses of different types.
|
||||
| }
|
||||
= ok
|
||||
|
||||
Tables of different types.
|
||||
|
||||
| byte table[256] tab
|
||||
| word table[256] wtab
|
||||
| vector (routine trashes a) table[256] vtab
|
||||
|
|
||||
| routine main {
|
||||
| }
|
||||
= ok
|
||||
|
||||
Typedefs of different types.
|
||||
|
||||
| typedef byte octet
|
||||
| typedef octet table[256] twokay
|
||||
| typedef routine trashes a game_routine
|
||||
| vector game_routine start_game
|
||||
|
|
||||
| routine main {
|
||||
| }
|
||||
= ok
|
||||
|
||||
Can't have two typedefs with the same name.
|
||||
|
||||
| typedef byte frank
|
||||
| typedef word frank
|
||||
|
|
||||
| routine main {
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
Explicit memory address.
|
||||
|
||||
| byte screen @ 1024
|
||||
@ -177,7 +205,7 @@ Cannot have both initial value and explicit address.
|
||||
|
||||
User-defined locations of other types.
|
||||
|
||||
| byte table screen @ 1024
|
||||
| byte table[256] screen @ 1024
|
||||
| word r1
|
||||
| word r2 @ 60000
|
||||
| word r3 : 2000
|
||||
@ -188,7 +216,7 @@ User-defined locations of other types.
|
||||
|
||||
Initialized byte table.
|
||||
|
||||
| byte table message : "WHAT DO YOU WANT TO DO NEXT?"
|
||||
| byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?"
|
||||
|
|
||||
| routine main {
|
||||
| }
|
||||
@ -290,7 +318,7 @@ Can't define two routines with the same name.
|
||||
|
||||
Declaring byte and word table memory location.
|
||||
|
||||
| byte table tab
|
||||
| byte table[256] tab
|
||||
|
|
||||
| routine main {
|
||||
| ld x, 0
|
||||
@ -301,7 +329,7 @@ Declaring byte and word table memory location.
|
||||
= ok
|
||||
|
||||
| word one
|
||||
| word table many
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main {
|
||||
| ld x, 0
|
||||
@ -313,11 +341,11 @@ Declaring byte and word table memory location.
|
||||
|
||||
Declaring and calling a vector.
|
||||
|
||||
| vector cinv
|
||||
| vector routine
|
||||
| inputs a
|
||||
| outputs x
|
||||
| trashes a, x, z, n
|
||||
| @ 788
|
||||
| cinv @ 788
|
||||
|
|
||||
| routine foo {
|
||||
| ld a, 0
|
||||
@ -344,11 +372,11 @@ Only vectors can be decorated with constraints like that.
|
||||
|
||||
Constraints set may only contain labels.
|
||||
|
||||
| vector cinv
|
||||
| vector routine
|
||||
| inputs a
|
||||
| outputs 200
|
||||
| trashes a, x, z, n
|
||||
| @ 788
|
||||
| cinv @ 788
|
||||
|
|
||||
| routine foo {
|
||||
| ld a, 0
|
||||
@ -363,11 +391,11 @@ Constraints set may only contain labels.
|
||||
|
||||
A vector can name itself in its inputs, outputs, and trashes.
|
||||
|
||||
| vector cinv
|
||||
| vector routine
|
||||
| inputs cinv, a
|
||||
| outputs cinv, x
|
||||
| trashes a, x, z, n
|
||||
| @ 788
|
||||
| cinv @ 788
|
||||
|
|
||||
| routine foo {
|
||||
| ld a, 0
|
||||
@ -383,7 +411,11 @@ A vector can name itself in its inputs, outputs, and trashes.
|
||||
A routine can be copied into a vector before the routine appears in the program,
|
||||
*however*, it must be marked as such with the keyword `forward`.
|
||||
|
||||
| vector cinv inputs cinv, a outputs cinv, x trashes a, x, z, n @ 788
|
||||
| vector routine
|
||||
| inputs cinv, a
|
||||
| outputs cinv, x
|
||||
| trashes a, x, z, n
|
||||
| cinv @ 788
|
||||
| routine main {
|
||||
| with interrupts off {
|
||||
| copy foo, cinv
|
||||
@ -395,7 +427,11 @@ A routine can be copied into a vector before the routine appears in the program,
|
||||
| }
|
||||
? SyntaxError: Undefined symbol
|
||||
|
||||
| vector cinv inputs cinv, a outputs cinv, x trashes a, x, z, n @ 788
|
||||
| vector routine
|
||||
| inputs cinv, a
|
||||
| outputs cinv, x
|
||||
| trashes a, x, z, n
|
||||
| cinv @ 788
|
||||
| routine main {
|
||||
| with interrupts off {
|
||||
| copy forward foo, cinv
|
||||
@ -425,7 +461,7 @@ goto.
|
||||
| }
|
||||
= ok
|
||||
|
||||
| vector foo
|
||||
| vector routine foo
|
||||
|
|
||||
| routine main {
|
||||
| goto foo
|
||||
@ -456,3 +492,40 @@ Buffers and pointers.
|
||||
| copy [ptr] + y, foo
|
||||
| }
|
||||
= ok
|
||||
|
||||
Routines can be defined in a new style.
|
||||
|
||||
| typedef routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| routine_type
|
||||
|
|
||||
| vector routine_type vec
|
||||
|
|
||||
| define foo routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| {
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| outputs vec
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| copy foo, vec
|
||||
| }
|
||||
= ok
|
||||
|
||||
Only routines can be defined in the new style.
|
||||
|
||||
| define foo byte table[256]
|
||||
|
|
||||
| routine main
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| ld a, 0
|
||||
| }
|
||||
? SyntaxError
|
||||
|
Loading…
x
Reference in New Issue
Block a user