mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-11-25 23:49:17 +00:00
commit
3cd28bdb3e
17
HISTORY.md
17
HISTORY.md
@ -1,6 +1,23 @@
|
||||
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
|
||||
----
|
||||
|
||||
|
160
README.md
160
README.md
@ -1,32 +1,38 @@
|
||||
SixtyPical
|
||||
==========
|
||||
|
||||
_Version 0.16. Work-in-progress, everything is subject to change._
|
||||
_Version 0.17. Work-in-progress, everything is subject to change._
|
||||
|
||||
**SixtyPical** is a 6502-like programming language with advanced
|
||||
static analysis.
|
||||
**SixtyPical** is a low-level programming language with advanced
|
||||
static analysis. Many of its primitive instructions resemble
|
||||
those of the 6502 CPU — in fact it is intended to be compiled to
|
||||
6502 machine code — but along with these 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
|
||||
in 6502 assembly (e.g. the programmer must choose the registers that
|
||||
values will be stored in) and is concomitantly easy for a compiler to
|
||||
translate it to 6502 machine language code.
|
||||
* You'd use assembly, but you don't want to spend hours
|
||||
debugging (say) a memory overrun that happened because of a
|
||||
ridiculous silly error.
|
||||
* You'd use C 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
|
||||
go through the program step by step, tracking not just the changes that
|
||||
happen during a _specific_ execution of the program, but _sets_ of changes
|
||||
that could _possibly_ happen in any run of the program. This lets us
|
||||
determine that certain things can never happen, which we can then formulate
|
||||
as safety checks.
|
||||
|
||||
In practice, this means it catches things like
|
||||
SixtyPical gives the programmer a coding regimen on par with assembly
|
||||
language in terms of size and hands-on-ness, but also able to catch
|
||||
many ridiculous silly errors at compile time, such as
|
||||
|
||||
* you forgot to clear carry before adding something to the accumulator
|
||||
* a subroutine that you call trashes a register you thought was preserved
|
||||
* a subroutine that you called trashes a register you thought it preserved
|
||||
* you tried to read or write a byte beyond the end of a byte array
|
||||
* you tried to write the address of something that was not a routine, to
|
||||
a jump vector
|
||||
|
||||
and suchlike. It also provides some convenient operations based on
|
||||
Many of these checks are done with _abstract interpretation_, where we
|
||||
go through the program step by step, tracking not just the changes that
|
||||
happen during a _specific_ execution of the program, but _sets_ of changes
|
||||
that could _possibly_ happen in any run of the program.
|
||||
|
||||
SixtyPical also provides some convenient operations based on
|
||||
machine-language programming idioms, such as
|
||||
|
||||
* copying values from one register to another (via a third register when
|
||||
@ -35,8 +41,15 @@ machine-language programming idioms, such as
|
||||
* explicit tail calls
|
||||
* indirect subroutine calls
|
||||
|
||||
The reference implementation can analyze and compile SixtyPical programs to
|
||||
6502 machine code.
|
||||
SixtyPical is defined by a specification document, a set of test cases,
|
||||
and a reference implementation written in Python 2. The reference
|
||||
implementation can analyze and compile SixtyPical programs to 6502 machine
|
||||
code, which can be run on several 6502-based 8-bit architectures:
|
||||
|
||||
* Commodore 64
|
||||
* Commodore VIC-20
|
||||
* Atari 2600 VCS
|
||||
* Apple II
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
@ -68,111 +81,4 @@ Documentation
|
||||
* [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md)
|
||||
* [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md)
|
||||
* [Output formats supported by `sixtypical`](doc/Output%20Formats.md)
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
### `low` and `high` address operators
|
||||
|
||||
To turn `word` type into `byte`.
|
||||
|
||||
Trying to remember if we have a compelling case for this or now. The best I can think
|
||||
of is for implementing 16-bit `cmp` in an efficient way. Maybe we should see if we
|
||||
can get by with 16-bit `cmp` instead though.
|
||||
|
||||
The problem is that once a byte is extracted, putting it back into a word is awkward.
|
||||
The address operators have to modify a destination in a special way. That is, when
|
||||
you say `st a, >word`, you are updating `word` to be `word & $ff | a << 8`, somelike.
|
||||
Is that consistent with `st`? Well, probably it is, but we have to explain it.
|
||||
It might make more sense, then, for it to be "part of the operation" instead of "part of
|
||||
the reference"; something like `st.hi x, word`; `st.lo y, word`. Dunno.
|
||||
|
||||
### Save multiple values in single block
|
||||
|
||||
As a shortcut for the idiom
|
||||
|
||||
save a { save var {
|
||||
...
|
||||
} }
|
||||
|
||||
allow
|
||||
|
||||
save a, var {
|
||||
...
|
||||
}
|
||||
|
||||
### Save values to other-than-the-stack
|
||||
|
||||
Allow
|
||||
|
||||
save a to temp_a {
|
||||
...
|
||||
}
|
||||
|
||||
Which uses some other storage location instead of the stack. A local static
|
||||
would be a good candidate for such.
|
||||
|
||||
### Make all symbols forward-referencable
|
||||
|
||||
Basically, don't do symbol-table lookups when parsing, but do have a more formal
|
||||
"symbol resolution" (linking) phase right after parsing.
|
||||
|
||||
### Associate each pointer with the buffer it points into
|
||||
|
||||
Check that the buffer being read or written to through pointer, appears in appropriate
|
||||
inputs or outputs set.
|
||||
|
||||
In the analysis, when we obtain a pointer, we need to record, in contect, what buffer
|
||||
that pointer came from.
|
||||
|
||||
When we write through that pointer, we need to set that buffer as written.
|
||||
|
||||
When we read through the pointer, we need to check that the buffer is readable.
|
||||
|
||||
### Table overlays
|
||||
|
||||
They are uninitialized, but the twist is, the address is a buffer that is
|
||||
an input to and/or output of the routine. So, they are defined (insofar
|
||||
as the buffer is defined.)
|
||||
|
||||
They are therefore a "view" of a section of a buffer.
|
||||
|
||||
This is slightly dangerous since it does permit aliases: the buffer and the
|
||||
table refer to the same memory.
|
||||
|
||||
Although, if they are `static`, you could say, in the routine in which they
|
||||
are `static`, as soon as you've established one, you can no longer use the
|
||||
buffer; and the ones you establish must be disjoint.
|
||||
|
||||
(That seems to be the most compelling case for restricting them to `static`.)
|
||||
|
||||
An alternative would be `static` pointers, which are currently not possible because
|
||||
pointers must be zero-page, thus `@`, thus uninitialized.
|
||||
|
||||
### Question "consistent initialization"
|
||||
|
||||
Question the value of the "consistent initialization" principle for `if` statement analysis.
|
||||
|
||||
Part of this is the trashes at the end; I think what it should be is that the trashes
|
||||
after the `if` is the union of the trashes in each of the branches; this would obviate the
|
||||
need to `trash` values explicitly, but if you tried to access them afterwards, it would still
|
||||
error.
|
||||
|
||||
### Tail-call optimization
|
||||
|
||||
More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot
|
||||
appear elsewhere.)
|
||||
|
||||
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can.
|
||||
The constraints should iron out the same both ways.
|
||||
|
||||
And - once we have this - why do we need `goto` to be in tail position, strictly?
|
||||
As long as the routine has consistent type context every place it exits, that should be fine.
|
||||
|
||||
### "Include" directives
|
||||
|
||||
Search a searchlist of include paths. And use them to make libraries of routines.
|
||||
|
||||
One such library routine might be an `interrupt routine` type for various architectures.
|
||||
Since "the supervisor" has stored values on the stack, we should be able to trash them
|
||||
with impunity, in such a routine.
|
||||
* [TODO](TODO.md)
|
||||
|
89
TODO.md
Normal file
89
TODO.md
Normal 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.
|
@ -18,25 +18,12 @@ from pprint import pprint
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from sixtypical.parser import Parser, ParsingContext
|
||||
from sixtypical.parser import Parser, ParsingContext, merge_programs
|
||||
from sixtypical.analyzer import Analyzer
|
||||
from sixtypical.emitter import Emitter, Byte, Word
|
||||
from sixtypical.outputter import outputter_class_for
|
||||
from sixtypical.compiler import Compiler
|
||||
|
||||
|
||||
def merge_programs(programs):
|
||||
"""Assumes that the programs do not have any conflicts."""
|
||||
|
||||
from sixtypical.ast import Program
|
||||
|
||||
full = Program(1, defns=[], routines=[])
|
||||
for p in programs:
|
||||
full.defns.extend(p.defns)
|
||||
full.routines.extend(p.routines)
|
||||
|
||||
return full
|
||||
|
||||
|
||||
def process_input_files(filenames, options):
|
||||
context = ParsingContext()
|
||||
|
||||
@ -68,7 +55,7 @@ def process_input_files(filenames, options):
|
||||
return
|
||||
if label:
|
||||
sys.stdout.write("*** {}:\n".format(label))
|
||||
sys.stdout.write(json.dumps(data, indent=4, sort_keys=True))
|
||||
sys.stdout.write(json.dumps(data, indent=4, sort_keys=True, separators=(',', ':')))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
fa = FallthruAnalyzer(debug=options.debug)
|
||||
@ -76,60 +63,26 @@ def process_input_files(filenames, options):
|
||||
compilation_roster = fa.serialize()
|
||||
dump(compilation_roster)
|
||||
|
||||
if options.analyze_only:
|
||||
if options.analyze_only or options.output is None:
|
||||
return
|
||||
|
||||
fh = sys.stdout
|
||||
|
||||
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))
|
||||
|
||||
start_addr = None
|
||||
if options.origin is not None:
|
||||
if options.origin.startswith('0x'):
|
||||
start_addr = int(options.origin, 16)
|
||||
else:
|
||||
start_addr = int(options.origin, 10)
|
||||
|
||||
# If we are outputting a .PRG, we output the load address first.
|
||||
# We don't use the Emitter for this b/c not part of addr space.
|
||||
if options.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'):
|
||||
fh.write(Word(start_addr).serialize(0))
|
||||
|
||||
emitter = Emitter(start_addr)
|
||||
for byte in prelude:
|
||||
emitter.emit(Byte(byte))
|
||||
compiler = Compiler(emitter)
|
||||
compiler.compile_program(program, compilation_roster=compilation_roster)
|
||||
|
||||
# If we are outputting a cartridge with boot and BRK address
|
||||
# at the end, pad to ROM size minus 4 bytes, and emit addresses.
|
||||
if options.output_format == 'atari2600-cart':
|
||||
emitter.pad_to_size(4096 - 4)
|
||||
emitter.emit(Word(start_addr))
|
||||
emitter.emit(Word(start_addr))
|
||||
|
||||
if options.debug:
|
||||
pprint(emitter.accum)
|
||||
else:
|
||||
emitter.serialize(fh)
|
||||
with open(options.output, 'wb') as fh:
|
||||
outputter = outputter_class_for(options.output_format)(fh, start_addr=start_addr)
|
||||
outputter.write_prelude()
|
||||
compiler = Compiler(outputter.emitter)
|
||||
compiler.compile_program(program, compilation_roster=compilation_roster)
|
||||
outputter.write_postlude()
|
||||
if options.debug:
|
||||
pprint(outputter.emitter)
|
||||
else:
|
||||
outputter.emitter.serialize_to(fh)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -140,6 +93,10 @@ if __name__ == '__main__':
|
||||
help="The SixtyPical source files to compile."
|
||||
)
|
||||
|
||||
argparser.add_argument(
|
||||
"--output", "-o", type=str, metavar='FILENAME',
|
||||
help="File to which generated 6502 code will be written."
|
||||
)
|
||||
argparser.add_argument(
|
||||
"--origin", type=str, default=None,
|
||||
help="Location in memory where the `main` routine will be "
|
||||
|
@ -8,8 +8,7 @@ Output Formats
|
||||
The file contains only the emitted bytes of the compiled SixtyPical
|
||||
program.
|
||||
|
||||
The default origin is $0000; it is not unlikely you will want to
|
||||
override this.
|
||||
The default origin is $0000; you will likely want to override this.
|
||||
|
||||
Note that the origin is not stored in the output file in this format;
|
||||
that information must be recorded separately.
|
||||
@ -20,8 +19,7 @@ The first two bytes of the file contain the origin address in
|
||||
little-endian format. The remainder of the file is the emitted bytes
|
||||
of the compiled SixtyPical program, starting at that origin.
|
||||
|
||||
The default origin is $C000; it is likely you will want to
|
||||
override this.
|
||||
The default origin is $C000; you will likely want override this.
|
||||
|
||||
This format coincides with Commodore's PRG format for disk files,
|
||||
thus its name.
|
||||
|
20
eg/apple2/lores.60p
Normal file
20
eg/apple2/lores.60p
Normal 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
19
eg/apple2/print.60p
Normal 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
|
||||
}
|
@ -33,9 +33,11 @@
|
||||
typedef routine
|
||||
inputs joy2, press_fire_msg, dispatch_game_state,
|
||||
actor_pos, actor_delta, actor_logic,
|
||||
player_died,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
outputs dispatch_game_state,
|
||||
actor_pos, actor_delta, actor_logic,
|
||||
player_died,
|
||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic
|
||||
game_state_routine
|
||||
@ -45,13 +47,13 @@ typedef routine
|
||||
//
|
||||
// Routines that conform to this type also follow this convention:
|
||||
//
|
||||
// Set carry if the player perished. Carry clear otherwise.
|
||||
// Set player_died to 1 if the player perished. Unchanged otherwise.
|
||||
//
|
||||
|
||||
typedef routine
|
||||
inputs pos, delta, joy2, screen
|
||||
outputs pos, delta, new_pos, screen, c
|
||||
trashes a, x, y, z, n, v, ptr
|
||||
inputs pos, delta, joy2, screen, player_died
|
||||
outputs pos, delta, new_pos, screen, player_died
|
||||
trashes a, x, y, z, n, v, c, ptr
|
||||
logic_routine
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
@ -87,6 +89,8 @@ word new_pos
|
||||
word table[256] actor_delta
|
||||
word delta
|
||||
|
||||
byte player_died
|
||||
|
||||
vector logic_routine table[256] actor_logic
|
||||
vector logic_routine dispatch_logic
|
||||
|
||||
@ -117,7 +121,7 @@ vector game_state_routine
|
||||
// Utility Routines
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
routine read_stick
|
||||
define read_stick routine
|
||||
inputs joy2
|
||||
outputs delta
|
||||
trashes a, x, z, n
|
||||
@ -182,7 +186,7 @@ define check_button routine
|
||||
}
|
||||
}
|
||||
|
||||
routine clear_screen
|
||||
define clear_screen routine
|
||||
outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
trashes a, y, c, n, z
|
||||
{
|
||||
@ -205,7 +209,7 @@ routine clear_screen
|
||||
} until z
|
||||
}
|
||||
|
||||
routine calculate_new_position
|
||||
define calculate_new_position routine
|
||||
inputs pos, delta
|
||||
outputs new_pos
|
||||
trashes a, c, n, z, v
|
||||
@ -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
|
||||
outputs actor_pos, actor_delta, actor_logic
|
||||
outputs actor_pos, actor_delta, actor_logic, player_died
|
||||
trashes pos, a, y, z, n, c, v
|
||||
{
|
||||
ld y, 0
|
||||
@ -259,9 +263,11 @@ routine init_game
|
||||
} until z
|
||||
|
||||
ld y, 0
|
||||
copy word 0, actor_pos + y
|
||||
copy word 40, actor_pos + y
|
||||
copy word 0, actor_delta + y
|
||||
copy player_logic, actor_logic + y
|
||||
|
||||
st y, player_died
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
@ -301,10 +307,9 @@ define player_logic logic_routine
|
||||
st off, c
|
||||
add ptr, pos
|
||||
copy 81, [ptr] + y
|
||||
|
||||
st off, c
|
||||
} else {
|
||||
st on, c
|
||||
ld a, 1
|
||||
st a, player_died
|
||||
}
|
||||
|
||||
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
||||
@ -314,8 +319,6 @@ define player_logic logic_routine
|
||||
trash ptr
|
||||
trash y
|
||||
trash v
|
||||
} else {
|
||||
st off, c
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,10 +354,6 @@ define enemy_logic logic_routine
|
||||
st off, c
|
||||
add ptr, pos
|
||||
copy 82, [ptr] + y
|
||||
|
||||
st off, c
|
||||
} else {
|
||||
st on, c
|
||||
}
|
||||
|
||||
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
||||
@ -373,8 +372,6 @@ define enemy_logic logic_routine
|
||||
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
|
||||
{
|
||||
ld x, 0
|
||||
st x, player_died
|
||||
for x up to 15 {
|
||||
copy actor_pos + x, pos
|
||||
copy actor_delta + x, delta
|
||||
@ -420,18 +418,19 @@ define game_state_play game_state_routine
|
||||
save x {
|
||||
copy actor_logic + x, dispatch_logic
|
||||
call dispatch_logic
|
||||
|
||||
if c {
|
||||
// Player died! Want no dead!
|
||||
call clear_screen
|
||||
copy game_state_game_over, dispatch_game_state
|
||||
}
|
||||
}
|
||||
|
||||
copy pos, actor_pos + x
|
||||
copy delta, actor_delta + x
|
||||
}
|
||||
|
||||
ld a, player_died
|
||||
if not z {
|
||||
// Player died! Want no dead!
|
||||
call clear_screen
|
||||
copy game_state_game_over, dispatch_game_state
|
||||
}
|
||||
|
||||
goto save_cinv
|
||||
}
|
||||
|
||||
@ -458,7 +457,7 @@ define our_cinv game_state_routine
|
||||
goto dispatch_game_state
|
||||
}
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs cinv
|
||||
outputs cinv, save_cinv, pos, dispatch_game_state,
|
||||
screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||
|
@ -3,7 +3,7 @@
|
||||
// Define where the screen starts in memory:
|
||||
byte table[256] screen @ 1024
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
// These are the values that will be written to by this routine:
|
||||
trashes a, x, z, n, screen
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ vector routine
|
||||
trashes z, n
|
||||
save_cinv
|
||||
|
||||
routine our_cinv
|
||||
define our_cinv routine
|
||||
inputs vic_border
|
||||
outputs vic_border
|
||||
trashes z, n
|
||||
@ -33,7 +33,7 @@ routine our_cinv
|
||||
goto save_cinv
|
||||
}
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs cinv
|
||||
outputs cinv, save_cinv
|
||||
trashes a, n, z
|
||||
|
@ -3,7 +3,7 @@ byte joy2 @ $dc00
|
||||
|
||||
word delta
|
||||
|
||||
routine read_stick
|
||||
define read_stick routine
|
||||
inputs joy2
|
||||
outputs delta
|
||||
trashes a, x, z, n
|
||||
@ -36,7 +36,7 @@ routine read_stick
|
||||
}
|
||||
}
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs joy2
|
||||
outputs delta
|
||||
trashes a, x, z, n, screen
|
||||
|
@ -49,7 +49,7 @@ byte scanline : 85 // %01010101
|
||||
// generating them as part of a SixtyPical program would not
|
||||
// be practical. So we just jump to this location instead.
|
||||
|
||||
routine pla_tay_pla_tax_pla_rti
|
||||
define pla_tay_pla_tax_pla_rti routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ $EA81
|
||||
|
@ -1,6 +1,6 @@
|
||||
byte screen @ 1024
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes a, z, n, screen
|
||||
{
|
||||
ld a, 83
|
||||
|
@ -1,4 +1,4 @@
|
||||
routine add_four
|
||||
define add_four routine
|
||||
inputs a
|
||||
outputs a
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
routine main
|
||||
define main routine
|
||||
inputs a
|
||||
outputs a
|
||||
trashes c, z, n, v
|
||||
|
@ -1,5 +1,5 @@
|
||||
word score
|
||||
routine main
|
||||
define main routine
|
||||
inputs score
|
||||
outputs score
|
||||
trashes a, c, z, v, n
|
||||
|
@ -2,7 +2,7 @@ buffer[2048] buf
|
||||
pointer ptr @ 254
|
||||
byte foo
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs buf
|
||||
outputs buf, y, foo
|
||||
trashes a, z, n, ptr
|
||||
|
@ -1,16 +1,16 @@
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine print
|
||||
define print routine
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 65
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes a, z, n
|
||||
{
|
||||
call print
|
||||
|
@ -1,9 +1,9 @@
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes a, x, y, z, n, c, v
|
||||
{
|
||||
ld a, 0
|
||||
|
@ -1,9 +1,9 @@
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes a, x, y, z, n, c, v
|
||||
{
|
||||
ld a, 0
|
||||
|
@ -1,7 +1,7 @@
|
||||
byte bar
|
||||
byte baz
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs baz
|
||||
outputs bar
|
||||
trashes a, n, z
|
||||
|
@ -1,9 +1,9 @@
|
||||
byte lives
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs lives
|
||||
outputs lives
|
||||
trashes a, x
|
||||
trashes a, x, z, n, c, v
|
||||
{
|
||||
ld a, 0
|
||||
st a, lives
|
||||
|
@ -1,4 +1,4 @@
|
||||
routine main
|
||||
define main routine
|
||||
trashes a, y, z, n, c
|
||||
{
|
||||
ld y, 65
|
||||
|
@ -1,14 +1,14 @@
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine bar trashes a, z, n {
|
||||
define bar routine trashes a, z, n {
|
||||
ld a, 66
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine main trashes a, z, n {
|
||||
define main routine trashes a, z, n {
|
||||
ld a, 65
|
||||
call chrout
|
||||
goto bar
|
||||
|
@ -1,6 +1,7 @@
|
||||
routine foo
|
||||
define main routine
|
||||
inputs a
|
||||
outputs a
|
||||
trashes z, n, c
|
||||
{
|
||||
cmp a, 42
|
||||
if z {
|
||||
|
@ -1,9 +1,9 @@
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes a, y, z, n, c
|
||||
{
|
||||
ld y, 65
|
||||
|
@ -1,11 +1,11 @@
|
||||
byte foo
|
||||
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine print
|
||||
define print routine
|
||||
inputs foo
|
||||
trashes a, z, n
|
||||
{
|
||||
@ -13,7 +13,7 @@ routine print
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes a, y, z, n, foo
|
||||
{
|
||||
ld y, 65
|
||||
|
@ -1,9 +1,9 @@
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs a
|
||||
trashes a, z, n
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
byte table[8] message : "WHAT?"
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs message
|
||||
outputs x, a, z, n
|
||||
{
|
||||
|
@ -1,19 +1,19 @@
|
||||
// This will not compile on its own, because there is no `main`.
|
||||
// But this and `vector-main.60p` together will compile.
|
||||
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine printa
|
||||
define printa routine
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 65
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine printb
|
||||
define printb routine
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 66
|
||||
|
@ -12,7 +12,7 @@ vector routine
|
||||
// call chrout
|
||||
// }
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes print, a, z, n
|
||||
{
|
||||
copy printa, print
|
||||
|
@ -11,26 +11,26 @@ vector (routine
|
||||
trashes a, z, n)
|
||||
table[32] vectors
|
||||
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine printa
|
||||
define printa routine
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 65
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine printb
|
||||
define printb routine
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 66
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs vectors
|
||||
outputs vectors
|
||||
trashes print, a, x, z, n, c
|
||||
|
@ -2,26 +2,26 @@ vector routine
|
||||
trashes a, z, n
|
||||
print
|
||||
|
||||
routine chrout
|
||||
define chrout routine
|
||||
inputs a
|
||||
trashes a
|
||||
@ 65490
|
||||
|
||||
routine printa
|
||||
define printa routine
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 65
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine printb
|
||||
define printb routine
|
||||
trashes a, z, n
|
||||
{
|
||||
ld a, 66
|
||||
call chrout
|
||||
}
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
trashes print, a, z, n
|
||||
{
|
||||
copy printa, print
|
||||
|
@ -1,7 +1,7 @@
|
||||
word one
|
||||
word table[256] many
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
inputs one, many
|
||||
outputs one, many
|
||||
trashes a, x, y, n, z
|
||||
|
@ -3,7 +3,7 @@
|
||||
// Define where the screen starts in memory:
|
||||
byte table[256] screen @ 7680
|
||||
|
||||
routine main
|
||||
define main routine
|
||||
// These are the values that will be written to by this routine:
|
||||
trashes a, x, z, n, screen
|
||||
{
|
||||
|
19
loadngo.sh
19
loadngo.sh
@ -1,6 +1,6 @@
|
||||
#!/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"
|
||||
shift 1
|
||||
@ -21,6 +21,21 @@ elif [ "X$arch" = "Xvic20" ]; then
|
||||
elif [ "X$arch" = "Xatari2600" ]; then
|
||||
output_format='atari2600-cart'
|
||||
emu='stella'
|
||||
elif [ "X$arch" = "Xapple2" ]; then
|
||||
src="$1"
|
||||
out=/tmp/a-out.bin
|
||||
bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src --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
|
||||
echo $usage && exit 1
|
||||
fi
|
||||
@ -38,7 +53,7 @@ fi
|
||||
### do it ###
|
||||
|
||||
out=/tmp/a-out.prg
|
||||
bin/sixtypical --traceback --output-format=$output_format $src > $out || exit 1
|
||||
bin/sixtypical --traceback --output-format=$output_format $src --output $out || exit 1
|
||||
ls -la $out
|
||||
$emu $out
|
||||
rm -f $out
|
||||
|
@ -1,6 +1,6 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save
|
||||
from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save
|
||||
from sixtypical.model import (
|
||||
TYPE_BYTE, TYPE_WORD,
|
||||
TableType, BufferType, PointerType, VectorType, RoutineType,
|
||||
@ -372,23 +372,23 @@ class Analyzer(object):
|
||||
context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes)
|
||||
|
||||
if self.debug:
|
||||
print "at start of routine `{}`:".format(routine.name)
|
||||
print context
|
||||
print("at start of routine `{}`:".format(routine.name))
|
||||
print(context)
|
||||
|
||||
self.analyze_block(routine.block, context)
|
||||
trashed = set(context.each_touched()) - set(context.each_meaningful())
|
||||
|
||||
if self.debug:
|
||||
print "at end of routine `{}`:".format(routine.name)
|
||||
print context
|
||||
print "trashed: ", LocationRef.format_set(trashed)
|
||||
print "outputs: ", LocationRef.format_set(type_.outputs)
|
||||
print("at end of routine `{}`:".format(routine.name))
|
||||
print(context)
|
||||
print("trashed: ", LocationRef.format_set(trashed))
|
||||
print("outputs: ", LocationRef.format_set(type_.outputs))
|
||||
trashed_outputs = type_.outputs & trashed
|
||||
if trashed_outputs:
|
||||
print "TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs)
|
||||
print ''
|
||||
print '-' * 79
|
||||
print ''
|
||||
print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs))
|
||||
print('')
|
||||
print('-' * 79)
|
||||
print('')
|
||||
|
||||
# even if we goto another routine, we can't trash an output.
|
||||
for ref in trashed:
|
||||
@ -550,6 +550,8 @@ class Analyzer(object):
|
||||
context.invalidate_range(dest)
|
||||
elif opcode == 'call':
|
||||
type = instr.location.type
|
||||
if not isinstance(type, (RoutineType, VectorType)):
|
||||
raise TypeMismatchError(instr, instr.location)
|
||||
if isinstance(type, VectorType):
|
||||
type = type.of_type
|
||||
for ref in type.inputs:
|
||||
@ -771,17 +773,22 @@ class Analyzer(object):
|
||||
context.set_writeable(instr.dest)
|
||||
|
||||
def analyze_save(self, instr, context):
|
||||
if len(instr.locations) != 1:
|
||||
raise NotImplementedError("Only 1 location in save is supported right now")
|
||||
location = instr.locations[0]
|
||||
self.assert_type(TYPE_BYTE, location)
|
||||
batons = []
|
||||
for location in instr.locations:
|
||||
self.assert_type(TYPE_BYTE, location)
|
||||
baton = context.extract(location)
|
||||
batons.append(baton)
|
||||
|
||||
baton = context.extract(location)
|
||||
self.analyze_block(instr.block, context)
|
||||
if context.encountered_gotos():
|
||||
raise IllegalJumpError(instr, instr)
|
||||
context.re_introduce(baton)
|
||||
|
||||
for location in reversed(instr.locations):
|
||||
baton = batons.pop()
|
||||
context.re_introduce(baton)
|
||||
|
||||
# We do this check outside the loop, because A is only preserved
|
||||
# if it is the outermost thing being `save`d.
|
||||
if location == REG_A:
|
||||
pass
|
||||
else:
|
||||
|
@ -37,14 +37,16 @@ class AST(object):
|
||||
def all_children(self):
|
||||
for attr in self.children_attrs:
|
||||
for child in self.attrs[attr]:
|
||||
if child is not None:
|
||||
yield child
|
||||
for subchild in child.all_children():
|
||||
yield subchild
|
||||
for attr in self.child_attrs:
|
||||
child = self.attrs[attr]
|
||||
if child is not None:
|
||||
yield child
|
||||
for subchild in child.all_children():
|
||||
yield subchild
|
||||
for attr in self.child_attrs:
|
||||
child = self.attrs[attr]
|
||||
yield child
|
||||
for subchild in child.all_children():
|
||||
yield subchild
|
||||
|
||||
|
||||
class Program(AST):
|
||||
|
@ -1,6 +1,6 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save
|
||||
from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save
|
||||
from sixtypical.model import (
|
||||
ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
|
||||
TYPE_BIT, TYPE_BYTE, TYPE_WORD,
|
||||
@ -112,7 +112,7 @@ class Compiler(object):
|
||||
routine_name = roster_row[-1]
|
||||
self.compile_routine(self.routines[routine_name])
|
||||
|
||||
for location, label in self.trampolines.iteritems():
|
||||
for location, label in self.trampolines.items():
|
||||
self.emitter.resolve_label(label)
|
||||
self.emitter.emit(JMP(Indirect(self.get_label(location.name))))
|
||||
self.emitter.emit(RTS())
|
||||
@ -618,27 +618,32 @@ class Compiler(object):
|
||||
self.emitter.emit(CLI())
|
||||
|
||||
def compile_save(self, instr):
|
||||
location = instr.locations[0]
|
||||
if location == REG_A:
|
||||
self.emitter.emit(PHA())
|
||||
self.compile_block(instr.block)
|
||||
self.emitter.emit(PLA())
|
||||
elif location == REG_X:
|
||||
self.emitter.emit(TXA())
|
||||
self.emitter.emit(PHA())
|
||||
self.compile_block(instr.block)
|
||||
self.emitter.emit(PLA())
|
||||
self.emitter.emit(TAX())
|
||||
elif location == REG_Y:
|
||||
self.emitter.emit(TYA())
|
||||
self.emitter.emit(PHA())
|
||||
self.compile_block(instr.block)
|
||||
self.emitter.emit(PLA())
|
||||
self.emitter.emit(TAY())
|
||||
else:
|
||||
src_label = self.get_label(location.name)
|
||||
self.emitter.emit(LDA(Absolute(src_label)))
|
||||
self.emitter.emit(PHA())
|
||||
self.compile_block(instr.block)
|
||||
self.emitter.emit(PLA())
|
||||
self.emitter.emit(STA(Absolute(src_label)))
|
||||
for location in instr.locations:
|
||||
if location == REG_A:
|
||||
self.emitter.emit(PHA())
|
||||
elif location == REG_X:
|
||||
self.emitter.emit(TXA())
|
||||
self.emitter.emit(PHA())
|
||||
elif location == REG_Y:
|
||||
self.emitter.emit(TYA())
|
||||
self.emitter.emit(PHA())
|
||||
else:
|
||||
src_label = self.get_label(location.name)
|
||||
self.emitter.emit(LDA(Absolute(src_label)))
|
||||
self.emitter.emit(PHA())
|
||||
|
||||
self.compile_block(instr.block)
|
||||
|
||||
for location in reversed(instr.locations):
|
||||
if location == REG_A:
|
||||
self.emitter.emit(PLA())
|
||||
elif location == REG_X:
|
||||
self.emitter.emit(PLA())
|
||||
self.emitter.emit(TAX())
|
||||
elif location == REG_Y:
|
||||
self.emitter.emit(PLA())
|
||||
self.emitter.emit(TAY())
|
||||
else:
|
||||
src_label = self.get_label(location.name)
|
||||
self.emitter.emit(PLA())
|
||||
self.emitter.emit(STA(Absolute(src_label)))
|
||||
|
@ -8,12 +8,15 @@ class Emittable(object):
|
||||
raise NotImplementedError
|
||||
|
||||
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
|
||||
|
||||
|
||||
class Byte(Emittable):
|
||||
def __init__(self, value):
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, str):
|
||||
value = ord(value)
|
||||
if value < -127 or value > 255:
|
||||
raise IndexError(value)
|
||||
@ -24,8 +27,8 @@ class Byte(Emittable):
|
||||
def size(self):
|
||||
return 1
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return chr(self.value)
|
||||
def serialize(self, addr):
|
||||
return [self.value]
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (self.__class__.__name__, self.value)
|
||||
@ -39,11 +42,11 @@ class Word(Emittable):
|
||||
def size(self):
|
||||
return 2
|
||||
|
||||
def serialize(self, addr=None):
|
||||
def serialize(self, addr):
|
||||
word = self.value
|
||||
low = word & 255
|
||||
high = (word >> 8) & 255
|
||||
return chr(low) + chr(high)
|
||||
return [low, high]
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (self.__class__.__name__, self.value)
|
||||
@ -59,10 +62,12 @@ class Table(Emittable):
|
||||
def size(self):
|
||||
return self._size
|
||||
|
||||
def serialize(self, addr=None):
|
||||
buf = ''.join([emittable.serialize() for emittable in self.value])
|
||||
def serialize(self, addr):
|
||||
buf = []
|
||||
for emittable in self.value:
|
||||
buf.extend(emittable.serialize(addr)) # FIXME: addr + offset
|
||||
while len(buf) < self.size():
|
||||
buf += chr(0)
|
||||
buf.append(0)
|
||||
return buf
|
||||
|
||||
def __repr__(self):
|
||||
@ -84,17 +89,17 @@ class Label(Emittable):
|
||||
def size(self):
|
||||
return 2
|
||||
|
||||
def serialize(self, addr=None, offset=0):
|
||||
def serialize(self, addr, offset=0):
|
||||
assert self.addr is not None, "unresolved label: %s" % self.name
|
||||
return Word(self.addr + offset).serialize()
|
||||
return Word(self.addr + offset).serialize(addr)
|
||||
|
||||
def serialize_relative_to(self, addr):
|
||||
assert self.addr is not None, "unresolved label: %s" % self.name
|
||||
return Byte(self.addr - (addr + 2)).serialize()
|
||||
return Byte(self.addr - (addr + 2)).serialize(addr)
|
||||
|
||||
def serialize_as_zero_page(self, offset=0):
|
||||
def serialize_as_zero_page(self, addr, offset=0):
|
||||
assert self.addr is not None, "unresolved label: %s" % self.name
|
||||
return Byte(self.addr + offset).serialize()
|
||||
return Byte(self.addr + offset).serialize(addr)
|
||||
|
||||
def __repr__(self):
|
||||
addr_s = ', addr=%r' % self.addr if self.addr is not None else ''
|
||||
@ -111,11 +116,11 @@ class Offset(Emittable):
|
||||
def size(self):
|
||||
self.label.size()
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return self.label.serialize(offset=self.offset)
|
||||
def serialize(self, addr):
|
||||
return self.label.serialize(addr, offset=self.offset)
|
||||
|
||||
def serialize_as_zero_page(self, offset=0):
|
||||
return self.label.serialize_as_zero_page(offset=self.offset)
|
||||
def serialize_as_zero_page(self, addr, offset=0):
|
||||
return self.label.serialize_as_zero_page(addr, offset=self.offset)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset)
|
||||
@ -129,8 +134,8 @@ class HighAddressByte(Emittable):
|
||||
def size(self):
|
||||
return 1
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return self.label.serialize()[0]
|
||||
def serialize(self, addr):
|
||||
return [self.label.serialize(addr)[0]]
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (self.__class__.__name__, self.label)
|
||||
@ -144,8 +149,8 @@ class LowAddressByte(Emittable):
|
||||
def size(self):
|
||||
return 1
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return self.label.serialize()[1]
|
||||
def serialize(self, addr):
|
||||
return [self.label.serialize(addr)[1]]
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (self.__class__.__name__, self.label)
|
||||
@ -166,11 +171,12 @@ class Emitter(object):
|
||||
self.accum.append(thing)
|
||||
self.addr += thing.size()
|
||||
|
||||
def serialize(self, stream):
|
||||
def serialize_to(self, stream):
|
||||
"""`stream` should be a file opened in binary mode."""
|
||||
addr = self.start_addr
|
||||
for emittable in self.accum:
|
||||
chunk = emittable.serialize(addr)
|
||||
stream.write(chunk)
|
||||
stream.write(bytearray(chunk))
|
||||
addr += len(chunk)
|
||||
|
||||
def make_label(self, name=None):
|
||||
|
@ -43,7 +43,7 @@ class FallthruAnalyzer(object):
|
||||
|
||||
while pending_routines:
|
||||
chains = [self.find_chain(k, pending_routines) for k in pending_routines.keys()]
|
||||
chains.sort(key=len, reverse=True)
|
||||
chains.sort(key=lambda x: (len(x), str(x)), reverse=True)
|
||||
c = chains[0]
|
||||
roster.append(c)
|
||||
for k in c:
|
||||
|
@ -8,7 +8,7 @@ class AddressingMode(Emittable):
|
||||
"""Size of the operand for the mode (not including the opcode)"""
|
||||
raise NotImplementedError
|
||||
|
||||
def serialize(self, addr=None):
|
||||
def serialize(self, addr):
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self):
|
||||
@ -19,8 +19,8 @@ class Implied(AddressingMode):
|
||||
def size(self):
|
||||
return 0
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return ''
|
||||
def serialize(self, addr):
|
||||
return []
|
||||
|
||||
def __repr__(self):
|
||||
return "%s()" % (self.__class__.__name__)
|
||||
@ -34,8 +34,8 @@ class Immediate(AddressingMode):
|
||||
def size(self):
|
||||
return 1
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return self.value.serialize()
|
||||
def serialize(self, addr):
|
||||
return self.value.serialize(addr)
|
||||
|
||||
|
||||
class Absolute(AddressingMode):
|
||||
@ -46,8 +46,8 @@ class Absolute(AddressingMode):
|
||||
def size(self):
|
||||
return 2
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return self.value.serialize()
|
||||
def serialize(self, addr):
|
||||
return self.value.serialize(addr)
|
||||
|
||||
|
||||
class AbsoluteX(Absolute):
|
||||
@ -66,8 +66,8 @@ class ZeroPage(AddressingMode):
|
||||
def size(self):
|
||||
return 1
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return self.value.serialize_as_zero_page()
|
||||
def serialize(self, addr):
|
||||
return self.value.serialize_as_zero_page(addr)
|
||||
|
||||
|
||||
class Indirect(AddressingMode):
|
||||
@ -78,8 +78,8 @@ class Indirect(AddressingMode):
|
||||
def size(self):
|
||||
return 2
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return self.value.serialize()
|
||||
def serialize(self, addr):
|
||||
return self.value.serialize(addr)
|
||||
|
||||
|
||||
class IndirectY(ZeroPage):
|
||||
@ -94,7 +94,7 @@ class Relative(AddressingMode):
|
||||
def size(self):
|
||||
return 1
|
||||
|
||||
def serialize(self, addr=None):
|
||||
def serialize(self, addr):
|
||||
return self.value.serialize_relative_to(addr)
|
||||
|
||||
|
||||
@ -108,11 +108,8 @@ class Instruction(Emittable):
|
||||
def size(self):
|
||||
return 1 + self.operand.size() if self.operand else 0
|
||||
|
||||
def serialize(self, addr=None):
|
||||
return (
|
||||
chr(self.opcodes[self.operand.__class__]) +
|
||||
self.operand.serialize(addr)
|
||||
)
|
||||
def serialize(self, addr):
|
||||
return [self.opcodes[self.operand.__class__]] + self.operand.serialize(addr)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (self.__class__.__name__, self.operand)
|
||||
|
@ -18,20 +18,6 @@ class Type(object):
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
def backpatch_constraint_labels(self, resolver):
|
||||
def resolve(w):
|
||||
if not isinstance(w, 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_BYTE = Type('byte', max_range=(0, 255))
|
||||
@ -41,11 +27,11 @@ TYPE_WORD = Type('word', max_range=(0, 65535))
|
||||
|
||||
class RoutineType(Type):
|
||||
"""This memory location contains the code for a routine."""
|
||||
def __init__(self, inputs=None, outputs=None, trashes=None):
|
||||
def __init__(self, inputs, outputs, trashes):
|
||||
self.name = 'routine'
|
||||
self.inputs = inputs or set()
|
||||
self.outputs = outputs or set()
|
||||
self.trashes = trashes or set()
|
||||
self.inputs = inputs
|
||||
self.outputs = outputs
|
||||
self.trashes = trashes
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % (
|
||||
@ -172,7 +158,7 @@ class LocationRef(Ref):
|
||||
|
||||
@classmethod
|
||||
def format_set(cls, location_refs):
|
||||
return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)])
|
||||
return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs, key=lambda x: x.name)])
|
||||
|
||||
|
||||
class IndirectRef(Ref):
|
||||
|
74
src/sixtypical/outputter.py
Normal file
74
src/sixtypical/outputter.py
Normal 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]
|
@ -18,6 +18,14 @@ class SymEntry(object):
|
||||
return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model)
|
||||
|
||||
|
||||
class ForwardReference(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (self.__class__.__name__, self.name)
|
||||
|
||||
|
||||
class ParsingContext(object):
|
||||
def __init__(self):
|
||||
self.symbols = {} # token -> SymEntry
|
||||
@ -33,7 +41,7 @@ class ParsingContext(object):
|
||||
def __str__(self):
|
||||
return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts)
|
||||
|
||||
def lookup(self, name):
|
||||
def fetch(self, name):
|
||||
if name in self.statics:
|
||||
return self.statics[name].model
|
||||
if name in self.symbols:
|
||||
@ -45,17 +53,65 @@ class Parser(object):
|
||||
def __init__(self, context, text, filename):
|
||||
self.context = context
|
||||
self.scanner = Scanner(text, filename)
|
||||
self.backpatch_instrs = []
|
||||
|
||||
def syntax_error(self, msg):
|
||||
self.scanner.syntax_error(msg)
|
||||
|
||||
def lookup(self, name):
|
||||
model = self.context.lookup(name)
|
||||
model = self.context.fetch(name)
|
||||
if model is None:
|
||||
self.syntax_error('Undefined symbol "{}"'.format(name))
|
||||
return model
|
||||
|
||||
def declare(self, name, symentry, 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
|
||||
|
||||
def program(self):
|
||||
@ -70,47 +126,19 @@ class Parser(object):
|
||||
typenames.extend(self.context.typedefs.keys())
|
||||
while self.scanner.on(*typenames):
|
||||
defn = self.defn()
|
||||
name = defn.name
|
||||
if self.context.lookup(name):
|
||||
self.syntax_error('Symbol "%s" already declared' % name)
|
||||
self.context.symbols[name] = SymEntry(defn, defn.location)
|
||||
self.declare(defn.name, SymEntry(defn, defn.location))
|
||||
defns.append(defn)
|
||||
while self.scanner.on('define', 'routine'):
|
||||
if self.scanner.consume('define'):
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
routine = self.routine(name)
|
||||
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)
|
||||
while self.scanner.consume('define'):
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
routine = self.routine(name)
|
||||
self.declare(name, SymEntry(routine, routine.location))
|
||||
routines.append(routine)
|
||||
self.scanner.check_type('EOF')
|
||||
|
||||
# now backpatch the executable types.
|
||||
#for type_name, type_ in self.context.typedefs.iteritems():
|
||||
# type_.backpatch_constraint_labels(lambda w: self.lookup(w))
|
||||
for defn in defns:
|
||||
defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
|
||||
for routine in routines:
|
||||
routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
|
||||
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)
|
||||
program = Program(self.scanner.line_number, defns=defns, routines=routines)
|
||||
self.resolve_symbols(program)
|
||||
return program
|
||||
|
||||
def typedef(self):
|
||||
self.scanner.expect('typedef')
|
||||
@ -250,27 +278,10 @@ class Parser(object):
|
||||
outputs = set(self.labels())
|
||||
if self.scanner.consume('trashes'):
|
||||
trashes = set(self.labels())
|
||||
return (inputs, outputs, trashes)
|
||||
|
||||
def legacy_routine(self):
|
||||
self.scanner.expect('routine')
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
(inputs, outputs, trashes) = self.constraints()
|
||||
type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
|
||||
if self.scanner.consume('@'):
|
||||
self.scanner.check_type('integer literal')
|
||||
block = None
|
||||
addr = int(self.scanner.token)
|
||||
self.scanner.scan()
|
||||
else:
|
||||
block = self.block()
|
||||
addr = None
|
||||
location = LocationRef(type_, name)
|
||||
return Routine(
|
||||
self.scanner.line_number,
|
||||
name=name, block=block, addr=addr,
|
||||
location=location
|
||||
return (
|
||||
set([ForwardReference(n) for n in inputs]),
|
||||
set([ForwardReference(n) for n in outputs]),
|
||||
set([ForwardReference(n) for n in trashes])
|
||||
)
|
||||
|
||||
def routine(self, name):
|
||||
@ -286,9 +297,11 @@ class Parser(object):
|
||||
else:
|
||||
statics = self.statics()
|
||||
|
||||
self.context.statics = self.compose_statics_dict(statics)
|
||||
self.clear_statics()
|
||||
for defn in statics:
|
||||
self.declare(defn.name, SymEntry(defn, defn.location), static=True)
|
||||
block = self.block()
|
||||
self.context.statics = {}
|
||||
self.clear_statics()
|
||||
|
||||
addr = None
|
||||
location = LocationRef(type_, name)
|
||||
@ -298,15 +311,6 @@ class Parser(object):
|
||||
location=location, statics=statics
|
||||
)
|
||||
|
||||
def compose_statics_dict(self, statics):
|
||||
c = {}
|
||||
for defn in statics:
|
||||
name = defn.name
|
||||
if self.context.lookup(name):
|
||||
self.syntax_error('Symbol "%s" already declared' % name)
|
||||
c[name] = SymEntry(defn, defn.location)
|
||||
return c
|
||||
|
||||
def labels(self):
|
||||
accum = []
|
||||
accum.append(self.label())
|
||||
@ -328,23 +332,19 @@ class Parser(object):
|
||||
accum.append(self.locexpr())
|
||||
return accum
|
||||
|
||||
def locexpr(self, forward=False):
|
||||
def locexpr(self):
|
||||
if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'):
|
||||
return self.const()
|
||||
elif forward:
|
||||
else:
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
loc = self.context.lookup(name)
|
||||
if loc is not None:
|
||||
loc = self.context.fetch(name)
|
||||
if loc:
|
||||
return loc
|
||||
else:
|
||||
return name
|
||||
else:
|
||||
loc = self.lookup(self.scanner.token)
|
||||
self.scanner.scan()
|
||||
return loc
|
||||
return ForwardReference(name)
|
||||
|
||||
def indlocexpr(self, forward=False):
|
||||
def indlocexpr(self):
|
||||
if self.scanner.consume('['):
|
||||
loc = self.locexpr()
|
||||
self.scanner.expect(']')
|
||||
@ -355,11 +355,11 @@ class Parser(object):
|
||||
loc = self.locexpr()
|
||||
return AddressRef(loc)
|
||||
else:
|
||||
return self.indexed_locexpr(forward=forward)
|
||||
return self.indexed_locexpr()
|
||||
|
||||
def indexed_locexpr(self, forward=False):
|
||||
loc = self.locexpr(forward=forward)
|
||||
if not isinstance(loc, basestring):
|
||||
def indexed_locexpr(self):
|
||||
loc = self.locexpr()
|
||||
if not isinstance(loc, str):
|
||||
index = None
|
||||
if self.scanner.consume('+'):
|
||||
index = self.locexpr()
|
||||
@ -453,17 +453,15 @@ class Parser(object):
|
||||
self.scanner.scan()
|
||||
name = self.scanner.token
|
||||
self.scanner.scan()
|
||||
instr = SingleOp(self.scanner.line_number, opcode=opcode, location=name, dest=None, src=None)
|
||||
self.backpatch_instrs.append(instr)
|
||||
instr = SingleOp(self.scanner.line_number, opcode=opcode, location=ForwardReference(name), dest=None, src=None)
|
||||
return instr
|
||||
elif self.scanner.token in ("copy",):
|
||||
opcode = self.scanner.token
|
||||
self.scanner.scan()
|
||||
src = self.indlocexpr(forward=True)
|
||||
src = self.indlocexpr()
|
||||
self.scanner.expect(',')
|
||||
dest = self.indlocexpr()
|
||||
instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src)
|
||||
self.backpatch_instrs.append(instr)
|
||||
return instr
|
||||
elif self.scanner.consume("with"):
|
||||
self.scanner.expect("interrupts")
|
||||
@ -479,3 +477,17 @@ class Parser(object):
|
||||
return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest)
|
||||
else:
|
||||
self.syntax_error('bad opcode "%s"' % self.scanner.token)
|
||||
|
||||
|
||||
# - - - -
|
||||
|
||||
|
||||
def merge_programs(programs):
|
||||
"""Assumes that the programs do not have any conflicts."""
|
||||
|
||||
full = Program(1, defns=[], routines=[])
|
||||
for p in programs:
|
||||
full.defns.extend(p.defns)
|
||||
full.routines.extend(p.routines)
|
||||
|
||||
return full
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,20 +7,20 @@ SixtyPical to 6502 machine code.
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Compile SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter </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"
|
||||
|
||||
Null program.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| {
|
||||
| }
|
||||
= $080D RTS
|
||||
|
||||
`nop` program.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| {
|
||||
| nop
|
||||
| }
|
||||
@ -29,7 +29,7 @@ Null program.
|
||||
|
||||
Rudimentary program.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs a
|
||||
| outputs a
|
||||
| trashes c, z, n, v
|
||||
@ -43,12 +43,12 @@ Rudimentary program.
|
||||
|
||||
Call extern.
|
||||
|
||||
| routine chrout
|
||||
| define chrout routine
|
||||
| inputs a
|
||||
| trashes a
|
||||
| @ 65490
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs a
|
||||
| trashes a, z, n
|
||||
| {
|
||||
@ -61,7 +61,7 @@ Call extern.
|
||||
|
||||
Call defined routine.
|
||||
|
||||
| routine foo
|
||||
| define foo routine
|
||||
| outputs a, x, y
|
||||
| trashes z, n
|
||||
| {
|
||||
@ -70,7 +70,7 @@ Call defined routine.
|
||||
| ld y, 0
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, x, y, z, n
|
||||
| {
|
||||
| call foo
|
||||
@ -86,7 +86,7 @@ Access a defined memory location.
|
||||
|
||||
| byte foo
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, y, z, n, foo
|
||||
| {
|
||||
| ld y, 0
|
||||
@ -102,7 +102,7 @@ Memory location with explicit address.
|
||||
|
||||
| byte screen @ 1024
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, z, n, screen
|
||||
| {
|
||||
| ld a, 100
|
||||
@ -118,7 +118,7 @@ and `and`, `or`, and `xor` use zero-page addressing.
|
||||
| byte zp @ $00
|
||||
| byte screen @ 100
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs screen, zp
|
||||
| outputs screen, zp
|
||||
| trashes a, z, n
|
||||
@ -144,7 +144,7 @@ Memory location with initial value.
|
||||
|
||||
| byte lives : 3
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs lives
|
||||
| trashes a, z, n
|
||||
| {
|
||||
@ -159,7 +159,7 @@ Word memory locations with explicit address, initial value.
|
||||
| word w1 @ 60001
|
||||
| word w2 : 3003
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs w1
|
||||
| outputs w2
|
||||
| trashes a, z, n
|
||||
@ -178,7 +178,7 @@ Initialized byte table, initialized with ASCII string. Bytes allocated, but bey
|
||||
|
||||
| byte table[8] message : "WHAT?"
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs message
|
||||
| outputs x, a, z, n
|
||||
| {
|
||||
@ -200,7 +200,7 @@ Initialized byte table, initialized with list of byte values.
|
||||
|
||||
| byte table[8] message : 255, 0, 129, 128, 127
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs message
|
||||
| outputs x, a, z, n
|
||||
| {
|
||||
@ -222,7 +222,7 @@ Initialized word table, initialized with list of word values.
|
||||
|
||||
| word table[4] message : 65535, 0, 127, 127
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| {
|
||||
| }
|
||||
= $080D RTS
|
||||
@ -239,7 +239,7 @@ Some instructions.
|
||||
|
||||
| byte foo
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, x, y, z, n, c, v, foo
|
||||
| {
|
||||
| ld a, 0
|
||||
@ -317,7 +317,7 @@ Some instructions on tables. (1/3)
|
||||
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, c, n, z, v
|
||||
@ -341,7 +341,7 @@ Some instructions on tables. (2/3)
|
||||
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, c, n, z
|
||||
@ -363,7 +363,7 @@ Some instructions on tables. (3/3)
|
||||
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, c, n, z
|
||||
@ -387,7 +387,7 @@ Some instructions on tables. (3/3)
|
||||
|
||||
Compiling `if`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, x, y, z, n, c, v
|
||||
| {
|
||||
| ld a, 0
|
||||
@ -406,7 +406,7 @@ Compiling `if`.
|
||||
|
||||
Compiling `if not`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, x, y, z, n, c, v
|
||||
| {
|
||||
| ld a, 0
|
||||
@ -425,7 +425,7 @@ Compiling `if not`.
|
||||
|
||||
Compiling `if` without `else`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, x, y, z, n, c, v
|
||||
| {
|
||||
| ld a, 0
|
||||
@ -442,7 +442,7 @@ Compiling `if` without `else`.
|
||||
|
||||
Compiling `repeat ... until z`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, y, z, n, c
|
||||
| {
|
||||
| ld y, 65
|
||||
@ -461,7 +461,7 @@ Compiling `repeat ... until z`.
|
||||
|
||||
Compiling `repeat ... until not z`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, y, z, n, c
|
||||
| {
|
||||
| ld y, 65
|
||||
@ -480,7 +480,7 @@ Compiling `repeat ... until not z`.
|
||||
|
||||
Compiling `repeat ... until n`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, y, z, n, c
|
||||
| {
|
||||
| ld y, 65
|
||||
@ -497,7 +497,7 @@ Compiling `repeat ... until n`.
|
||||
|
||||
Compiling `repeat ... until not n`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, y, z, n, c
|
||||
| {
|
||||
| ld y, 199
|
||||
@ -514,7 +514,7 @@ Compiling `repeat ... until not n`.
|
||||
|
||||
Compiling `repeat forever`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, y, z, n, c
|
||||
| {
|
||||
| ld y, 65
|
||||
@ -529,7 +529,7 @@ Compiling `repeat forever`.
|
||||
|
||||
The body of `repeat forever` can be empty.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| {
|
||||
| repeat {
|
||||
| } forever
|
||||
@ -579,7 +579,7 @@ Compiling `for ... down to`.
|
||||
|
||||
Compiling `save`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs a
|
||||
| outputs a
|
||||
| trashes z, n
|
||||
@ -601,10 +601,32 @@ Compiling `save`.
|
||||
= $0816 PLA
|
||||
= $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.
|
||||
|
||||
| byte foo
|
||||
| routine main
|
||||
| define main routine
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| save foo {
|
||||
@ -625,7 +647,7 @@ Indexed access.
|
||||
| byte one
|
||||
| byte table[256] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs many
|
||||
| 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] tab2
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs tab1
|
||||
| outputs tab2
|
||||
| trashes a, x, n, z
|
||||
@ -664,7 +686,7 @@ Byte storage locations take up only 1 byte in memory.
|
||||
| byte one
|
||||
| byte two
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs one, two
|
||||
| trashes a, x, n, z
|
||||
| {
|
||||
@ -682,7 +704,7 @@ Copy byte to byte.
|
||||
| byte bar
|
||||
| byte baz
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs baz
|
||||
| outputs bar
|
||||
| trashes a, n, z
|
||||
@ -698,7 +720,7 @@ Copy word to word.
|
||||
| word bar
|
||||
| word baz
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs baz
|
||||
| outputs bar
|
||||
| trashes a, n, z
|
||||
@ -715,7 +737,7 @@ Copy literal word to word.
|
||||
|
||||
| word bar
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs bar
|
||||
| trashes a, n, z
|
||||
| {
|
||||
@ -731,7 +753,7 @@ You can also copy a literal word to a word table.
|
||||
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
@ -751,7 +773,7 @@ Copy vector to vector.
|
||||
| vector routine bar
|
||||
| vector routine baz
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs baz
|
||||
| outputs bar
|
||||
| trashes a, n, z
|
||||
@ -772,7 +794,7 @@ Copy routine to vector, inside an `interrupts off` block.
|
||||
| trashes z, n
|
||||
| bar
|
||||
|
|
||||
| routine foo
|
||||
| define foo routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
@ -780,7 +802,7 @@ Copy routine to vector, inside an `interrupts off` block.
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs bar
|
||||
| trashes a, n, z
|
||||
| {
|
||||
@ -806,14 +828,14 @@ Copy routine (by forward reference) to vector.
|
||||
| trashes z, n
|
||||
| bar
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs bar
|
||||
| trashes a, n, z
|
||||
| {
|
||||
| copy foo, bar
|
||||
| }
|
||||
|
|
||||
| routine foo
|
||||
| define foo routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
@ -833,7 +855,7 @@ Copy word to word table and back, with both `x` and `y` as indexes.
|
||||
| word one
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs one, many
|
||||
| outputs one, many
|
||||
| trashes a, x, y, n, z
|
||||
@ -877,14 +899,14 @@ Indirect call.
|
||||
| trashes z, n
|
||||
| foo
|
||||
|
|
||||
| routine bar
|
||||
| define bar routine
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs x, foo
|
||||
| trashes a, z, n
|
||||
| {
|
||||
@ -904,7 +926,7 @@ Indirect call.
|
||||
|
||||
Compiling `goto`. Note that no `RTS` is emitted after the `JMP`.
|
||||
|
||||
| routine bar
|
||||
| define bar routine
|
||||
| inputs y
|
||||
| outputs x, y
|
||||
| trashes z, n
|
||||
@ -912,7 +934,7 @@ Compiling `goto`. Note that no `RTS` is emitted after the `JMP`.
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs x, y
|
||||
| trashes a, z, n
|
||||
| {
|
||||
@ -937,11 +959,11 @@ Copying to and from a vector table.
|
||||
| trashes a, z, n
|
||||
| table[256] many
|
||||
|
|
||||
| routine bar outputs x trashes a, z, n {
|
||||
| define bar routine outputs x trashes a, z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs one, many
|
||||
| outputs one, many
|
||||
| trashes a, x, n, z
|
||||
@ -982,7 +1004,7 @@ Copying to and from a vector table.
|
||||
Adding a constant word to a word memory location.
|
||||
|
||||
| word score
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs score
|
||||
| outputs score
|
||||
| trashes a, c, z, v, n
|
||||
@ -1003,7 +1025,7 @@ Adding a word memory location to another word memory location.
|
||||
|
||||
| word score
|
||||
| word delta
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs score, delta
|
||||
| outputs score
|
||||
| trashes a, c, z, v, n
|
||||
@ -1023,7 +1045,7 @@ Adding a word memory location to another word memory location.
|
||||
Subtracting a constant word from a word memory location.
|
||||
|
||||
| word score
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs score
|
||||
| outputs score
|
||||
| trashes a, c, z, v, n
|
||||
@ -1044,7 +1066,7 @@ Subtracting a word memory location from another word memory location.
|
||||
|
||||
| word score
|
||||
| word delta
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs score, delta
|
||||
| outputs score
|
||||
| trashes a, c, z, v, n
|
||||
@ -1068,7 +1090,7 @@ Load address into pointer.
|
||||
| buffer[2048] buf
|
||||
| pointer ptr @ 254
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs buf
|
||||
| outputs buf, y
|
||||
| trashes a, z, n, ptr
|
||||
@ -1088,7 +1110,7 @@ Write literal through a pointer.
|
||||
| buffer[2048] buf
|
||||
| pointer ptr @ 254
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs buf
|
||||
| outputs buf, y
|
||||
| trashes a, z, n, ptr
|
||||
@ -1112,7 +1134,7 @@ Write stored value through a pointer.
|
||||
| pointer ptr @ 254
|
||||
| byte foo
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs foo, buf
|
||||
| outputs y, buf
|
||||
| trashes a, z, n, ptr
|
||||
@ -1136,7 +1158,7 @@ Read through a pointer, into a byte storage location, or the `a` register.
|
||||
| pointer ptr @ 254
|
||||
| byte foo
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs buf
|
||||
| outputs y, foo
|
||||
| trashes a, z, n, ptr
|
||||
@ -1162,7 +1184,7 @@ Read and write through two pointers.
|
||||
| pointer ptra @ 252
|
||||
| pointer ptrb @ 254
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs buf
|
||||
| outputs buf
|
||||
| trashes a, y, z, n, ptra, ptrb
|
||||
@ -1191,7 +1213,7 @@ Write the `a` register through a pointer.
|
||||
| pointer ptr @ 254
|
||||
| byte foo
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs buf
|
||||
| outputs buf
|
||||
| trashes a, y, z, n, ptr
|
||||
@ -1218,7 +1240,7 @@ Note that this is *not* range-checked. (Yet.)
|
||||
| byte foo
|
||||
| word delta
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs buf
|
||||
| outputs y, foo, delta
|
||||
| trashes a, c, v, z, n, ptr
|
||||
@ -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.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs a
|
||||
| outputs x
|
||||
| trashes a, z, n
|
||||
|
@ -48,7 +48,7 @@ Treat R as a mutable set and start with an empty list of lists L. Then,
|
||||
- Remove all elements occurring in C, from R.
|
||||
- Repeat until R is empty.
|
||||
|
||||
When times comes to generate code, generate it in the order given by L.
|
||||
When time comes to generate code, generate it in the order given by L.
|
||||
In addition, each sublist in L represents a number of routines to
|
||||
generate; all except the final routine in such a sublist need not have
|
||||
any jump instruction generated for its final `goto`.
|
||||
@ -65,7 +65,7 @@ to pass these tests to be considered an implementation of SixtyPical.
|
||||
-> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)"
|
||||
|
||||
-> Functionality "Compile SixtyPical program with fallthru optimization" is implemented by
|
||||
-> shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter </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"
|
||||
|
||||
@ -94,7 +94,7 @@ If `main` does a `goto foo`, then it can fall through to `foo`.
|
||||
| }
|
||||
= [
|
||||
= [
|
||||
= "main",
|
||||
= "main",
|
||||
= "foo"
|
||||
= ]
|
||||
= ]
|
||||
@ -119,9 +119,9 @@ of them to fall through, when selecting the order of routines.
|
||||
| }
|
||||
= [
|
||||
= [
|
||||
= "main",
|
||||
= "main",
|
||||
= "foo"
|
||||
= ],
|
||||
= ],
|
||||
= [
|
||||
= "bar"
|
||||
= ]
|
||||
@ -144,7 +144,7 @@ nothing ever falls through to `main`.
|
||||
= [
|
||||
= [
|
||||
= "main"
|
||||
= ],
|
||||
= ],
|
||||
= [
|
||||
= "foo"
|
||||
= ]
|
||||
@ -172,10 +172,10 @@ fall through to the other.
|
||||
= [
|
||||
= [
|
||||
= "main"
|
||||
= ],
|
||||
= ],
|
||||
= [
|
||||
= "bar",
|
||||
= "foo"
|
||||
= "foo",
|
||||
= "bar"
|
||||
= ]
|
||||
= ]
|
||||
|
||||
@ -204,12 +204,12 @@ routine.
|
||||
= [
|
||||
= [
|
||||
= "main"
|
||||
= ],
|
||||
= [
|
||||
= "bar"
|
||||
= ],
|
||||
= ],
|
||||
= [
|
||||
= "foo"
|
||||
= ],
|
||||
= [
|
||||
= "bar"
|
||||
= ]
|
||||
= ]
|
||||
|
||||
@ -238,9 +238,9 @@ If, however, they are the same goto, one can be optimized away.
|
||||
= [
|
||||
= [
|
||||
= "main"
|
||||
= ],
|
||||
= ],
|
||||
= [
|
||||
= "foo",
|
||||
= "foo",
|
||||
= "bar"
|
||||
= ]
|
||||
= ]
|
||||
@ -269,12 +269,12 @@ because we don't necessarily know what actual routine the vector contains.
|
||||
= [
|
||||
= [
|
||||
= "main"
|
||||
= ],
|
||||
= [
|
||||
= "bar"
|
||||
= ],
|
||||
= ],
|
||||
= [
|
||||
= "foo"
|
||||
= ],
|
||||
= [
|
||||
= "bar"
|
||||
= ]
|
||||
= ]
|
||||
|
||||
@ -321,14 +321,14 @@ Our algorithm might not be strictly optimal, but it does a good job.
|
||||
| }
|
||||
= [
|
||||
= [
|
||||
= "main",
|
||||
= "r1",
|
||||
= "r2",
|
||||
= "r3",
|
||||
= "main",
|
||||
= "r1",
|
||||
= "r2",
|
||||
= "r3",
|
||||
= "r4"
|
||||
= ],
|
||||
= ],
|
||||
= [
|
||||
= "r5",
|
||||
= "r5",
|
||||
= "r6"
|
||||
= ]
|
||||
= ]
|
||||
@ -416,12 +416,12 @@ in the "true" branch is a `goto`.
|
||||
| {
|
||||
| }
|
||||
= $080D RTS
|
||||
= $080E LDA #$FF
|
||||
= $0810 RTS
|
||||
= $0811 LDA #$00
|
||||
= $0813 BNE $081D
|
||||
= $0815 LDA #$01
|
||||
= $0817 JMP $080E
|
||||
= $081A JMP $0822
|
||||
= $081D LDA #$02
|
||||
= $081F JMP $080D
|
||||
= $080E LDA #$00
|
||||
= $0810 BNE $081A
|
||||
= $0812 LDA #$01
|
||||
= $0814 JMP $081F
|
||||
= $0817 JMP $081F
|
||||
= $081A LDA #$02
|
||||
= $081C JMP $080D
|
||||
= $081F LDA #$FF
|
||||
= $0821 RTS
|
||||
|
@ -16,7 +16,7 @@ but not necessarily sensible programs.
|
||||
|
||||
Rudimentary program.
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, 0
|
||||
| add a, 1
|
||||
| }
|
||||
@ -26,7 +26,7 @@ Program with comments.
|
||||
|
||||
| // Welcome to my program.
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, 0
|
||||
| add a, 1 // We are adding the thing.
|
||||
| sub a, 1
|
||||
@ -40,7 +40,7 @@ Program with comments.
|
||||
|
||||
Hex literals.
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, $ff
|
||||
| add a, $01
|
||||
| }
|
||||
@ -48,7 +48,7 @@ Hex literals.
|
||||
|
||||
Syntax error.
|
||||
|
||||
| routine foo (
|
||||
| define foo routine (
|
||||
| ld a, 0
|
||||
| add a, 1
|
||||
| )
|
||||
@ -65,12 +65,12 @@ Another syntax error.
|
||||
|
||||
Extern routines
|
||||
|
||||
| routine chrout
|
||||
| define chrout routine
|
||||
| inputs a
|
||||
| trashes a
|
||||
| @ 65490
|
||||
|
|
||||
| routine chrin
|
||||
| define chrin routine
|
||||
| outputs a
|
||||
| trashes x
|
||||
| @ 65487
|
||||
@ -78,7 +78,7 @@ Extern routines
|
||||
|
||||
Trash.
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| trash a
|
||||
| trash n
|
||||
| }
|
||||
@ -86,7 +86,7 @@ Trash.
|
||||
|
||||
`nop`.
|
||||
|
||||
| routine main
|
||||
| define main routine
|
||||
| {
|
||||
| nop
|
||||
| }
|
||||
@ -94,7 +94,7 @@ Trash.
|
||||
|
||||
If with not
|
||||
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld y, 0
|
||||
| cmp y, 10
|
||||
| if not z {
|
||||
@ -106,7 +106,7 @@ If with not
|
||||
|
||||
Repeat loop
|
||||
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld y, 0
|
||||
| repeat {
|
||||
| inc y
|
||||
@ -117,7 +117,7 @@ Repeat loop
|
||||
|
||||
"While" loop
|
||||
|
||||
| routine foo inputs y {
|
||||
| define foo routine inputs y {
|
||||
| repeat {
|
||||
| cmp y, 10
|
||||
| if not z {
|
||||
@ -129,7 +129,7 @@ Repeat loop
|
||||
|
||||
Repeat forever
|
||||
|
||||
| routine foo inputs y {
|
||||
| define foo routine inputs y {
|
||||
| repeat {
|
||||
| inc y
|
||||
| } forever
|
||||
@ -138,7 +138,7 @@ Repeat forever
|
||||
|
||||
Repeat with not
|
||||
|
||||
| routine foo inputs y {
|
||||
| define foo routine inputs y {
|
||||
| repeat {
|
||||
| inc y
|
||||
| } until not z
|
||||
@ -149,7 +149,7 @@ Basic "open-faced for" loops, up and down.
|
||||
|
||||
| byte table[256] tab
|
||||
|
|
||||
| routine foo trashes a, x, c, z, v {
|
||||
| define foo routine trashes a, x, c, z, v {
|
||||
| ld x, 0
|
||||
| for x up to 15 {
|
||||
| ld a, tab + x
|
||||
@ -163,7 +163,7 @@ Basic "open-faced for" loops, up and down.
|
||||
|
||||
Other blocks.
|
||||
|
||||
| routine main trashes a, x, c, z, v {
|
||||
| define main routine trashes a, x, c, z, v {
|
||||
| with interrupts off {
|
||||
| save a, x, c {
|
||||
| ld a, 0
|
||||
@ -183,7 +183,7 @@ User-defined memory addresses of different types.
|
||||
| buffer[2048] buf
|
||||
| pointer ptr
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
= ok
|
||||
|
||||
@ -193,7 +193,7 @@ Tables of different types and some operations on them.
|
||||
| word table[256] wmany
|
||||
| vector (routine trashes a) table[256] vmany
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld x, 0
|
||||
| ld a, 0
|
||||
| st off, c
|
||||
@ -215,7 +215,7 @@ greater than 0 and less than or equal to 256.
|
||||
|
||||
| word table[512] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
@ -227,7 +227,7 @@ greater than 0 and less than or equal to 256.
|
||||
|
||||
| word table[0] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
@ -239,7 +239,7 @@ greater than 0 and less than or equal to 256.
|
||||
|
||||
| word table[48] many
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| inputs many
|
||||
| outputs many
|
||||
| trashes a, x, n, z
|
||||
@ -256,7 +256,7 @@ Typedefs of different types.
|
||||
| typedef routine trashes a game_routine
|
||||
| vector game_routine start_game
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
= ok
|
||||
|
||||
@ -265,7 +265,7 @@ Can't have two typedefs with the same name.
|
||||
| typedef byte frank
|
||||
| typedef word frank
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
@ -280,7 +280,7 @@ Constants.
|
||||
|
|
||||
| byte lark: lives
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, lives
|
||||
| }
|
||||
= ok
|
||||
@ -290,7 +290,7 @@ Can't have two constants with the same name.
|
||||
| const w1 1000
|
||||
| const w1 word 0
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
@ -298,7 +298,7 @@ Explicit memory address.
|
||||
|
||||
| byte screen @ 1024
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, 100
|
||||
| st a, screen
|
||||
| shl screen
|
||||
@ -310,7 +310,7 @@ Initialized memory locations.
|
||||
|
||||
| byte lives : 3
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, lives
|
||||
| st a, lives
|
||||
| }
|
||||
@ -320,7 +320,7 @@ Cannot have both initial value and explicit address.
|
||||
|
||||
| byte screen : 3 @ 1024
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, lives
|
||||
| st a, lives
|
||||
| }
|
||||
@ -333,7 +333,7 @@ User-defined locations of other types.
|
||||
| word r2 @ 60000
|
||||
| word r3 : 2000
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
= ok
|
||||
|
||||
@ -341,15 +341,15 @@ Initialized byte table, initialized with ASCII string.
|
||||
|
||||
| byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?"
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
= ok
|
||||
|
||||
Can't initialize anything but a byte table with a string.
|
||||
|
||||
| word message : "WHAT DO YOU WANT TO DO NEXT?"
|
||||
| word message : "OUCH! WHAT DO YOU DO?"
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
@ -357,13 +357,13 @@ Initialized byte table, initialized with list of bytes.
|
||||
|
||||
| byte table[8] charmap : 0, 255, 129, 192, 0, 1, 2, 4
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
= ok
|
||||
|
||||
Can't access an undeclared memory location.
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, 0
|
||||
| st a, lives
|
||||
| }
|
||||
@ -374,7 +374,7 @@ Can't define two memory locations with the same name.
|
||||
| byte lives
|
||||
| byte lives
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld a, 0
|
||||
| st a, lives
|
||||
| }
|
||||
@ -384,19 +384,19 @@ Can't shadow the name of a register or a flag.
|
||||
|
||||
| byte a
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
| byte z
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
Can't call routine that hasn't been defined.
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld x, 0
|
||||
| ld y, 1
|
||||
| call up
|
||||
@ -404,44 +404,26 @@ Can't call routine that hasn't been defined.
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
And you can't call a non-routine.
|
||||
|
||||
| byte up
|
||||
|
|
||||
| routine main {
|
||||
| ld x, 0
|
||||
| ld y, 1
|
||||
| call up
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
| routine main {
|
||||
| ld x, 0
|
||||
| ld y, 1
|
||||
| call x
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
But you can call a routine that is yet to be defined, further on.
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld x, 0
|
||||
| ld y, 1
|
||||
| call up
|
||||
| call up
|
||||
| }
|
||||
| routine up {
|
||||
| define up routine {
|
||||
| ld a, 0
|
||||
| }
|
||||
= ok
|
||||
|
||||
Can't define two routines with the same name.
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| inc x
|
||||
| inc y
|
||||
| }
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld x, 0
|
||||
| ld y, 1
|
||||
| }
|
||||
@ -451,7 +433,7 @@ Declaring byte and word table memory location.
|
||||
|
||||
| byte table[256] tab
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld x, 0
|
||||
| ld y, 0
|
||||
| ld a, tab + x
|
||||
@ -462,7 +444,7 @@ Declaring byte and word table memory location.
|
||||
| word one
|
||||
| word table[256] many
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| ld x, 0
|
||||
| copy one, many + x
|
||||
| copy word 0, many + x
|
||||
@ -478,10 +460,10 @@ Declaring and calling a vector.
|
||||
| trashes a, x, z, n
|
||||
| cinv @ 788
|
||||
|
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld a, 0
|
||||
| }
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| with interrupts off {
|
||||
| copy foo, cinv
|
||||
| }
|
||||
@ -497,7 +479,7 @@ Only vectors can be decorated with constraints like that.
|
||||
| trashes a, x, z, n
|
||||
| @ 788
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
@ -509,10 +491,10 @@ Constraints set may only contain labels.
|
||||
| trashes a, x, z, n
|
||||
| cinv @ 788
|
||||
|
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld a, 0
|
||||
| }
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| with interrupts off {
|
||||
| copy foo, cinv
|
||||
| }
|
||||
@ -528,10 +510,10 @@ A vector can name itself in its inputs, outputs, and trashes.
|
||||
| trashes a, x, z, n
|
||||
| cinv @ 788
|
||||
|
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld a, 0
|
||||
| }
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| with interrupts off {
|
||||
| copy foo, cinv
|
||||
| }
|
||||
@ -548,50 +530,43 @@ references in the source of a `copy` instruction.
|
||||
| outputs cinv, x
|
||||
| trashes a, x, z, n
|
||||
| cinv @ 788
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| with interrupts off {
|
||||
| copy foo, cinv
|
||||
| }
|
||||
| call cinv
|
||||
| }
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld a, 0
|
||||
| }
|
||||
= ok
|
||||
|
||||
goto.
|
||||
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld a, 0
|
||||
| }
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| goto foo
|
||||
| }
|
||||
= ok
|
||||
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| goto foo
|
||||
| }
|
||||
| routine foo {
|
||||
| define foo routine {
|
||||
| ld a, 0
|
||||
| }
|
||||
= ok
|
||||
|
||||
| vector routine foo
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| goto foo
|
||||
| }
|
||||
= ok
|
||||
|
||||
| routine main {
|
||||
| goto foo
|
||||
| }
|
||||
? SyntaxError
|
||||
|
||||
| byte foo
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| goto foo
|
||||
| }
|
||||
? SyntaxError
|
||||
@ -603,7 +578,7 @@ Buffers and pointers.
|
||||
| pointer ptrb
|
||||
| byte foo
|
||||
|
|
||||
| routine main {
|
||||
| define main routine {
|
||||
| copy ^buf, ptr
|
||||
| copy 123, [ptr] + y
|
||||
| copy [ptr] + y, foo
|
||||
@ -629,7 +604,28 @@ Routines can be defined in a new style.
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| define main routine
|
||||
| outputs vec
|
||||
| trashes a, z, n
|
||||
| {
|
||||
| copy foo, vec
|
||||
| }
|
||||
= ok
|
||||
|
||||
| typedef routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| routine_type
|
||||
|
|
||||
| vector routine_type vec
|
||||
|
|
||||
| define foo routine_type
|
||||
| {
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| outputs vec
|
||||
| trashes a, z, n
|
||||
| {
|
||||
|
Loading…
Reference in New Issue
Block a user