1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-12-01 16:50:09 +00:00

Merge pull request #14 from catseye/develop-0.17

Develop 0.17
This commit is contained in:
Chris Pressey 2018-11-16 11:48:24 +00:00 committed by GitHub
commit 3cd28bdb3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 1207 additions and 938 deletions

View File

@ -1,6 +1,23 @@
History of SixtyPical History of SixtyPical
===================== =====================
0.17
----
* `save X, Y, Z { }` now allowed as a shortcut for nested `save`s.
* If the name in a location expression isn't found in the symbol
table, a forward reference will _always_ be generated; and forward
references in _all_ operations will be resolved after parsing.
* As a consequence, trying to call or goto a non-routine-typed symbol
is now an analysis error, not a syntax error.
* Deprecated `routine foo ...` syntax has been removed.
* Split TODO off into own file.
* `sixtypical` no longer writes the compiled binary to standard
output. The `--output` command-line argument should be given
to get a compiled binary; otherwise only analysis is run.
* Internal cleanups, including a hierarchy of `Outputters`.
* All tests pass when `sixtypical` is run under Python 3.5.2.
0.16 0.16
---- ----

160
README.md
View File

@ -1,32 +1,38 @@
SixtyPical SixtyPical
========== ==========
_Version 0.16. Work-in-progress, everything is subject to change._ _Version 0.17. Work-in-progress, everything is subject to change._
**SixtyPical** is a 6502-like programming language with advanced **SixtyPical** is a low-level programming language with advanced
static analysis. static analysis. Many of its primitive instructions resemble
those of the 6502 CPU — in fact it is intended to be compiled to
6502 machine code — but along with these instructions are
constructs which ease structuring and analyzing the code. The
language aims to fill this niche:
"6502-like" means that it has similar restrictions as programming * You'd use assembly, but you don't want to spend hours
in 6502 assembly (e.g. the programmer must choose the registers that debugging (say) a memory overrun that happened because of a
values will be stored in) and is concomitantly easy for a compiler to ridiculous silly error.
translate it to 6502 machine language code. * You'd use C or some other "high-level" language, but you don't
want the extra overhead added by the compiler to manage the
stack and registers.
"Advanced static analysis" includes _abstract interpretation_, where we SixtyPical gives the programmer a coding regimen on par with assembly
go through the program step by step, tracking not just the changes that language in terms of size and hands-on-ness, but also able to catch
happen during a _specific_ execution of the program, but _sets_ of changes many ridiculous silly errors at compile time, such as
that could _possibly_ happen in any run of the program. This lets us
determine that certain things can never happen, which we can then formulate
as safety checks.
In practice, this means it catches things like
* you forgot to clear carry before adding something to the accumulator * you forgot to clear carry before adding something to the accumulator
* a subroutine that you call trashes a register you thought was preserved * a subroutine that you called trashes a register you thought it preserved
* you tried to read or write a byte beyond the end of a byte array * you tried to read or write a byte beyond the end of a byte array
* you tried to write the address of something that was not a routine, to * you tried to write the address of something that was not a routine, to
a jump vector a jump vector
and suchlike. It also provides some convenient operations based on Many of these checks are done with _abstract interpretation_, where we
go through the program step by step, tracking not just the changes that
happen during a _specific_ execution of the program, but _sets_ of changes
that could _possibly_ happen in any run of the program.
SixtyPical also provides some convenient operations based on
machine-language programming idioms, such as machine-language programming idioms, such as
* copying values from one register to another (via a third register when * copying values from one register to another (via a third register when
@ -35,8 +41,15 @@ machine-language programming idioms, such as
* explicit tail calls * explicit tail calls
* indirect subroutine calls * indirect subroutine calls
The reference implementation can analyze and compile SixtyPical programs to SixtyPical is defined by a specification document, a set of test cases,
6502 machine code. and a reference implementation written in Python 2. The reference
implementation can analyze and compile SixtyPical programs to 6502 machine
code, which can be run on several 6502-based 8-bit architectures:
* Commodore 64
* Commodore VIC-20
* Atari 2600 VCS
* Apple II
Quick Start Quick Start
----------- -----------
@ -68,111 +81,4 @@ Documentation
* [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md) * [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md)
* [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md) * [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md)
* [Output formats supported by `sixtypical`](doc/Output%20Formats.md) * [Output formats supported by `sixtypical`](doc/Output%20Formats.md)
* [TODO](TODO.md)
TODO
----
### `low` and `high` address operators
To turn `word` type into `byte`.
Trying to remember if we have a compelling case for this or now. The best I can think
of is for implementing 16-bit `cmp` in an efficient way. Maybe we should see if we
can get by with 16-bit `cmp` instead though.
The problem is that once a byte is extracted, putting it back into a word is awkward.
The address operators have to modify a destination in a special way. That is, when
you say `st a, >word`, you are updating `word` to be `word & $ff | a << 8`, somelike.
Is that consistent with `st`? Well, probably it is, but we have to explain it.
It might make more sense, then, for it to be "part of the operation" instead of "part of
the reference"; something like `st.hi x, word`; `st.lo y, word`. Dunno.
### Save multiple values in single block
As a shortcut for the idiom
save a { save var {
...
} }
allow
save a, var {
...
}
### Save values to other-than-the-stack
Allow
save a to temp_a {
...
}
Which uses some other storage location instead of the stack. A local static
would be a good candidate for such.
### Make all symbols forward-referencable
Basically, don't do symbol-table lookups when parsing, but do have a more formal
"symbol resolution" (linking) phase right after parsing.
### Associate each pointer with the buffer it points into
Check that the buffer being read or written to through pointer, appears in appropriate
inputs or outputs set.
In the analysis, when we obtain a pointer, we need to record, in contect, what buffer
that pointer came from.
When we write through that pointer, we need to set that buffer as written.
When we read through the pointer, we need to check that the buffer is readable.
### Table overlays
They are uninitialized, but the twist is, the address is a buffer that is
an input to and/or output of the routine. So, they are defined (insofar
as the buffer is defined.)
They are therefore a "view" of a section of a buffer.
This is slightly dangerous since it does permit aliases: the buffer and the
table refer to the same memory.
Although, if they are `static`, you could say, in the routine in which they
are `static`, as soon as you've established one, you can no longer use the
buffer; and the ones you establish must be disjoint.
(That seems to be the most compelling case for restricting them to `static`.)
An alternative would be `static` pointers, which are currently not possible because
pointers must be zero-page, thus `@`, thus uninitialized.
### Question "consistent initialization"
Question the value of the "consistent initialization" principle for `if` statement analysis.
Part of this is the trashes at the end; I think what it should be is that the trashes
after the `if` is the union of the trashes in each of the branches; this would obviate the
need to `trash` values explicitly, but if you tried to access them afterwards, it would still
error.
### Tail-call optimization
More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot
appear elsewhere.)
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can.
The constraints should iron out the same both ways.
And - once we have this - why do we need `goto` to be in tail position, strictly?
As long as the routine has consistent type context every place it exits, that should be fine.
### "Include" directives
Search a searchlist of include paths. And use them to make libraries of routines.
One such library routine might be an `interrupt routine` type for various architectures.
Since "the supervisor" has stored values on the stack, we should be able to trash them
with impunity, in such a routine.

89
TODO.md Normal file
View File

@ -0,0 +1,89 @@
TODO for SixtyPical
===================
### 16-bit `cmp`
This is because we don't actually want `low` and `high` address operators
that turn `word` type into `byte`.
This is because this immediately makes things harder (that is, effectively
impossible) to analyze.
16-bit `cmp` also benefits from some special differences between `cmp`
and `sub` on 6502, so it would be nice to capture them.
### Save values to other-than-the-stack
Allow
save a to temp_a {
...
}
Which uses some other storage location instead of the stack. A local static
would be a good candidate for such.
### Associate each pointer with the buffer it points into
Check that the buffer being read or written to through pointer, appears in appropriate
inputs or outputs set.
In the analysis, when we obtain a pointer, we need to record, in context, what buffer
that pointer came from.
When we write through that pointer, we need to set that buffer as written.
When we read through the pointer, we need to check that the buffer is readable.
### Table overlays
They are uninitialized, but the twist is, the address is a buffer that is
an input to and/or output of the routine. So, they are defined (insofar
as the buffer is defined.)
They are therefore a "view" of a section of a buffer.
This is slightly dangerous since it does permit aliases: the buffer and the
table refer to the same memory.
Although, if they are `static`, you could say, in the routine in which they
are `static`, as soon as you've established one, you can no longer use the
buffer; and the ones you establish must be disjoint.
(That seems to be the most compelling case for restricting them to `static`.)
An alternative would be `static` pointers, which are currently not possible because
pointers must be zero-page, thus `@`, thus uninitialized.
### Question "consistent initialization"
Question the value of the "consistent initialization" principle for `if` statement analysis.
Part of this is the trashes at the end; I think what it should be is that the trashes
after the `if` is the union of the trashes in each of the branches; this would obviate the
need to `trash` values explicitly, but if you tried to access them afterwards, it would still
error.
### Tail-call optimization
More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot
appear elsewhere.)
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,
if the block is in tail position. The constraints should iron out the same both ways.
And - once we have this - why do we need `goto` to be in tail position, strictly?
As long as the routine has consistent type context every place it exits, that should be fine.
### "Include" directives
Search a searchlist of include paths. And use them to make libraries of routines.
One such library routine might be an `interrupt routine` type for various architectures.
Since "the supervisor" has stored values on the stack, we should be able to trash them
with impunity, in such a routine.
### Line numbers in analysis error messages
For analysis errors, there is a line number, but it's the line of the routine
after the routine in which the analysis error occurred. Fix this.

View File

@ -18,25 +18,12 @@ from pprint import pprint
import sys import sys
import traceback import traceback
from sixtypical.parser import Parser, ParsingContext from sixtypical.parser import Parser, ParsingContext, merge_programs
from sixtypical.analyzer import Analyzer from sixtypical.analyzer import Analyzer
from sixtypical.emitter import Emitter, Byte, Word from sixtypical.outputter import outputter_class_for
from sixtypical.compiler import Compiler from sixtypical.compiler import Compiler
def merge_programs(programs):
"""Assumes that the programs do not have any conflicts."""
from sixtypical.ast import Program
full = Program(1, defns=[], routines=[])
for p in programs:
full.defns.extend(p.defns)
full.routines.extend(p.routines)
return full
def process_input_files(filenames, options): def process_input_files(filenames, options):
context = ParsingContext() context = ParsingContext()
@ -68,7 +55,7 @@ def process_input_files(filenames, options):
return return
if label: if label:
sys.stdout.write("*** {}:\n".format(label)) sys.stdout.write("*** {}:\n".format(label))
sys.stdout.write(json.dumps(data, indent=4, sort_keys=True)) sys.stdout.write(json.dumps(data, indent=4, sort_keys=True, separators=(',', ':')))
sys.stdout.write("\n") sys.stdout.write("\n")
fa = FallthruAnalyzer(debug=options.debug) fa = FallthruAnalyzer(debug=options.debug)
@ -76,60 +63,26 @@ def process_input_files(filenames, options):
compilation_roster = fa.serialize() compilation_roster = fa.serialize()
dump(compilation_roster) dump(compilation_roster)
if options.analyze_only: if options.analyze_only or options.output is None:
return return
fh = sys.stdout start_addr = None
if options.output_format == 'raw':
start_addr = 0x0000
prelude = []
elif options.output_format == 'prg':
start_addr = 0xc000
prelude = []
elif options.output_format == 'c64-basic-prg':
start_addr = 0x0801
prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
elif options.output_format == 'vic20-basic-prg':
start_addr = 0x1001
prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
elif options.output_format == 'atari2600-cart':
start_addr = 0xf000
prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9,
0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb]
else:
raise NotImplementedError("Unknown output format: {}".format(options.output_format))
if options.origin is not None: if options.origin is not None:
if options.origin.startswith('0x'): if options.origin.startswith('0x'):
start_addr = int(options.origin, 16) start_addr = int(options.origin, 16)
else: else:
start_addr = int(options.origin, 10) start_addr = int(options.origin, 10)
# If we are outputting a .PRG, we output the load address first. with open(options.output, 'wb') as fh:
# We don't use the Emitter for this b/c not part of addr space. outputter = outputter_class_for(options.output_format)(fh, start_addr=start_addr)
if options.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'): outputter.write_prelude()
fh.write(Word(start_addr).serialize(0)) compiler = Compiler(outputter.emitter)
compiler.compile_program(program, compilation_roster=compilation_roster)
emitter = Emitter(start_addr) outputter.write_postlude()
for byte in prelude: if options.debug:
emitter.emit(Byte(byte)) pprint(outputter.emitter)
compiler = Compiler(emitter) else:
compiler.compile_program(program, compilation_roster=compilation_roster) outputter.emitter.serialize_to(fh)
# If we are outputting a cartridge with boot and BRK address
# at the end, pad to ROM size minus 4 bytes, and emit addresses.
if options.output_format == 'atari2600-cart':
emitter.pad_to_size(4096 - 4)
emitter.emit(Word(start_addr))
emitter.emit(Word(start_addr))
if options.debug:
pprint(emitter.accum)
else:
emitter.serialize(fh)
if __name__ == '__main__': if __name__ == '__main__':
@ -140,6 +93,10 @@ if __name__ == '__main__':
help="The SixtyPical source files to compile." help="The SixtyPical source files to compile."
) )
argparser.add_argument(
"--output", "-o", type=str, metavar='FILENAME',
help="File to which generated 6502 code will be written."
)
argparser.add_argument( argparser.add_argument(
"--origin", type=str, default=None, "--origin", type=str, default=None,
help="Location in memory where the `main` routine will be " help="Location in memory where the `main` routine will be "

View File

@ -8,8 +8,7 @@ Output Formats
The file contains only the emitted bytes of the compiled SixtyPical The file contains only the emitted bytes of the compiled SixtyPical
program. program.
The default origin is $0000; it is not unlikely you will want to The default origin is $0000; you will likely want to override this.
override this.
Note that the origin is not stored in the output file in this format; Note that the origin is not stored in the output file in this format;
that information must be recorded separately. that information must be recorded separately.
@ -20,8 +19,7 @@ The first two bytes of the file contain the origin address in
little-endian format. The remainder of the file is the emitted bytes little-endian format. The remainder of the file is the emitted bytes
of the compiled SixtyPical program, starting at that origin. of the compiled SixtyPical program, starting at that origin.
The default origin is $C000; it is likely you will want to The default origin is $C000; you will likely want override this.
override this.
This format coincides with Commodore's PRG format for disk files, This format coincides with Commodore's PRG format for disk files,
thus its name. thus its name.

20
eg/apple2/lores.60p Normal file
View File

@ -0,0 +1,20 @@
byte ds_graphics @ $C050
byte ds_text @ $C051
byte ds_full @ $C052
byte ds_split @ $C053
byte ds_page1 @ $C054
byte ds_page2 @ $C055
byte ds_lores @ $C056
byte ds_hires @ $C057
define main routine
inputs a
outputs ds_lores, ds_page1, ds_split, ds_graphics
trashes a, z, n
{
ld a, 0
st a, ds_lores
st a, ds_page1
st a, ds_split
st a, ds_graphics
}

19
eg/apple2/print.60p Normal file
View File

@ -0,0 +1,19 @@
// Write ">AB>" to "standard output"
define cout routine
inputs a
trashes a
@ $FDED
define main routine
trashes a, z, n
{
ld a, 62
call cout
ld a, 65
call cout
ld a, 66
call cout
ld a, 62
call cout
}

View File

@ -33,9 +33,11 @@
typedef routine typedef routine
inputs joy2, press_fire_msg, dispatch_game_state, inputs joy2, press_fire_msg, dispatch_game_state,
actor_pos, actor_delta, actor_logic, actor_pos, actor_delta, actor_logic,
player_died,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs dispatch_game_state, outputs dispatch_game_state,
actor_pos, actor_delta, actor_logic, actor_pos, actor_delta, actor_logic,
player_died,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic
game_state_routine game_state_routine
@ -45,13 +47,13 @@ typedef routine
// //
// Routines that conform to this type also follow this convention: // Routines that conform to this type also follow this convention:
// //
// Set carry if the player perished. Carry clear otherwise. // Set player_died to 1 if the player perished. Unchanged otherwise.
// //
typedef routine typedef routine
inputs pos, delta, joy2, screen inputs pos, delta, joy2, screen, player_died
outputs pos, delta, new_pos, screen, c outputs pos, delta, new_pos, screen, player_died
trashes a, x, y, z, n, v, ptr trashes a, x, y, z, n, v, c, ptr
logic_routine logic_routine
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@ -87,6 +89,8 @@ word new_pos
word table[256] actor_delta word table[256] actor_delta
word delta word delta
byte player_died
vector logic_routine table[256] actor_logic vector logic_routine table[256] actor_logic
vector logic_routine dispatch_logic vector logic_routine dispatch_logic
@ -117,7 +121,7 @@ vector game_state_routine
// Utility Routines // Utility Routines
// ---------------------------------------------------------------- // ----------------------------------------------------------------
routine read_stick define read_stick routine
inputs joy2 inputs joy2
outputs delta outputs delta
trashes a, x, z, n trashes a, x, z, n
@ -182,7 +186,7 @@ define check_button routine
} }
} }
routine clear_screen define clear_screen routine
outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, y, c, n, z trashes a, y, c, n, z
{ {
@ -205,7 +209,7 @@ routine clear_screen
} until z } until z
} }
routine calculate_new_position define calculate_new_position routine
inputs pos, delta inputs pos, delta
outputs new_pos outputs new_pos
trashes a, c, n, z, v trashes a, c, n, z, v
@ -239,9 +243,9 @@ define check_new_position_in_bounds routine
} }
} }
routine init_game define init_game routine
inputs actor_pos, actor_delta, actor_logic inputs actor_pos, actor_delta, actor_logic
outputs actor_pos, actor_delta, actor_logic outputs actor_pos, actor_delta, actor_logic, player_died
trashes pos, a, y, z, n, c, v trashes pos, a, y, z, n, c, v
{ {
ld y, 0 ld y, 0
@ -259,9 +263,11 @@ routine init_game
} until z } until z
ld y, 0 ld y, 0
copy word 0, actor_pos + y copy word 40, actor_pos + y
copy word 0, actor_delta + y copy word 0, actor_delta + y
copy player_logic, actor_logic + y copy player_logic, actor_logic + y
st y, player_died
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@ -301,10 +307,9 @@ define player_logic logic_routine
st off, c st off, c
add ptr, pos add ptr, pos
copy 81, [ptr] + y copy 81, [ptr] + y
st off, c
} else { } else {
st on, c ld a, 1
st a, player_died
} }
// FIXME these trashes, strictly speaking, probably shouldn't be needed, // FIXME these trashes, strictly speaking, probably shouldn't be needed,
@ -314,8 +319,6 @@ define player_logic logic_routine
trash ptr trash ptr
trash y trash y
trash v trash v
} else {
st off, c
} }
} }
@ -351,10 +354,6 @@ define enemy_logic logic_routine
st off, c st off, c
add ptr, pos add ptr, pos
copy 82, [ptr] + y copy 82, [ptr] + y
st off, c
} else {
st on, c
} }
// FIXME these trashes, strictly speaking, probably shouldn't be needed, // FIXME these trashes, strictly speaking, probably shouldn't be needed,
@ -373,8 +372,6 @@ define enemy_logic logic_routine
copy $ffd8, delta copy $ffd8, delta
} }
} }
st off, c
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@ -408,6 +405,7 @@ define game_state_title_screen game_state_routine
define game_state_play game_state_routine define game_state_play game_state_routine
{ {
ld x, 0 ld x, 0
st x, player_died
for x up to 15 { for x up to 15 {
copy actor_pos + x, pos copy actor_pos + x, pos
copy actor_delta + x, delta copy actor_delta + x, delta
@ -420,18 +418,19 @@ define game_state_play game_state_routine
save x { save x {
copy actor_logic + x, dispatch_logic copy actor_logic + x, dispatch_logic
call dispatch_logic call dispatch_logic
if c {
// Player died! Want no dead!
call clear_screen
copy game_state_game_over, dispatch_game_state
}
} }
copy pos, actor_pos + x copy pos, actor_pos + x
copy delta, actor_delta + x copy delta, actor_delta + x
} }
ld a, player_died
if not z {
// Player died! Want no dead!
call clear_screen
copy game_state_game_over, dispatch_game_state
}
goto save_cinv goto save_cinv
} }
@ -458,7 +457,7 @@ define our_cinv game_state_routine
goto dispatch_game_state goto dispatch_game_state
} }
routine main define main routine
inputs cinv inputs cinv
outputs cinv, save_cinv, pos, dispatch_game_state, outputs cinv, save_cinv, pos, dispatch_game_state,
screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4

View File

@ -3,7 +3,7 @@
// Define where the screen starts in memory: // Define where the screen starts in memory:
byte table[256] screen @ 1024 byte table[256] screen @ 1024
routine main define main routine
// These are the values that will be written to by this routine: // These are the values that will be written to by this routine:
trashes a, x, z, n, screen trashes a, x, z, n, screen
{ {

View File

@ -24,7 +24,7 @@ vector routine
trashes z, n trashes z, n
save_cinv save_cinv
routine our_cinv define our_cinv routine
inputs vic_border inputs vic_border
outputs vic_border outputs vic_border
trashes z, n trashes z, n
@ -33,7 +33,7 @@ routine our_cinv
goto save_cinv goto save_cinv
} }
routine main define main routine
inputs cinv inputs cinv
outputs cinv, save_cinv outputs cinv, save_cinv
trashes a, n, z trashes a, n, z

View File

@ -3,7 +3,7 @@ byte joy2 @ $dc00
word delta word delta
routine read_stick define read_stick routine
inputs joy2 inputs joy2
outputs delta outputs delta
trashes a, x, z, n trashes a, x, z, n
@ -36,7 +36,7 @@ routine read_stick
} }
} }
routine main define main routine
inputs joy2 inputs joy2
outputs delta outputs delta
trashes a, x, z, n, screen trashes a, x, z, n, screen

View File

@ -49,7 +49,7 @@ byte scanline : 85 // %01010101
// generating them as part of a SixtyPical program would not // generating them as part of a SixtyPical program would not
// be practical. So we just jump to this location instead. // be practical. So we just jump to this location instead.
routine pla_tay_pla_tax_pla_rti define pla_tay_pla_tax_pla_rti routine
inputs a inputs a
trashes a trashes a
@ $EA81 @ $EA81

View File

@ -1,6 +1,6 @@
byte screen @ 1024 byte screen @ 1024
routine main define main routine
trashes a, z, n, screen trashes a, z, n, screen
{ {
ld a, 83 ld a, 83

View File

@ -1,4 +1,4 @@
routine add_four define add_four routine
inputs a inputs a
outputs a outputs a
{ {

View File

@ -1,4 +1,4 @@
routine main define main routine
inputs a inputs a
outputs a outputs a
trashes c, z, n, v trashes c, z, n, v

View File

@ -1,5 +1,5 @@
word score word score
routine main define main routine
inputs score inputs score
outputs score outputs score
trashes a, c, z, v, n trashes a, c, z, v, n

View File

@ -2,7 +2,7 @@ buffer[2048] buf
pointer ptr @ 254 pointer ptr @ 254
byte foo byte foo
routine main define main routine
inputs buf inputs buf
outputs buf, y, foo outputs buf, y, foo
trashes a, z, n, ptr trashes a, z, n, ptr

View File

@ -1,16 +1,16 @@
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine print define print routine
trashes a, z, n trashes a, z, n
{ {
ld a, 65 ld a, 65
call chrout call chrout
} }
routine main define main routine
trashes a, z, n trashes a, z, n
{ {
call print call print

View File

@ -1,9 +1,9 @@
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine main define main routine
trashes a, x, y, z, n, c, v trashes a, x, y, z, n, c, v
{ {
ld a, 0 ld a, 0

View File

@ -1,9 +1,9 @@
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine main define main routine
trashes a, x, y, z, n, c, v trashes a, x, y, z, n, c, v
{ {
ld a, 0 ld a, 0

View File

@ -1,7 +1,7 @@
byte bar byte bar
byte baz byte baz
routine main define main routine
inputs baz inputs baz
outputs bar outputs bar
trashes a, n, z trashes a, n, z

View File

@ -1,9 +1,9 @@
byte lives byte lives
routine main define main routine
inputs lives inputs lives
outputs lives outputs lives
trashes a, x trashes a, x, z, n, c, v
{ {
ld a, 0 ld a, 0
st a, lives st a, lives

View File

@ -1,4 +1,4 @@
routine main define main routine
trashes a, y, z, n, c trashes a, y, z, n, c
{ {
ld y, 65 ld y, 65

View File

@ -1,14 +1,14 @@
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine bar trashes a, z, n { define bar routine trashes a, z, n {
ld a, 66 ld a, 66
call chrout call chrout
} }
routine main trashes a, z, n { define main routine trashes a, z, n {
ld a, 65 ld a, 65
call chrout call chrout
goto bar goto bar

View File

@ -1,6 +1,7 @@
routine foo define main routine
inputs a inputs a
outputs a outputs a
trashes z, n, c
{ {
cmp a, 42 cmp a, 42
if z { if z {

View File

@ -1,9 +1,9 @@
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine main define main routine
trashes a, y, z, n, c trashes a, y, z, n, c
{ {
ld y, 65 ld y, 65

View File

@ -1,11 +1,11 @@
byte foo byte foo
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine print define print routine
inputs foo inputs foo
trashes a, z, n trashes a, z, n
{ {
@ -13,7 +13,7 @@ routine print
call chrout call chrout
} }
routine main define main routine
trashes a, y, z, n, foo trashes a, y, z, n, foo
{ {
ld y, 65 ld y, 65

View File

@ -1,9 +1,9 @@
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine main define main routine
inputs a inputs a
trashes a, z, n trashes a, z, n
{ {

View File

@ -1,6 +1,6 @@
byte table[8] message : "WHAT?" byte table[8] message : "WHAT?"
routine main define main routine
inputs message inputs message
outputs x, a, z, n outputs x, a, z, n
{ {

View File

@ -1,19 +1,19 @@
// This will not compile on its own, because there is no `main`. // This will not compile on its own, because there is no `main`.
// But this and `vector-main.60p` together will compile. // But this and `vector-main.60p` together will compile.
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine printa define printa routine
trashes a, z, n trashes a, z, n
{ {
ld a, 65 ld a, 65
call chrout call chrout
} }
routine printb define printb routine
trashes a, z, n trashes a, z, n
{ {
ld a, 66 ld a, 66

View File

@ -12,7 +12,7 @@ vector routine
// call chrout // call chrout
// } // }
routine main define main routine
trashes print, a, z, n trashes print, a, z, n
{ {
copy printa, print copy printa, print

View File

@ -11,26 +11,26 @@ vector (routine
trashes a, z, n) trashes a, z, n)
table[32] vectors table[32] vectors
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine printa define printa routine
trashes a, z, n trashes a, z, n
{ {
ld a, 65 ld a, 65
call chrout call chrout
} }
routine printb define printb routine
trashes a, z, n trashes a, z, n
{ {
ld a, 66 ld a, 66
call chrout call chrout
} }
routine main define main routine
inputs vectors inputs vectors
outputs vectors outputs vectors
trashes print, a, x, z, n, c trashes print, a, x, z, n, c

View File

@ -2,26 +2,26 @@ vector routine
trashes a, z, n trashes a, z, n
print print
routine chrout define chrout routine
inputs a inputs a
trashes a trashes a
@ 65490 @ 65490
routine printa define printa routine
trashes a, z, n trashes a, z, n
{ {
ld a, 65 ld a, 65
call chrout call chrout
} }
routine printb define printb routine
trashes a, z, n trashes a, z, n
{ {
ld a, 66 ld a, 66
call chrout call chrout
} }
routine main define main routine
trashes print, a, z, n trashes print, a, z, n
{ {
copy printa, print copy printa, print

View File

@ -1,7 +1,7 @@
word one word one
word table[256] many word table[256] many
routine main define main routine
inputs one, many inputs one, many
outputs one, many outputs one, many
trashes a, x, y, n, z trashes a, x, y, n, z

View File

@ -3,7 +3,7 @@
// Define where the screen starts in memory: // Define where the screen starts in memory:
byte table[256] screen @ 7680 byte table[256] screen @ 7680
routine main define main routine
// These are the values that will be written to by this routine: // These are the values that will be written to by this routine:
trashes a, x, z, n, screen trashes a, x, z, n, screen
{ {

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
usage="Usage: loadngo.sh (c64|vic20|atari2600) [--dry-run] <source.60p>" usage="Usage: loadngo.sh (c64|vic20|atari2600|apple2) [--dry-run] <source.60p>"
arch="$1" arch="$1"
shift 1 shift 1
@ -21,6 +21,21 @@ elif [ "X$arch" = "Xvic20" ]; then
elif [ "X$arch" = "Xatari2600" ]; then elif [ "X$arch" = "Xatari2600" ]; then
output_format='atari2600-cart' output_format='atari2600-cart'
emu='stella' emu='stella'
elif [ "X$arch" = "Xapple2" ]; then
src="$1"
out=/tmp/a-out.bin
bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src --output $out || exit 1
ls -la $out
cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk
# TODO: replace HELLO with something that does like
# BLOAD "PROG"
# CALL 8192
# (not BRUN because it does not always return to BASIC afterwards not sure why)
a2rm sixtypical.dsk PROG
a2in B sixtypical.dsk PROG $out
linapple -d1 sixtypical.dsk -autoboot
rm -f $out sixtypical.dsk
exit 0
else else
echo $usage && exit 1 echo $usage && exit 1
fi fi
@ -38,7 +53,7 @@ fi
### do it ### ### do it ###
out=/tmp/a-out.prg out=/tmp/a-out.prg
bin/sixtypical --traceback --output-format=$output_format $src > $out || exit 1 bin/sixtypical --traceback --output-format=$output_format $src --output $out || exit 1
ls -la $out ls -la $out
$emu $out $emu $out
rm -f $out rm -f $out

View File

@ -1,6 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save
from sixtypical.model import ( from sixtypical.model import (
TYPE_BYTE, TYPE_WORD, TYPE_BYTE, TYPE_WORD,
TableType, BufferType, PointerType, VectorType, RoutineType, TableType, BufferType, PointerType, VectorType, RoutineType,
@ -372,23 +372,23 @@ class Analyzer(object):
context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes) context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes)
if self.debug: if self.debug:
print "at start of routine `{}`:".format(routine.name) print("at start of routine `{}`:".format(routine.name))
print context print(context)
self.analyze_block(routine.block, context) self.analyze_block(routine.block, context)
trashed = set(context.each_touched()) - set(context.each_meaningful()) trashed = set(context.each_touched()) - set(context.each_meaningful())
if self.debug: if self.debug:
print "at end of routine `{}`:".format(routine.name) print("at end of routine `{}`:".format(routine.name))
print context print(context)
print "trashed: ", LocationRef.format_set(trashed) print("trashed: ", LocationRef.format_set(trashed))
print "outputs: ", LocationRef.format_set(type_.outputs) print("outputs: ", LocationRef.format_set(type_.outputs))
trashed_outputs = type_.outputs & trashed trashed_outputs = type_.outputs & trashed
if trashed_outputs: if trashed_outputs:
print "TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs) print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs))
print '' print('')
print '-' * 79 print('-' * 79)
print '' print('')
# even if we goto another routine, we can't trash an output. # even if we goto another routine, we can't trash an output.
for ref in trashed: for ref in trashed:
@ -550,6 +550,8 @@ class Analyzer(object):
context.invalidate_range(dest) context.invalidate_range(dest)
elif opcode == 'call': elif opcode == 'call':
type = instr.location.type type = instr.location.type
if not isinstance(type, (RoutineType, VectorType)):
raise TypeMismatchError(instr, instr.location)
if isinstance(type, VectorType): if isinstance(type, VectorType):
type = type.of_type type = type.of_type
for ref in type.inputs: for ref in type.inputs:
@ -771,17 +773,22 @@ class Analyzer(object):
context.set_writeable(instr.dest) context.set_writeable(instr.dest)
def analyze_save(self, instr, context): def analyze_save(self, instr, context):
if len(instr.locations) != 1: batons = []
raise NotImplementedError("Only 1 location in save is supported right now") for location in instr.locations:
location = instr.locations[0] self.assert_type(TYPE_BYTE, location)
self.assert_type(TYPE_BYTE, location) baton = context.extract(location)
batons.append(baton)
baton = context.extract(location)
self.analyze_block(instr.block, context) self.analyze_block(instr.block, context)
if context.encountered_gotos(): if context.encountered_gotos():
raise IllegalJumpError(instr, instr) raise IllegalJumpError(instr, instr)
context.re_introduce(baton)
for location in reversed(instr.locations):
baton = batons.pop()
context.re_introduce(baton)
# We do this check outside the loop, because A is only preserved
# if it is the outermost thing being `save`d.
if location == REG_A: if location == REG_A:
pass pass
else: else:

View File

@ -37,14 +37,16 @@ class AST(object):
def all_children(self): def all_children(self):
for attr in self.children_attrs: for attr in self.children_attrs:
for child in self.attrs[attr]: for child in self.attrs[attr]:
if child is not None:
yield child
for subchild in child.all_children():
yield subchild
for attr in self.child_attrs:
child = self.attrs[attr]
if child is not None:
yield child yield child
for subchild in child.all_children(): for subchild in child.all_children():
yield subchild yield subchild
for attr in self.child_attrs:
child = self.attrs[attr]
yield child
for subchild in child.all_children():
yield subchild
class Program(AST): class Program(AST):

View File

@ -1,6 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save
from sixtypical.model import ( from sixtypical.model import (
ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef, ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
TYPE_BIT, TYPE_BYTE, TYPE_WORD, TYPE_BIT, TYPE_BYTE, TYPE_WORD,
@ -112,7 +112,7 @@ class Compiler(object):
routine_name = roster_row[-1] routine_name = roster_row[-1]
self.compile_routine(self.routines[routine_name]) self.compile_routine(self.routines[routine_name])
for location, label in self.trampolines.iteritems(): for location, label in self.trampolines.items():
self.emitter.resolve_label(label) self.emitter.resolve_label(label)
self.emitter.emit(JMP(Indirect(self.get_label(location.name)))) self.emitter.emit(JMP(Indirect(self.get_label(location.name))))
self.emitter.emit(RTS()) self.emitter.emit(RTS())
@ -618,27 +618,32 @@ class Compiler(object):
self.emitter.emit(CLI()) self.emitter.emit(CLI())
def compile_save(self, instr): def compile_save(self, instr):
location = instr.locations[0] for location in instr.locations:
if location == REG_A: if location == REG_A:
self.emitter.emit(PHA()) self.emitter.emit(PHA())
self.compile_block(instr.block) elif location == REG_X:
self.emitter.emit(PLA()) self.emitter.emit(TXA())
elif location == REG_X: self.emitter.emit(PHA())
self.emitter.emit(TXA()) elif location == REG_Y:
self.emitter.emit(PHA()) self.emitter.emit(TYA())
self.compile_block(instr.block) self.emitter.emit(PHA())
self.emitter.emit(PLA()) else:
self.emitter.emit(TAX()) src_label = self.get_label(location.name)
elif location == REG_Y: self.emitter.emit(LDA(Absolute(src_label)))
self.emitter.emit(TYA()) self.emitter.emit(PHA())
self.emitter.emit(PHA())
self.compile_block(instr.block) self.compile_block(instr.block)
self.emitter.emit(PLA())
self.emitter.emit(TAY()) for location in reversed(instr.locations):
else: if location == REG_A:
src_label = self.get_label(location.name) self.emitter.emit(PLA())
self.emitter.emit(LDA(Absolute(src_label))) elif location == REG_X:
self.emitter.emit(PHA()) self.emitter.emit(PLA())
self.compile_block(instr.block) self.emitter.emit(TAX())
self.emitter.emit(PLA()) elif location == REG_Y:
self.emitter.emit(STA(Absolute(src_label))) self.emitter.emit(PLA())
self.emitter.emit(TAY())
else:
src_label = self.get_label(location.name)
self.emitter.emit(PLA())
self.emitter.emit(STA(Absolute(src_label)))

View File

@ -8,12 +8,15 @@ class Emittable(object):
raise NotImplementedError raise NotImplementedError
def serialize(self, addr): def serialize(self, addr):
"""Should return an array of unsigned bytes (integers from 0 to 255.)
`addr` is the address the value is being serialized at; for most objects
it makes no difference, but some objects (like relative branches) do care."""
raise NotImplementedError raise NotImplementedError
class Byte(Emittable): class Byte(Emittable):
def __init__(self, value): def __init__(self, value):
if isinstance(value, basestring): if isinstance(value, str):
value = ord(value) value = ord(value)
if value < -127 or value > 255: if value < -127 or value > 255:
raise IndexError(value) raise IndexError(value)
@ -24,8 +27,8 @@ class Byte(Emittable):
def size(self): def size(self):
return 1 return 1
def serialize(self, addr=None): def serialize(self, addr):
return chr(self.value) return [self.value]
def __repr__(self): def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.value) return "%s(%r)" % (self.__class__.__name__, self.value)
@ -39,11 +42,11 @@ class Word(Emittable):
def size(self): def size(self):
return 2 return 2
def serialize(self, addr=None): def serialize(self, addr):
word = self.value word = self.value
low = word & 255 low = word & 255
high = (word >> 8) & 255 high = (word >> 8) & 255
return chr(low) + chr(high) return [low, high]
def __repr__(self): def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.value) return "%s(%r)" % (self.__class__.__name__, self.value)
@ -59,10 +62,12 @@ class Table(Emittable):
def size(self): def size(self):
return self._size return self._size
def serialize(self, addr=None): def serialize(self, addr):
buf = ''.join([emittable.serialize() for emittable in self.value]) buf = []
for emittable in self.value:
buf.extend(emittable.serialize(addr)) # FIXME: addr + offset
while len(buf) < self.size(): while len(buf) < self.size():
buf += chr(0) buf.append(0)
return buf return buf
def __repr__(self): def __repr__(self):
@ -84,17 +89,17 @@ class Label(Emittable):
def size(self): def size(self):
return 2 return 2
def serialize(self, addr=None, offset=0): def serialize(self, addr, offset=0):
assert self.addr is not None, "unresolved label: %s" % self.name assert self.addr is not None, "unresolved label: %s" % self.name
return Word(self.addr + offset).serialize() return Word(self.addr + offset).serialize(addr)
def serialize_relative_to(self, addr): def serialize_relative_to(self, addr):
assert self.addr is not None, "unresolved label: %s" % self.name assert self.addr is not None, "unresolved label: %s" % self.name
return Byte(self.addr - (addr + 2)).serialize() return Byte(self.addr - (addr + 2)).serialize(addr)
def serialize_as_zero_page(self, offset=0): def serialize_as_zero_page(self, addr, offset=0):
assert self.addr is not None, "unresolved label: %s" % self.name assert self.addr is not None, "unresolved label: %s" % self.name
return Byte(self.addr + offset).serialize() return Byte(self.addr + offset).serialize(addr)
def __repr__(self): def __repr__(self):
addr_s = ', addr=%r' % self.addr if self.addr is not None else '' addr_s = ', addr=%r' % self.addr if self.addr is not None else ''
@ -111,11 +116,11 @@ class Offset(Emittable):
def size(self): def size(self):
self.label.size() self.label.size()
def serialize(self, addr=None): def serialize(self, addr):
return self.label.serialize(offset=self.offset) return self.label.serialize(addr, offset=self.offset)
def serialize_as_zero_page(self, offset=0): def serialize_as_zero_page(self, addr, offset=0):
return self.label.serialize_as_zero_page(offset=self.offset) return self.label.serialize_as_zero_page(addr, offset=self.offset)
def __repr__(self): def __repr__(self):
return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset) return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset)
@ -129,8 +134,8 @@ class HighAddressByte(Emittable):
def size(self): def size(self):
return 1 return 1
def serialize(self, addr=None): def serialize(self, addr):
return self.label.serialize()[0] return [self.label.serialize(addr)[0]]
def __repr__(self): def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.label) return "%s(%r)" % (self.__class__.__name__, self.label)
@ -144,8 +149,8 @@ class LowAddressByte(Emittable):
def size(self): def size(self):
return 1 return 1
def serialize(self, addr=None): def serialize(self, addr):
return self.label.serialize()[1] return [self.label.serialize(addr)[1]]
def __repr__(self): def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.label) return "%s(%r)" % (self.__class__.__name__, self.label)
@ -166,11 +171,12 @@ class Emitter(object):
self.accum.append(thing) self.accum.append(thing)
self.addr += thing.size() self.addr += thing.size()
def serialize(self, stream): def serialize_to(self, stream):
"""`stream` should be a file opened in binary mode."""
addr = self.start_addr addr = self.start_addr
for emittable in self.accum: for emittable in self.accum:
chunk = emittable.serialize(addr) chunk = emittable.serialize(addr)
stream.write(chunk) stream.write(bytearray(chunk))
addr += len(chunk) addr += len(chunk)
def make_label(self, name=None): def make_label(self, name=None):

View File

@ -43,7 +43,7 @@ class FallthruAnalyzer(object):
while pending_routines: while pending_routines:
chains = [self.find_chain(k, pending_routines) for k in pending_routines.keys()] chains = [self.find_chain(k, pending_routines) for k in pending_routines.keys()]
chains.sort(key=len, reverse=True) chains.sort(key=lambda x: (len(x), str(x)), reverse=True)
c = chains[0] c = chains[0]
roster.append(c) roster.append(c)
for k in c: for k in c:

View File

@ -8,7 +8,7 @@ class AddressingMode(Emittable):
"""Size of the operand for the mode (not including the opcode)""" """Size of the operand for the mode (not including the opcode)"""
raise NotImplementedError raise NotImplementedError
def serialize(self, addr=None): def serialize(self, addr):
raise NotImplementedError raise NotImplementedError
def __repr__(self): def __repr__(self):
@ -19,8 +19,8 @@ class Implied(AddressingMode):
def size(self): def size(self):
return 0 return 0
def serialize(self, addr=None): def serialize(self, addr):
return '' return []
def __repr__(self): def __repr__(self):
return "%s()" % (self.__class__.__name__) return "%s()" % (self.__class__.__name__)
@ -34,8 +34,8 @@ class Immediate(AddressingMode):
def size(self): def size(self):
return 1 return 1
def serialize(self, addr=None): def serialize(self, addr):
return self.value.serialize() return self.value.serialize(addr)
class Absolute(AddressingMode): class Absolute(AddressingMode):
@ -46,8 +46,8 @@ class Absolute(AddressingMode):
def size(self): def size(self):
return 2 return 2
def serialize(self, addr=None): def serialize(self, addr):
return self.value.serialize() return self.value.serialize(addr)
class AbsoluteX(Absolute): class AbsoluteX(Absolute):
@ -66,8 +66,8 @@ class ZeroPage(AddressingMode):
def size(self): def size(self):
return 1 return 1
def serialize(self, addr=None): def serialize(self, addr):
return self.value.serialize_as_zero_page() return self.value.serialize_as_zero_page(addr)
class Indirect(AddressingMode): class Indirect(AddressingMode):
@ -78,8 +78,8 @@ class Indirect(AddressingMode):
def size(self): def size(self):
return 2 return 2
def serialize(self, addr=None): def serialize(self, addr):
return self.value.serialize() return self.value.serialize(addr)
class IndirectY(ZeroPage): class IndirectY(ZeroPage):
@ -94,7 +94,7 @@ class Relative(AddressingMode):
def size(self): def size(self):
return 1 return 1
def serialize(self, addr=None): def serialize(self, addr):
return self.value.serialize_relative_to(addr) return self.value.serialize_relative_to(addr)
@ -108,11 +108,8 @@ class Instruction(Emittable):
def size(self): def size(self):
return 1 + self.operand.size() if self.operand else 0 return 1 + self.operand.size() if self.operand else 0
def serialize(self, addr=None): def serialize(self, addr):
return ( return [self.opcodes[self.operand.__class__]] + self.operand.serialize(addr)
chr(self.opcodes[self.operand.__class__]) +
self.operand.serialize(addr)
)
def __repr__(self): def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.operand) return "%s(%r)" % (self.__class__.__name__, self.operand)

View File

@ -18,20 +18,6 @@ class Type(object):
def __hash__(self): def __hash__(self):
return hash(self.name) 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', max_range=(0, 1)) TYPE_BIT = Type('bit', max_range=(0, 1))
TYPE_BYTE = Type('byte', max_range=(0, 255)) TYPE_BYTE = Type('byte', max_range=(0, 255))
@ -41,11 +27,11 @@ TYPE_WORD = Type('word', max_range=(0, 65535))
class RoutineType(Type): class RoutineType(Type):
"""This memory location contains the code for a routine.""" """This memory location contains the code for a routine."""
def __init__(self, inputs=None, outputs=None, trashes=None): def __init__(self, inputs, outputs, trashes):
self.name = 'routine' self.name = 'routine'
self.inputs = inputs or set() self.inputs = inputs
self.outputs = outputs or set() self.outputs = outputs
self.trashes = trashes or set() self.trashes = trashes
def __repr__(self): def __repr__(self):
return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % ( return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % (
@ -172,7 +158,7 @@ class LocationRef(Ref):
@classmethod @classmethod
def format_set(cls, location_refs): def format_set(cls, location_refs):
return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)]) return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs, key=lambda x: x.name)])
class IndirectRef(Ref): class IndirectRef(Ref):

View File

@ -0,0 +1,74 @@
"""Executable file writer."""
from sixtypical.emitter import Emitter, Byte, Word
class Outputter(object):
def __init__(self, fh, start_addr=None):
self.start_addr = self.__class__.start_addr
if start_addr is not None:
self.start_addr = start_addr
self.prelude = self.__class__.prelude
self.fh = fh
self.emitter = Emitter(self.start_addr)
def write_header(self):
pass
def write_prelude(self):
self.write_header()
for byte in self.prelude:
self.emitter.emit(Byte(byte))
def write_postlude(self):
pass
class RawOutputter(Outputter):
start_addr = 0x0000
prelude = []
class PrgOutputter(Outputter):
start_addr = 0xc000
prelude = []
def write_header(self):
# If we are outputting a .PRG, we output the load address first.
# We don't use the Emitter for this b/c not part of addr space.
self.fh.write(bytearray(Word(self.start_addr).serialize(0)))
class C64BasicPrgOutputter(PrgOutputter):
start_addr = 0x0801
prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
class Vic20BasicPrgOutputter(PrgOutputter):
start_addr = 0x1001
prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
class Atari2600CartOutputter(Outputter):
start_addr = 0xf000
prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9,
0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb]
def write_postlude(self):
# If we are outputting a cartridge with boot and BRK address
# at the end, pad to ROM size minus 4 bytes, and emit addresses.
self.emitter.pad_to_size(4096 - 4)
self.emitter.emit(Word(self.start_addr))
self.emitter.emit(Word(self.start_addr))
def outputter_class_for(output_format):
return {
'raw': RawOutputter,
'prg': PrgOutputter,
'c64-basic-prg': C64BasicPrgOutputter,
'vic20-basic-prg': Vic20BasicPrgOutputter,
'atari2600-cart': Atari2600CartOutputter,
}[output_format]

View File

@ -18,6 +18,14 @@ class SymEntry(object):
return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model) return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model)
class ForwardReference(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.name)
class ParsingContext(object): class ParsingContext(object):
def __init__(self): def __init__(self):
self.symbols = {} # token -> SymEntry self.symbols = {} # token -> SymEntry
@ -33,7 +41,7 @@ class ParsingContext(object):
def __str__(self): def __str__(self):
return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts) return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts)
def lookup(self, name): def fetch(self, name):
if name in self.statics: if name in self.statics:
return self.statics[name].model return self.statics[name].model
if name in self.symbols: if name in self.symbols:
@ -45,17 +53,65 @@ class Parser(object):
def __init__(self, context, text, filename): def __init__(self, context, text, filename):
self.context = context self.context = context
self.scanner = Scanner(text, filename) self.scanner = Scanner(text, filename)
self.backpatch_instrs = []
def syntax_error(self, msg): def syntax_error(self, msg):
self.scanner.syntax_error(msg) self.scanner.syntax_error(msg)
def lookup(self, name): def lookup(self, name):
model = self.context.lookup(name) model = self.context.fetch(name)
if model is None: if model is None:
self.syntax_error('Undefined symbol "{}"'.format(name)) self.syntax_error('Undefined symbol "{}"'.format(name))
return model return model
def declare(self, name, symentry, static=False):
if self.context.fetch(name):
self.syntax_error('Symbol "%s" already declared' % name)
if static:
self.context.statics[name] = symentry
else:
self.context.symbols[name] = symentry
def clear_statics(self):
self.context.statics = {}
# ---- symbol resolution
def resolve_symbols(self, program):
# This could stand to be better unified.
def backpatch_constraint_labels(type_):
def resolve(w):
if not isinstance(w, ForwardReference):
return w
return self.lookup(w.name)
if isinstance(type_, TableType):
backpatch_constraint_labels(type_.of_type)
elif isinstance(type_, VectorType):
backpatch_constraint_labels(type_.of_type)
elif isinstance(type_, RoutineType):
type_.inputs = set([resolve(w) for w in type_.inputs])
type_.outputs = set([resolve(w) for w in type_.outputs])
type_.trashes = set([resolve(w) for w in type_.trashes])
for defn in program.defns:
backpatch_constraint_labels(defn.location.type)
for routine in program.routines:
backpatch_constraint_labels(routine.location.type)
def resolve_fwd_reference(obj, field):
field_value = getattr(obj, field, None)
if isinstance(field_value, ForwardReference):
setattr(obj, field, self.lookup(field_value.name))
elif isinstance(field_value, IndexedRef):
if isinstance(field_value.ref, ForwardReference):
field_value.ref = self.lookup(field_value.ref.name)
for node in program.all_children():
if isinstance(node, SingleOp):
resolve_fwd_reference(node, 'location')
resolve_fwd_reference(node, 'src')
resolve_fwd_reference(node, 'dest')
# --- grammar productions # --- grammar productions
def program(self): def program(self):
@ -70,47 +126,19 @@ class Parser(object):
typenames.extend(self.context.typedefs.keys()) typenames.extend(self.context.typedefs.keys())
while self.scanner.on(*typenames): while self.scanner.on(*typenames):
defn = self.defn() defn = self.defn()
name = defn.name self.declare(defn.name, SymEntry(defn, defn.location))
if self.context.lookup(name):
self.syntax_error('Symbol "%s" already declared' % name)
self.context.symbols[name] = SymEntry(defn, defn.location)
defns.append(defn) defns.append(defn)
while self.scanner.on('define', 'routine'): while self.scanner.consume('define'):
if self.scanner.consume('define'): name = self.scanner.token
name = self.scanner.token self.scanner.scan()
self.scanner.scan() routine = self.routine(name)
routine = self.routine(name) self.declare(name, SymEntry(routine, routine.location))
else:
routine = self.legacy_routine()
name = routine.name
if self.context.lookup(name):
self.syntax_error('Symbol "%s" already declared' % name)
self.context.symbols[name] = SymEntry(routine, routine.location)
routines.append(routine) routines.append(routine)
self.scanner.check_type('EOF') self.scanner.check_type('EOF')
# now backpatch the executable types. program = Program(self.scanner.line_number, defns=defns, routines=routines)
#for type_name, type_ in self.context.typedefs.iteritems(): self.resolve_symbols(program)
# type_.backpatch_constraint_labels(lambda w: self.lookup(w)) return program
for defn in defns:
defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
for routine in routines:
routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
for instr in self.backpatch_instrs:
if instr.opcode in ('call', 'goto'):
name = instr.location
model = self.lookup(name)
if not isinstance(model.type, (RoutineType, VectorType)):
self.syntax_error('Illegal call of non-executable "%s"' % name)
instr.location = model
if instr.opcode in ('copy',) and isinstance(instr.src, basestring):
name = instr.src
model = self.lookup(name)
if not isinstance(model.type, (RoutineType, VectorType)):
self.syntax_error('Illegal copy of non-executable "%s"' % name)
instr.src = model
return Program(self.scanner.line_number, defns=defns, routines=routines)
def typedef(self): def typedef(self):
self.scanner.expect('typedef') self.scanner.expect('typedef')
@ -250,27 +278,10 @@ class Parser(object):
outputs = set(self.labels()) outputs = set(self.labels())
if self.scanner.consume('trashes'): if self.scanner.consume('trashes'):
trashes = set(self.labels()) trashes = set(self.labels())
return (inputs, outputs, trashes) return (
set([ForwardReference(n) for n in inputs]),
def legacy_routine(self): set([ForwardReference(n) for n in outputs]),
self.scanner.expect('routine') set([ForwardReference(n) for n in trashes])
name = self.scanner.token
self.scanner.scan()
(inputs, outputs, trashes) = self.constraints()
type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
if self.scanner.consume('@'):
self.scanner.check_type('integer literal')
block = None
addr = int(self.scanner.token)
self.scanner.scan()
else:
block = self.block()
addr = None
location = LocationRef(type_, name)
return Routine(
self.scanner.line_number,
name=name, block=block, addr=addr,
location=location
) )
def routine(self, name): def routine(self, name):
@ -286,9 +297,11 @@ class Parser(object):
else: else:
statics = self.statics() statics = self.statics()
self.context.statics = self.compose_statics_dict(statics) self.clear_statics()
for defn in statics:
self.declare(defn.name, SymEntry(defn, defn.location), static=True)
block = self.block() block = self.block()
self.context.statics = {} self.clear_statics()
addr = None addr = None
location = LocationRef(type_, name) location = LocationRef(type_, name)
@ -298,15 +311,6 @@ class Parser(object):
location=location, statics=statics location=location, statics=statics
) )
def compose_statics_dict(self, statics):
c = {}
for defn in statics:
name = defn.name
if self.context.lookup(name):
self.syntax_error('Symbol "%s" already declared' % name)
c[name] = SymEntry(defn, defn.location)
return c
def labels(self): def labels(self):
accum = [] accum = []
accum.append(self.label()) accum.append(self.label())
@ -328,23 +332,19 @@ class Parser(object):
accum.append(self.locexpr()) accum.append(self.locexpr())
return accum return accum
def locexpr(self, forward=False): def locexpr(self):
if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'): if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'):
return self.const() return self.const()
elif forward: else:
name = self.scanner.token name = self.scanner.token
self.scanner.scan() self.scanner.scan()
loc = self.context.lookup(name) loc = self.context.fetch(name)
if loc is not None: if loc:
return loc return loc
else: else:
return name return ForwardReference(name)
else:
loc = self.lookup(self.scanner.token)
self.scanner.scan()
return loc
def indlocexpr(self, forward=False): def indlocexpr(self):
if self.scanner.consume('['): if self.scanner.consume('['):
loc = self.locexpr() loc = self.locexpr()
self.scanner.expect(']') self.scanner.expect(']')
@ -355,11 +355,11 @@ class Parser(object):
loc = self.locexpr() loc = self.locexpr()
return AddressRef(loc) return AddressRef(loc)
else: else:
return self.indexed_locexpr(forward=forward) return self.indexed_locexpr()
def indexed_locexpr(self, forward=False): def indexed_locexpr(self):
loc = self.locexpr(forward=forward) loc = self.locexpr()
if not isinstance(loc, basestring): if not isinstance(loc, str):
index = None index = None
if self.scanner.consume('+'): if self.scanner.consume('+'):
index = self.locexpr() index = self.locexpr()
@ -453,17 +453,15 @@ class Parser(object):
self.scanner.scan() self.scanner.scan()
name = self.scanner.token name = self.scanner.token
self.scanner.scan() self.scanner.scan()
instr = SingleOp(self.scanner.line_number, opcode=opcode, location=name, dest=None, src=None) instr = SingleOp(self.scanner.line_number, opcode=opcode, location=ForwardReference(name), dest=None, src=None)
self.backpatch_instrs.append(instr)
return instr return instr
elif self.scanner.token in ("copy",): elif self.scanner.token in ("copy",):
opcode = self.scanner.token opcode = self.scanner.token
self.scanner.scan() self.scanner.scan()
src = self.indlocexpr(forward=True) src = self.indlocexpr()
self.scanner.expect(',') self.scanner.expect(',')
dest = self.indlocexpr() dest = self.indlocexpr()
instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src) instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src)
self.backpatch_instrs.append(instr)
return instr return instr
elif self.scanner.consume("with"): elif self.scanner.consume("with"):
self.scanner.expect("interrupts") self.scanner.expect("interrupts")
@ -479,3 +477,17 @@ class Parser(object):
return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest) return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest)
else: else:
self.syntax_error('bad opcode "%s"' % self.scanner.token) self.syntax_error('bad opcode "%s"' % self.scanner.token)
# - - - -
def merge_programs(programs):
"""Assumes that the programs do not have any conflicts."""
full = Program(1, defns=[], routines=[])
for p in programs:
full.defns.extend(p.defns)
full.routines.extend(p.routines)
return full

File diff suppressed because it is too large Load Diff

View File

@ -7,20 +7,20 @@ SixtyPical to 6502 machine code.
[Falderal]: http://catseye.tc/node/Falderal [Falderal]: http://catseye.tc/node/Falderal
-> Functionality "Compile SixtyPical program" is implemented by -> Functionality "Compile SixtyPical program" is implemented by
-> shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo" -> shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
-> Tests for functionality "Compile SixtyPical program" -> Tests for functionality "Compile SixtyPical program"
Null program. Null program.
| routine main | define main routine
| { | {
| } | }
= $080D RTS = $080D RTS
`nop` program. `nop` program.
| routine main | define main routine
| { | {
| nop | nop
| } | }
@ -29,7 +29,7 @@ Null program.
Rudimentary program. Rudimentary program.
| routine main | define main routine
| inputs a | inputs a
| outputs a | outputs a
| trashes c, z, n, v | trashes c, z, n, v
@ -43,12 +43,12 @@ Rudimentary program.
Call extern. Call extern.
| routine chrout | define chrout routine
| inputs a | inputs a
| trashes a | trashes a
| @ 65490 | @ 65490
| |
| routine main | define main routine
| inputs a | inputs a
| trashes a, z, n | trashes a, z, n
| { | {
@ -61,7 +61,7 @@ Call extern.
Call defined routine. Call defined routine.
| routine foo | define foo routine
| outputs a, x, y | outputs a, x, y
| trashes z, n | trashes z, n
| { | {
@ -70,7 +70,7 @@ Call defined routine.
| ld y, 0 | ld y, 0
| } | }
| |
| routine main | define main routine
| trashes a, x, y, z, n | trashes a, x, y, z, n
| { | {
| call foo | call foo
@ -86,7 +86,7 @@ Access a defined memory location.
| byte foo | byte foo
| |
| routine main | define main routine
| trashes a, y, z, n, foo | trashes a, y, z, n, foo
| { | {
| ld y, 0 | ld y, 0
@ -102,7 +102,7 @@ Memory location with explicit address.
| byte screen @ 1024 | byte screen @ 1024
| |
| routine main | define main routine
| trashes a, z, n, screen | trashes a, z, n, screen
| { | {
| ld a, 100 | ld a, 100
@ -118,7 +118,7 @@ and `and`, `or`, and `xor` use zero-page addressing.
| byte zp @ $00 | byte zp @ $00
| byte screen @ 100 | byte screen @ 100
| |
| routine main | define main routine
| inputs screen, zp | inputs screen, zp
| outputs screen, zp | outputs screen, zp
| trashes a, z, n | trashes a, z, n
@ -144,7 +144,7 @@ Memory location with initial value.
| byte lives : 3 | byte lives : 3
| |
| routine main | define main routine
| inputs lives | inputs lives
| trashes a, z, n | trashes a, z, n
| { | {
@ -159,7 +159,7 @@ Word memory locations with explicit address, initial value.
| word w1 @ 60001 | word w1 @ 60001
| word w2 : 3003 | word w2 : 3003
| |
| routine main | define main routine
| inputs w1 | inputs w1
| outputs w2 | outputs w2
| trashes a, z, n | trashes a, z, n
@ -178,7 +178,7 @@ Initialized byte table, initialized with ASCII string. Bytes allocated, but bey
| byte table[8] message : "WHAT?" | byte table[8] message : "WHAT?"
| |
| routine main | define main routine
| inputs message | inputs message
| outputs x, a, z, n | outputs x, a, z, n
| { | {
@ -200,7 +200,7 @@ Initialized byte table, initialized with list of byte values.
| byte table[8] message : 255, 0, 129, 128, 127 | byte table[8] message : 255, 0, 129, 128, 127
| |
| routine main | define main routine
| inputs message | inputs message
| outputs x, a, z, n | outputs x, a, z, n
| { | {
@ -222,7 +222,7 @@ Initialized word table, initialized with list of word values.
| word table[4] message : 65535, 0, 127, 127 | word table[4] message : 65535, 0, 127, 127
| |
| routine main | define main routine
| { | {
| } | }
= $080D RTS = $080D RTS
@ -239,7 +239,7 @@ Some instructions.
| byte foo | byte foo
| |
| routine main | define main routine
| trashes a, x, y, z, n, c, v, foo | trashes a, x, y, z, n, c, v, foo
| { | {
| ld a, 0 | ld a, 0
@ -317,7 +317,7 @@ Some instructions on tables. (1/3)
| byte table[256] many | byte table[256] many
| |
| routine main | define main routine
| inputs many | inputs many
| outputs many | outputs many
| trashes a, x, c, n, z, v | trashes a, x, c, n, z, v
@ -341,7 +341,7 @@ Some instructions on tables. (2/3)
| byte table[256] many | byte table[256] many
| |
| routine main | define main routine
| inputs many | inputs many
| outputs many | outputs many
| trashes a, x, c, n, z | trashes a, x, c, n, z
@ -363,7 +363,7 @@ Some instructions on tables. (3/3)
| byte table[256] many | byte table[256] many
| |
| routine main | define main routine
| inputs many | inputs many
| outputs many | outputs many
| trashes a, x, c, n, z | trashes a, x, c, n, z
@ -387,7 +387,7 @@ Some instructions on tables. (3/3)
Compiling `if`. Compiling `if`.
| routine main | define main routine
| trashes a, x, y, z, n, c, v | trashes a, x, y, z, n, c, v
| { | {
| ld a, 0 | ld a, 0
@ -406,7 +406,7 @@ Compiling `if`.
Compiling `if not`. Compiling `if not`.
| routine main | define main routine
| trashes a, x, y, z, n, c, v | trashes a, x, y, z, n, c, v
| { | {
| ld a, 0 | ld a, 0
@ -425,7 +425,7 @@ Compiling `if not`.
Compiling `if` without `else`. Compiling `if` without `else`.
| routine main | define main routine
| trashes a, x, y, z, n, c, v | trashes a, x, y, z, n, c, v
| { | {
| ld a, 0 | ld a, 0
@ -442,7 +442,7 @@ Compiling `if` without `else`.
Compiling `repeat ... until z`. Compiling `repeat ... until z`.
| routine main | define main routine
| trashes a, y, z, n, c | trashes a, y, z, n, c
| { | {
| ld y, 65 | ld y, 65
@ -461,7 +461,7 @@ Compiling `repeat ... until z`.
Compiling `repeat ... until not z`. Compiling `repeat ... until not z`.
| routine main | define main routine
| trashes a, y, z, n, c | trashes a, y, z, n, c
| { | {
| ld y, 65 | ld y, 65
@ -480,7 +480,7 @@ Compiling `repeat ... until not z`.
Compiling `repeat ... until n`. Compiling `repeat ... until n`.
| routine main | define main routine
| trashes a, y, z, n, c | trashes a, y, z, n, c
| { | {
| ld y, 65 | ld y, 65
@ -497,7 +497,7 @@ Compiling `repeat ... until n`.
Compiling `repeat ... until not n`. Compiling `repeat ... until not n`.
| routine main | define main routine
| trashes a, y, z, n, c | trashes a, y, z, n, c
| { | {
| ld y, 199 | ld y, 199
@ -514,7 +514,7 @@ Compiling `repeat ... until not n`.
Compiling `repeat forever`. Compiling `repeat forever`.
| routine main | define main routine
| trashes a, y, z, n, c | trashes a, y, z, n, c
| { | {
| ld y, 65 | ld y, 65
@ -529,7 +529,7 @@ Compiling `repeat forever`.
The body of `repeat forever` can be empty. The body of `repeat forever` can be empty.
| routine main | define main routine
| { | {
| repeat { | repeat {
| } forever | } forever
@ -579,7 +579,7 @@ Compiling `for ... down to`.
Compiling `save`. Compiling `save`.
| routine main | define main routine
| inputs a | inputs a
| outputs a | outputs a
| trashes z, n | trashes z, n
@ -601,10 +601,32 @@ Compiling `save`.
= $0816 PLA = $0816 PLA
= $0817 RTS = $0817 RTS
Compiling `save` with shortcut syntax.
| define main routine
| inputs a
| outputs a
| trashes z, n
| {
| save a, x {
| ld a, 0
| ld x, 1
| }
| }
= $080D PHA
= $080E TXA
= $080F PHA
= $0810 LDA #$00
= $0812 LDX #$01
= $0814 PLA
= $0815 TAX
= $0816 PLA
= $0817 RTS
Compiling `save` on a user-defined location. Compiling `save` on a user-defined location.
| byte foo | byte foo
| routine main | define main routine
| trashes a, z, n | trashes a, z, n
| { | {
| save foo { | save foo {
@ -625,7 +647,7 @@ Indexed access.
| byte one | byte one
| byte table[256] many | byte table[256] many
| |
| routine main | define main routine
| outputs many | outputs many
| trashes a, x, n, z | trashes a, x, n, z
| { | {
@ -645,7 +667,7 @@ Byte tables take up, at most, 256 bytes in memory.
| byte table[256] tab1 | byte table[256] tab1
| byte table[256] tab2 | byte table[256] tab2
| |
| routine main | define main routine
| inputs tab1 | inputs tab1
| outputs tab2 | outputs tab2
| trashes a, x, n, z | trashes a, x, n, z
@ -664,7 +686,7 @@ Byte storage locations take up only 1 byte in memory.
| byte one | byte one
| byte two | byte two
| |
| routine main | define main routine
| outputs one, two | outputs one, two
| trashes a, x, n, z | trashes a, x, n, z
| { | {
@ -682,7 +704,7 @@ Copy byte to byte.
| byte bar | byte bar
| byte baz | byte baz
| |
| routine main | define main routine
| inputs baz | inputs baz
| outputs bar | outputs bar
| trashes a, n, z | trashes a, n, z
@ -698,7 +720,7 @@ Copy word to word.
| word bar | word bar
| word baz | word baz
| |
| routine main | define main routine
| inputs baz | inputs baz
| outputs bar | outputs bar
| trashes a, n, z | trashes a, n, z
@ -715,7 +737,7 @@ Copy literal word to word.
| word bar | word bar
| |
| routine main | define main routine
| outputs bar | outputs bar
| trashes a, n, z | trashes a, n, z
| { | {
@ -731,7 +753,7 @@ You can also copy a literal word to a word table.
| word table[256] many | word table[256] many
| |
| routine main | define main routine
| inputs many | inputs many
| outputs many | outputs many
| trashes a, x, n, z | trashes a, x, n, z
@ -751,7 +773,7 @@ Copy vector to vector.
| vector routine bar | vector routine bar
| vector routine baz | vector routine baz
| |
| routine main | define main routine
| inputs baz | inputs baz
| outputs bar | outputs bar
| trashes a, n, z | trashes a, n, z
@ -772,7 +794,7 @@ Copy routine to vector, inside an `interrupts off` block.
| trashes z, n | trashes z, n
| bar | bar
| |
| routine foo | define foo routine
| inputs x | inputs x
| outputs x | outputs x
| trashes z, n | trashes z, n
@ -780,7 +802,7 @@ Copy routine to vector, inside an `interrupts off` block.
| inc x | inc x
| } | }
| |
| routine main | define main routine
| outputs bar | outputs bar
| trashes a, n, z | trashes a, n, z
| { | {
@ -806,14 +828,14 @@ Copy routine (by forward reference) to vector.
| trashes z, n | trashes z, n
| bar | bar
| |
| routine main | define main routine
| outputs bar | outputs bar
| trashes a, n, z | trashes a, n, z
| { | {
| copy foo, bar | copy foo, bar
| } | }
| |
| routine foo | define foo routine
| inputs x | inputs x
| outputs x | outputs x
| trashes z, n | trashes z, n
@ -833,7 +855,7 @@ Copy word to word table and back, with both `x` and `y` as indexes.
| word one | word one
| word table[256] many | word table[256] many
| |
| routine main | define main routine
| inputs one, many | inputs one, many
| outputs one, many | outputs one, many
| trashes a, x, y, n, z | trashes a, x, y, n, z
@ -877,14 +899,14 @@ Indirect call.
| trashes z, n | trashes z, n
| foo | foo
| |
| routine bar | define bar routine
| outputs x | outputs x
| trashes z, n | trashes z, n
| { | {
| ld x, 200 | ld x, 200
| } | }
| |
| routine main | define main routine
| outputs x, foo | outputs x, foo
| trashes a, z, n | trashes a, z, n
| { | {
@ -904,7 +926,7 @@ Indirect call.
Compiling `goto`. Note that no `RTS` is emitted after the `JMP`. Compiling `goto`. Note that no `RTS` is emitted after the `JMP`.
| routine bar | define bar routine
| inputs y | inputs y
| outputs x, y | outputs x, y
| trashes z, n | trashes z, n
@ -912,7 +934,7 @@ Compiling `goto`. Note that no `RTS` is emitted after the `JMP`.
| ld x, 200 | ld x, 200
| } | }
| |
| routine main | define main routine
| outputs x, y | outputs x, y
| trashes a, z, n | trashes a, z, n
| { | {
@ -937,11 +959,11 @@ Copying to and from a vector table.
| trashes a, z, n | trashes a, z, n
| table[256] many | table[256] many
| |
| routine bar outputs x trashes a, z, n { | define bar routine outputs x trashes a, z, n {
| ld x, 200 | ld x, 200
| } | }
| |
| routine main | define main routine
| inputs one, many | inputs one, many
| outputs one, many | outputs one, many
| trashes a, x, n, z | trashes a, x, n, z
@ -982,7 +1004,7 @@ Copying to and from a vector table.
Adding a constant word to a word memory location. Adding a constant word to a word memory location.
| word score | word score
| routine main | define main routine
| inputs score | inputs score
| outputs score | outputs score
| trashes a, c, z, v, n | trashes a, c, z, v, n
@ -1003,7 +1025,7 @@ Adding a word memory location to another word memory location.
| word score | word score
| word delta | word delta
| routine main | define main routine
| inputs score, delta | inputs score, delta
| outputs score | outputs score
| trashes a, c, z, v, n | trashes a, c, z, v, n
@ -1023,7 +1045,7 @@ Adding a word memory location to another word memory location.
Subtracting a constant word from a word memory location. Subtracting a constant word from a word memory location.
| word score | word score
| routine main | define main routine
| inputs score | inputs score
| outputs score | outputs score
| trashes a, c, z, v, n | trashes a, c, z, v, n
@ -1044,7 +1066,7 @@ Subtracting a word memory location from another word memory location.
| word score | word score
| word delta | word delta
| routine main | define main routine
| inputs score, delta | inputs score, delta
| outputs score | outputs score
| trashes a, c, z, v, n | trashes a, c, z, v, n
@ -1068,7 +1090,7 @@ Load address into pointer.
| buffer[2048] buf | buffer[2048] buf
| pointer ptr @ 254 | pointer ptr @ 254
| |
| routine main | define main routine
| inputs buf | inputs buf
| outputs buf, y | outputs buf, y
| trashes a, z, n, ptr | trashes a, z, n, ptr
@ -1088,7 +1110,7 @@ Write literal through a pointer.
| buffer[2048] buf | buffer[2048] buf
| pointer ptr @ 254 | pointer ptr @ 254
| |
| routine main | define main routine
| inputs buf | inputs buf
| outputs buf, y | outputs buf, y
| trashes a, z, n, ptr | trashes a, z, n, ptr
@ -1112,7 +1134,7 @@ Write stored value through a pointer.
| pointer ptr @ 254 | pointer ptr @ 254
| byte foo | byte foo
| |
| routine main | define main routine
| inputs foo, buf | inputs foo, buf
| outputs y, buf | outputs y, buf
| trashes a, z, n, ptr | trashes a, z, n, ptr
@ -1136,7 +1158,7 @@ Read through a pointer, into a byte storage location, or the `a` register.
| pointer ptr @ 254 | pointer ptr @ 254
| byte foo | byte foo
| |
| routine main | define main routine
| inputs buf | inputs buf
| outputs y, foo | outputs y, foo
| trashes a, z, n, ptr | trashes a, z, n, ptr
@ -1162,7 +1184,7 @@ Read and write through two pointers.
| pointer ptra @ 252 | pointer ptra @ 252
| pointer ptrb @ 254 | pointer ptrb @ 254
| |
| routine main | define main routine
| inputs buf | inputs buf
| outputs buf | outputs buf
| trashes a, y, z, n, ptra, ptrb | trashes a, y, z, n, ptra, ptrb
@ -1191,7 +1213,7 @@ Write the `a` register through a pointer.
| pointer ptr @ 254 | pointer ptr @ 254
| byte foo | byte foo
| |
| routine main | define main routine
| inputs buf | inputs buf
| outputs buf | outputs buf
| trashes a, y, z, n, ptr | trashes a, y, z, n, ptr
@ -1218,7 +1240,7 @@ Note that this is *not* range-checked. (Yet.)
| byte foo | byte foo
| word delta | word delta
| |
| routine main | define main routine
| inputs buf | inputs buf
| outputs y, foo, delta | outputs y, foo, delta
| trashes a, c, v, z, n, ptr | trashes a, c, v, z, n, ptr
@ -1261,7 +1283,7 @@ Note that this is *not* range-checked. (Yet.)
Trash does nothing except indicate that we do not care about the value anymore. Trash does nothing except indicate that we do not care about the value anymore.
| routine main | define main routine
| inputs a | inputs a
| outputs x | outputs x
| trashes a, z, n | trashes a, z, n

View File

@ -48,7 +48,7 @@ Treat R as a mutable set and start with an empty list of lists L. Then,
- Remove all elements occurring in C, from R. - Remove all elements occurring in C, from R.
- Repeat until R is empty. - Repeat until R is empty.
When times comes to generate code, generate it in the order given by L. When time comes to generate code, generate it in the order given by L.
In addition, each sublist in L represents a number of routines to In addition, each sublist in L represents a number of routines to
generate; all except the final routine in such a sublist need not have generate; all except the final routine in such a sublist need not have
any jump instruction generated for its final `goto`. any jump instruction generated for its final `goto`.
@ -65,7 +65,7 @@ to pass these tests to be considered an implementation of SixtyPical.
-> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)" -> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)"
-> Functionality "Compile SixtyPical program with fallthru optimization" is implemented by -> Functionality "Compile SixtyPical program with fallthru optimization" is implemented by
-> shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo" -> shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
-> Tests for functionality "Dump fallthru info for SixtyPical program" -> Tests for functionality "Dump fallthru info for SixtyPical program"
@ -174,8 +174,8 @@ fall through to the other.
= "main" = "main"
= ], = ],
= [ = [
= "bar", = "foo",
= "foo" = "bar"
= ] = ]
= ] = ]
@ -206,10 +206,10 @@ routine.
= "main" = "main"
= ], = ],
= [ = [
= "bar" = "foo"
= ], = ],
= [ = [
= "foo" = "bar"
= ] = ]
= ] = ]
@ -271,10 +271,10 @@ because we don't necessarily know what actual routine the vector contains.
= "main" = "main"
= ], = ],
= [ = [
= "bar" = "foo"
= ], = ],
= [ = [
= "foo" = "bar"
= ] = ]
= ] = ]
@ -416,12 +416,12 @@ in the "true" branch is a `goto`.
| { | {
| } | }
= $080D RTS = $080D RTS
= $080E LDA #$FF = $080E LDA #$00
= $0810 RTS = $0810 BNE $081A
= $0811 LDA #$00 = $0812 LDA #$01
= $0813 BNE $081D = $0814 JMP $081F
= $0815 LDA #$01 = $0817 JMP $081F
= $0817 JMP $080E = $081A LDA #$02
= $081A JMP $0822 = $081C JMP $080D
= $081D LDA #$02 = $081F LDA #$FF
= $081F JMP $080D = $0821 RTS

View File

@ -16,7 +16,7 @@ but not necessarily sensible programs.
Rudimentary program. Rudimentary program.
| routine main { | define main routine {
| ld a, 0 | ld a, 0
| add a, 1 | add a, 1
| } | }
@ -26,7 +26,7 @@ Program with comments.
| // Welcome to my program. | // Welcome to my program.
| |
| routine main { | define main routine {
| ld a, 0 | ld a, 0
| add a, 1 // We are adding the thing. | add a, 1 // We are adding the thing.
| sub a, 1 | sub a, 1
@ -40,7 +40,7 @@ Program with comments.
Hex literals. Hex literals.
| routine main { | define main routine {
| ld a, $ff | ld a, $ff
| add a, $01 | add a, $01
| } | }
@ -48,7 +48,7 @@ Hex literals.
Syntax error. Syntax error.
| routine foo ( | define foo routine (
| ld a, 0 | ld a, 0
| add a, 1 | add a, 1
| ) | )
@ -65,12 +65,12 @@ Another syntax error.
Extern routines Extern routines
| routine chrout | define chrout routine
| inputs a | inputs a
| trashes a | trashes a
| @ 65490 | @ 65490
| |
| routine chrin | define chrin routine
| outputs a | outputs a
| trashes x | trashes x
| @ 65487 | @ 65487
@ -78,7 +78,7 @@ Extern routines
Trash. Trash.
| routine main { | define main routine {
| trash a | trash a
| trash n | trash n
| } | }
@ -86,7 +86,7 @@ Trash.
`nop`. `nop`.
| routine main | define main routine
| { | {
| nop | nop
| } | }
@ -94,7 +94,7 @@ Trash.
If with not If with not
| routine foo { | define foo routine {
| ld y, 0 | ld y, 0
| cmp y, 10 | cmp y, 10
| if not z { | if not z {
@ -106,7 +106,7 @@ If with not
Repeat loop Repeat loop
| routine foo { | define foo routine {
| ld y, 0 | ld y, 0
| repeat { | repeat {
| inc y | inc y
@ -117,7 +117,7 @@ Repeat loop
"While" loop "While" loop
| routine foo inputs y { | define foo routine inputs y {
| repeat { | repeat {
| cmp y, 10 | cmp y, 10
| if not z { | if not z {
@ -129,7 +129,7 @@ Repeat loop
Repeat forever Repeat forever
| routine foo inputs y { | define foo routine inputs y {
| repeat { | repeat {
| inc y | inc y
| } forever | } forever
@ -138,7 +138,7 @@ Repeat forever
Repeat with not Repeat with not
| routine foo inputs y { | define foo routine inputs y {
| repeat { | repeat {
| inc y | inc y
| } until not z | } until not z
@ -149,7 +149,7 @@ Basic "open-faced for" loops, up and down.
| byte table[256] tab | byte table[256] tab
| |
| routine foo trashes a, x, c, z, v { | define foo routine trashes a, x, c, z, v {
| ld x, 0 | ld x, 0
| for x up to 15 { | for x up to 15 {
| ld a, tab + x | ld a, tab + x
@ -163,7 +163,7 @@ Basic "open-faced for" loops, up and down.
Other blocks. Other blocks.
| routine main trashes a, x, c, z, v { | define main routine trashes a, x, c, z, v {
| with interrupts off { | with interrupts off {
| save a, x, c { | save a, x, c {
| ld a, 0 | ld a, 0
@ -183,7 +183,7 @@ User-defined memory addresses of different types.
| buffer[2048] buf | buffer[2048] buf
| pointer ptr | pointer ptr
| |
| routine main { | define main routine {
| } | }
= ok = ok
@ -193,7 +193,7 @@ Tables of different types and some operations on them.
| word table[256] wmany | word table[256] wmany
| vector (routine trashes a) table[256] vmany | vector (routine trashes a) table[256] vmany
| |
| routine main { | define main routine {
| ld x, 0 | ld x, 0
| ld a, 0 | ld a, 0
| st off, c | st off, c
@ -215,7 +215,7 @@ greater than 0 and less than or equal to 256.
| word table[512] many | word table[512] many
| |
| routine main | define main routine
| inputs many | inputs many
| outputs many | outputs many
| trashes a, x, n, z | trashes a, x, n, z
@ -227,7 +227,7 @@ greater than 0 and less than or equal to 256.
| word table[0] many | word table[0] many
| |
| routine main | define main routine
| inputs many | inputs many
| outputs many | outputs many
| trashes a, x, n, z | trashes a, x, n, z
@ -239,7 +239,7 @@ greater than 0 and less than or equal to 256.
| word table[48] many | word table[48] many
| |
| routine main | define main routine
| inputs many | inputs many
| outputs many | outputs many
| trashes a, x, n, z | trashes a, x, n, z
@ -256,7 +256,7 @@ Typedefs of different types.
| typedef routine trashes a game_routine | typedef routine trashes a game_routine
| vector game_routine start_game | vector game_routine start_game
| |
| routine main { | define main routine {
| } | }
= ok = ok
@ -265,7 +265,7 @@ Can't have two typedefs with the same name.
| typedef byte frank | typedef byte frank
| typedef word frank | typedef word frank
| |
| routine main { | define main routine {
| } | }
? SyntaxError ? SyntaxError
@ -280,7 +280,7 @@ Constants.
| |
| byte lark: lives | byte lark: lives
| |
| routine main { | define main routine {
| ld a, lives | ld a, lives
| } | }
= ok = ok
@ -290,7 +290,7 @@ Can't have two constants with the same name.
| const w1 1000 | const w1 1000
| const w1 word 0 | const w1 word 0
| |
| routine main { | define main routine {
| } | }
? SyntaxError ? SyntaxError
@ -298,7 +298,7 @@ Explicit memory address.
| byte screen @ 1024 | byte screen @ 1024
| |
| routine main { | define main routine {
| ld a, 100 | ld a, 100
| st a, screen | st a, screen
| shl screen | shl screen
@ -310,7 +310,7 @@ Initialized memory locations.
| byte lives : 3 | byte lives : 3
| |
| routine main { | define main routine {
| ld a, lives | ld a, lives
| st a, lives | st a, lives
| } | }
@ -320,7 +320,7 @@ Cannot have both initial value and explicit address.
| byte screen : 3 @ 1024 | byte screen : 3 @ 1024
| |
| routine main { | define main routine {
| ld a, lives | ld a, lives
| st a, lives | st a, lives
| } | }
@ -333,7 +333,7 @@ User-defined locations of other types.
| word r2 @ 60000 | word r2 @ 60000
| word r3 : 2000 | word r3 : 2000
| |
| routine main { | define main routine {
| } | }
= ok = ok
@ -341,15 +341,15 @@ Initialized byte table, initialized with ASCII string.
| byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?" | byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?"
| |
| routine main { | define main routine {
| } | }
= ok = ok
Can't initialize anything but a byte table with a string. Can't initialize anything but a byte table with a string.
| word message : "WHAT DO YOU WANT TO DO NEXT?" | word message : "OUCH! WHAT DO YOU DO?"
| |
| routine main { | define main routine {
| } | }
? SyntaxError ? SyntaxError
@ -357,13 +357,13 @@ Initialized byte table, initialized with list of bytes.
| byte table[8] charmap : 0, 255, 129, 192, 0, 1, 2, 4 | byte table[8] charmap : 0, 255, 129, 192, 0, 1, 2, 4
| |
| routine main { | define main routine {
| } | }
= ok = ok
Can't access an undeclared memory location. Can't access an undeclared memory location.
| routine main { | define main routine {
| ld a, 0 | ld a, 0
| st a, lives | st a, lives
| } | }
@ -374,7 +374,7 @@ Can't define two memory locations with the same name.
| byte lives | byte lives
| byte lives | byte lives
| |
| routine main { | define main routine {
| ld a, 0 | ld a, 0
| st a, lives | st a, lives
| } | }
@ -384,19 +384,19 @@ Can't shadow the name of a register or a flag.
| byte a | byte a
| |
| routine main { | define main routine {
| } | }
? SyntaxError ? SyntaxError
| byte z | byte z
| |
| routine main { | define main routine {
| } | }
? SyntaxError ? SyntaxError
Can't call routine that hasn't been defined. Can't call routine that hasn't been defined.
| routine main { | define main routine {
| ld x, 0 | ld x, 0
| ld y, 1 | ld y, 1
| call up | call up
@ -404,44 +404,26 @@ Can't call routine that hasn't been defined.
| } | }
? SyntaxError ? SyntaxError
And you can't call a non-routine.
| byte up
|
| routine main {
| ld x, 0
| ld y, 1
| call up
| }
? SyntaxError
| routine main {
| ld x, 0
| ld y, 1
| call x
| }
? SyntaxError
But you can call a routine that is yet to be defined, further on. But you can call a routine that is yet to be defined, further on.
| routine main { | define main routine {
| ld x, 0 | ld x, 0
| ld y, 1 | ld y, 1
| call up | call up
| call up | call up
| } | }
| routine up { | define up routine {
| ld a, 0 | ld a, 0
| } | }
= ok = ok
Can't define two routines with the same name. Can't define two routines with the same name.
| routine main { | define main routine {
| inc x | inc x
| inc y | inc y
| } | }
| routine main { | define main routine {
| ld x, 0 | ld x, 0
| ld y, 1 | ld y, 1
| } | }
@ -451,7 +433,7 @@ Declaring byte and word table memory location.
| byte table[256] tab | byte table[256] tab
| |
| routine main { | define main routine {
| ld x, 0 | ld x, 0
| ld y, 0 | ld y, 0
| ld a, tab + x | ld a, tab + x
@ -462,7 +444,7 @@ Declaring byte and word table memory location.
| word one | word one
| word table[256] many | word table[256] many
| |
| routine main { | define main routine {
| ld x, 0 | ld x, 0
| copy one, many + x | copy one, many + x
| copy word 0, many + x | copy word 0, many + x
@ -478,10 +460,10 @@ Declaring and calling a vector.
| trashes a, x, z, n | trashes a, x, z, n
| cinv @ 788 | cinv @ 788
| |
| routine foo { | define foo routine {
| ld a, 0 | ld a, 0
| } | }
| routine main { | define main routine {
| with interrupts off { | with interrupts off {
| copy foo, cinv | copy foo, cinv
| } | }
@ -497,7 +479,7 @@ Only vectors can be decorated with constraints like that.
| trashes a, x, z, n | trashes a, x, z, n
| @ 788 | @ 788
| |
| routine main { | define main routine {
| } | }
? SyntaxError ? SyntaxError
@ -509,10 +491,10 @@ Constraints set may only contain labels.
| trashes a, x, z, n | trashes a, x, z, n
| cinv @ 788 | cinv @ 788
| |
| routine foo { | define foo routine {
| ld a, 0 | ld a, 0
| } | }
| routine main { | define main routine {
| with interrupts off { | with interrupts off {
| copy foo, cinv | copy foo, cinv
| } | }
@ -528,10 +510,10 @@ A vector can name itself in its inputs, outputs, and trashes.
| trashes a, x, z, n | trashes a, x, z, n
| cinv @ 788 | cinv @ 788
| |
| routine foo { | define foo routine {
| ld a, 0 | ld a, 0
| } | }
| routine main { | define main routine {
| with interrupts off { | with interrupts off {
| copy foo, cinv | copy foo, cinv
| } | }
@ -548,50 +530,43 @@ references in the source of a `copy` instruction.
| outputs cinv, x | outputs cinv, x
| trashes a, x, z, n | trashes a, x, z, n
| cinv @ 788 | cinv @ 788
| routine main { | define main routine {
| with interrupts off { | with interrupts off {
| copy foo, cinv | copy foo, cinv
| } | }
| call cinv | call cinv
| } | }
| routine foo { | define foo routine {
| ld a, 0 | ld a, 0
| } | }
= ok = ok
goto. goto.
| routine foo { | define foo routine {
| ld a, 0 | ld a, 0
| } | }
| routine main { | define main routine {
| goto foo | goto foo
| } | }
= ok = ok
| routine main { | define main routine {
| goto foo | goto foo
| } | }
| routine foo { | define foo routine {
| ld a, 0 | ld a, 0
| } | }
= ok = ok
| vector routine foo | vector routine foo
| |
| routine main { | define main routine {
| goto foo | goto foo
| } | }
= ok = ok
| routine main { | define main routine {
| goto foo
| }
? SyntaxError
| byte foo
|
| routine main {
| goto foo | goto foo
| } | }
? SyntaxError ? SyntaxError
@ -603,7 +578,7 @@ Buffers and pointers.
| pointer ptrb | pointer ptrb
| byte foo | byte foo
| |
| routine main { | define main routine {
| copy ^buf, ptr | copy ^buf, ptr
| copy 123, [ptr] + y | copy 123, [ptr] + y
| copy [ptr] + y, foo | copy [ptr] + y, foo
@ -629,7 +604,28 @@ Routines can be defined in a new style.
| inc x | inc x
| } | }
| |
| routine main | define main routine
| outputs vec
| trashes a, z, n
| {
| copy foo, vec
| }
= ok
| typedef routine
| inputs x
| outputs x
| trashes z, n
| routine_type
|
| vector routine_type vec
|
| define foo routine_type
| {
| inc x
| }
|
| define main routine
| outputs vec | outputs vec
| trashes a, z, n | trashes a, z, n
| { | {