diff --git a/HISTORY.md b/HISTORY.md index a004965..63e1128 100644 --- a/HISTORY.md +++ b/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 ---- diff --git a/README.md b/README.md index 17b548e..48c4471 100644 --- a/README.md +++ b/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) diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..908f893 --- /dev/null +++ b/TODO.md @@ -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. diff --git a/bin/sixtypical b/bin/sixtypical index 680b41e..eb1867c 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -18,25 +18,12 @@ from pprint import pprint import sys import traceback -from sixtypical.parser import Parser, ParsingContext +from sixtypical.parser import Parser, ParsingContext, merge_programs from sixtypical.analyzer import Analyzer -from sixtypical.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 " diff --git a/doc/Output Formats.md b/doc/Output Formats.md index bebe1fd..d01363d 100644 --- a/doc/Output Formats.md +++ b/doc/Output Formats.md @@ -8,8 +8,7 @@ Output Formats The file contains only the emitted bytes of the compiled SixtyPical program. -The default origin is $0000; it is not unlikely you will want to -override this. +The default origin is $0000; you will likely want to override this. Note that the origin is not stored in the output file in this format; that information must be recorded separately. @@ -20,8 +19,7 @@ The first two bytes of the file contain the origin address in little-endian format. The remainder of the file is the emitted bytes of the compiled SixtyPical program, starting at that origin. -The default origin is $C000; it is likely you will want to -override this. +The default origin is $C000; you will likely want override this. This format coincides with Commodore's PRG format for disk files, thus its name. diff --git a/eg/apple2/lores.60p b/eg/apple2/lores.60p new file mode 100644 index 0000000..c68e6dd --- /dev/null +++ b/eg/apple2/lores.60p @@ -0,0 +1,20 @@ +byte ds_graphics @ $C050 +byte ds_text @ $C051 +byte ds_full @ $C052 +byte ds_split @ $C053 +byte ds_page1 @ $C054 +byte ds_page2 @ $C055 +byte ds_lores @ $C056 +byte ds_hires @ $C057 + +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 +} diff --git a/eg/apple2/print.60p b/eg/apple2/print.60p new file mode 100644 index 0000000..9dd0b5e --- /dev/null +++ b/eg/apple2/print.60p @@ -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 +} diff --git a/eg/c64/demo-game/demo-game.60p b/eg/c64/demo-game/demo-game.60p index fd72065..4097f70 100644 --- a/eg/c64/demo-game/demo-game.60p +++ b/eg/c64/demo-game/demo-game.60p @@ -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 diff --git a/eg/c64/hearts.60p b/eg/c64/hearts.60p index 43df36d..3a69adf 100644 --- a/eg/c64/hearts.60p +++ b/eg/c64/hearts.60p @@ -3,7 +3,7 @@ // Define where the screen starts in memory: byte table[256] screen @ 1024 -routine main +define main routine // These are the values that will be written to by this routine: trashes a, x, z, n, screen { diff --git a/eg/c64/intr1.60p b/eg/c64/intr1.60p index 68cad5c..b817d18 100644 --- a/eg/c64/intr1.60p +++ b/eg/c64/intr1.60p @@ -24,7 +24,7 @@ vector routine trashes z, n save_cinv -routine our_cinv +define our_cinv routine inputs vic_border outputs vic_border trashes z, n @@ -33,7 +33,7 @@ routine our_cinv goto save_cinv } -routine main +define main routine inputs cinv outputs cinv, save_cinv trashes a, n, z diff --git a/eg/c64/joystick.60p b/eg/c64/joystick.60p index 8783bd7..fdbaa83 100644 --- a/eg/c64/joystick.60p +++ b/eg/c64/joystick.60p @@ -3,7 +3,7 @@ byte joy2 @ $dc00 word delta -routine read_stick +define read_stick routine inputs joy2 outputs delta trashes a, x, z, n @@ -36,7 +36,7 @@ routine read_stick } } -routine main +define main routine inputs joy2 outputs delta trashes a, x, z, n, screen diff --git a/eg/c64/ribos/ribos2.60p b/eg/c64/ribos/ribos2.60p index b9d9877..ee86006 100644 --- a/eg/c64/ribos/ribos2.60p +++ b/eg/c64/ribos/ribos2.60p @@ -49,7 +49,7 @@ byte scanline : 85 // %01010101 // generating them as part of a SixtyPical program would not // be practical. So we just jump to this location instead. -routine pla_tay_pla_tax_pla_rti +define pla_tay_pla_tax_pla_rti routine inputs a trashes a @ $EA81 diff --git a/eg/c64/screen1.60p b/eg/c64/screen1.60p index 5a96691..a855d51 100644 --- a/eg/c64/screen1.60p +++ b/eg/c64/screen1.60p @@ -1,6 +1,6 @@ byte screen @ 1024 -routine main +define main routine trashes a, z, n, screen { ld a, 83 diff --git a/eg/rudiments/add-fail.60p b/eg/rudiments/add-fail.60p index ee9e034..ba653a4 100644 --- a/eg/rudiments/add-fail.60p +++ b/eg/rudiments/add-fail.60p @@ -1,4 +1,4 @@ -routine add_four +define add_four routine inputs a outputs a { diff --git a/eg/rudiments/add-pass.60p b/eg/rudiments/add-pass.60p index 65c69f1..4427cbc 100644 --- a/eg/rudiments/add-pass.60p +++ b/eg/rudiments/add-pass.60p @@ -1,4 +1,4 @@ -routine main +define main routine inputs a outputs a trashes c, z, n, v diff --git a/eg/rudiments/add-word.60p b/eg/rudiments/add-word.60p index ccf4d7a..a3c1d86 100644 --- a/eg/rudiments/add-word.60p +++ b/eg/rudiments/add-word.60p @@ -1,5 +1,5 @@ word score -routine main +define main routine inputs score outputs score trashes a, c, z, v, n diff --git a/eg/rudiments/buffer.60p b/eg/rudiments/buffer.60p index ce4dfbe..79bc512 100644 --- a/eg/rudiments/buffer.60p +++ b/eg/rudiments/buffer.60p @@ -2,7 +2,7 @@ buffer[2048] buf pointer ptr @ 254 byte foo -routine main +define main routine inputs buf outputs buf, y, foo trashes a, z, n, ptr diff --git a/eg/rudiments/call.60p b/eg/rudiments/call.60p index bfa0b89..a7fc616 100644 --- a/eg/rudiments/call.60p +++ b/eg/rudiments/call.60p @@ -1,16 +1,16 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine print +define print routine trashes a, z, n { ld a, 65 call chrout } -routine main +define main routine trashes a, z, n { call print diff --git a/eg/rudiments/conditional.60p b/eg/rudiments/conditional.60p index eb5711c..3cdb1fb 100644 --- a/eg/rudiments/conditional.60p +++ b/eg/rudiments/conditional.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine trashes a, x, y, z, n, c, v { ld a, 0 diff --git a/eg/rudiments/conditional2.60p b/eg/rudiments/conditional2.60p index 444de48..6483cfd 100644 --- a/eg/rudiments/conditional2.60p +++ b/eg/rudiments/conditional2.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine trashes a, x, y, z, n, c, v { ld a, 0 diff --git a/eg/rudiments/copy.60p b/eg/rudiments/copy.60p index ffc07d1..d31fd6b 100644 --- a/eg/rudiments/copy.60p +++ b/eg/rudiments/copy.60p @@ -1,7 +1,7 @@ byte bar byte baz -routine main +define main routine inputs baz outputs bar trashes a, n, z diff --git a/eg/rudiments/example.60p b/eg/rudiments/example.60p index d2b9e7a..c4475da 100644 --- a/eg/rudiments/example.60p +++ b/eg/rudiments/example.60p @@ -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 diff --git a/eg/rudiments/forever.60p b/eg/rudiments/forever.60p index e8b1c61..a9968cf 100644 --- a/eg/rudiments/forever.60p +++ b/eg/rudiments/forever.60p @@ -1,4 +1,4 @@ -routine main +define main routine trashes a, y, z, n, c { ld y, 65 diff --git a/eg/rudiments/goto.60p b/eg/rudiments/goto.60p index 5c8c0f8..6aafcbb 100644 --- a/eg/rudiments/goto.60p +++ b/eg/rudiments/goto.60p @@ -1,14 +1,14 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine bar trashes a, z, n { +define bar routine trashes a, z, n { ld a, 66 call chrout } -routine main trashes a, z, n { +define main routine trashes a, z, n { ld a, 65 call chrout goto bar diff --git a/eg/rudiments/if.60p b/eg/rudiments/if.60p index 64c63ae..9b0b968 100644 --- a/eg/rudiments/if.60p +++ b/eg/rudiments/if.60p @@ -1,6 +1,7 @@ -routine foo +define main routine inputs a outputs a + trashes z, n, c { cmp a, 42 if z { diff --git a/eg/rudiments/loop.60p b/eg/rudiments/loop.60p index 7de6f09..2f3d95b 100644 --- a/eg/rudiments/loop.60p +++ b/eg/rudiments/loop.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine trashes a, y, z, n, c { ld y, 65 diff --git a/eg/rudiments/memloc.60p b/eg/rudiments/memloc.60p index f8886ce..d6e49d1 100644 --- a/eg/rudiments/memloc.60p +++ b/eg/rudiments/memloc.60p @@ -1,11 +1,11 @@ byte foo -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine print +define print routine inputs foo trashes a, z, n { @@ -13,7 +13,7 @@ routine print call chrout } -routine main +define main routine trashes a, y, z, n, foo { ld y, 65 diff --git a/eg/rudiments/print.60p b/eg/rudiments/print.60p index c1c29c3..d060ebc 100644 --- a/eg/rudiments/print.60p +++ b/eg/rudiments/print.60p @@ -1,9 +1,9 @@ -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine main +define main routine inputs a trashes a, z, n { diff --git a/eg/rudiments/range-error.60p b/eg/rudiments/range-error.60p index 32b61a1..87e1095 100644 --- a/eg/rudiments/range-error.60p +++ b/eg/rudiments/range-error.60p @@ -1,6 +1,6 @@ byte table[8] message : "WHAT?" -routine main +define main routine inputs message outputs x, a, z, n { diff --git a/eg/rudiments/vector-inc.60p b/eg/rudiments/vector-inc.60p index 69a69c7..1e2bd8e 100644 --- a/eg/rudiments/vector-inc.60p +++ b/eg/rudiments/vector-inc.60p @@ -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 diff --git a/eg/rudiments/vector-main.60p b/eg/rudiments/vector-main.60p index b6e45e9..f8df898 100644 --- a/eg/rudiments/vector-main.60p +++ b/eg/rudiments/vector-main.60p @@ -12,7 +12,7 @@ vector routine // call chrout // } -routine main +define main routine trashes print, a, z, n { copy printa, print diff --git a/eg/rudiments/vector-table.60p b/eg/rudiments/vector-table.60p index d1fd0d0..4362012 100644 --- a/eg/rudiments/vector-table.60p +++ b/eg/rudiments/vector-table.60p @@ -11,26 +11,26 @@ vector (routine trashes a, z, n) table[32] vectors -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine printa +define printa routine trashes a, z, n { ld a, 65 call chrout } -routine printb +define printb routine trashes a, z, n { ld a, 66 call chrout } -routine main +define main routine inputs vectors outputs vectors trashes print, a, x, z, n, c diff --git a/eg/rudiments/vector.60p b/eg/rudiments/vector.60p index fd86d93..7783165 100644 --- a/eg/rudiments/vector.60p +++ b/eg/rudiments/vector.60p @@ -2,26 +2,26 @@ vector routine trashes a, z, n print -routine chrout +define chrout routine inputs a trashes a @ 65490 -routine printa +define printa routine trashes a, z, n { ld a, 65 call chrout } -routine printb +define printb routine trashes a, z, n { ld a, 66 call chrout } -routine main +define main routine trashes print, a, z, n { copy printa, print diff --git a/eg/rudiments/word-table.60p b/eg/rudiments/word-table.60p index ef40dd8..ffad04a 100644 --- a/eg/rudiments/word-table.60p +++ b/eg/rudiments/word-table.60p @@ -1,7 +1,7 @@ word one word table[256] many -routine main +define main routine inputs one, many outputs one, many trashes a, x, y, n, z diff --git a/eg/vic20/hearts.60p b/eg/vic20/hearts.60p index e4a615b..ffc4369 100644 --- a/eg/vic20/hearts.60p +++ b/eg/vic20/hearts.60p @@ -3,7 +3,7 @@ // Define where the screen starts in memory: byte table[256] screen @ 7680 -routine main +define main routine // These are the values that will be written to by this routine: trashes a, x, z, n, screen { diff --git a/loadngo.sh b/loadngo.sh index e196167..b2d6461 100755 --- a/loadngo.sh +++ b/loadngo.sh @@ -1,6 +1,6 @@ #!/bin/sh -usage="Usage: loadngo.sh (c64|vic20|atari2600) [--dry-run] " +usage="Usage: loadngo.sh (c64|vic20|atari2600|apple2) [--dry-run] " arch="$1" shift 1 @@ -21,6 +21,21 @@ elif [ "X$arch" = "Xvic20" ]; then elif [ "X$arch" = "Xatari2600" ]; then output_format='atari2600-cart' emu='stella' +elif [ "X$arch" = "Xapple2" ]; then + src="$1" + out=/tmp/a-out.bin + bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src --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 diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index ecb3b57..98959e1 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save +from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.model import ( TYPE_BYTE, TYPE_WORD, TableType, BufferType, PointerType, VectorType, RoutineType, @@ -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: diff --git a/src/sixtypical/ast.py b/src/sixtypical/ast.py index 718636f..f7aad36 100644 --- a/src/sixtypical/ast.py +++ b/src/sixtypical/ast.py @@ -37,14 +37,16 @@ class AST(object): def all_children(self): for attr in self.children_attrs: for child in self.attrs[attr]: + if child is not None: + yield child + for subchild in child.all_children(): + yield subchild + for attr in self.child_attrs: + child = self.attrs[attr] + if child is not None: yield child for subchild in child.all_children(): yield subchild - for attr in self.child_attrs: - child = self.attrs[attr] - yield child - for subchild in child.all_children(): - yield subchild class Program(AST): diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 34ed70f..3061443 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save +from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.model import ( ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef, TYPE_BIT, TYPE_BYTE, TYPE_WORD, @@ -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))) diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index a1b962c..5b4d346 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -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): diff --git a/src/sixtypical/fallthru.py b/src/sixtypical/fallthru.py index 85024bd..995fbcf 100644 --- a/src/sixtypical/fallthru.py +++ b/src/sixtypical/fallthru.py @@ -43,7 +43,7 @@ class FallthruAnalyzer(object): while pending_routines: chains = [self.find_chain(k, pending_routines) for k in pending_routines.keys()] - chains.sort(key=len, reverse=True) + chains.sort(key=lambda x: (len(x), str(x)), reverse=True) c = chains[0] roster.append(c) for k in c: diff --git a/src/sixtypical/gen6502.py b/src/sixtypical/gen6502.py index 7b8d07c..d3c14c7 100644 --- a/src/sixtypical/gen6502.py +++ b/src/sixtypical/gen6502.py @@ -8,7 +8,7 @@ class AddressingMode(Emittable): """Size of the operand for the mode (not including the opcode)""" raise NotImplementedError - def serialize(self, addr=None): + def serialize(self, addr): raise NotImplementedError def __repr__(self): @@ -19,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) diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index 30d5d54..7204dde 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -18,20 +18,6 @@ class Type(object): def __hash__(self): return hash(self.name) - def backpatch_constraint_labels(self, resolver): - def resolve(w): - if not isinstance(w, 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): diff --git a/src/sixtypical/outputter.py b/src/sixtypical/outputter.py new file mode 100644 index 0000000..b3828ef --- /dev/null +++ b/src/sixtypical/outputter.py @@ -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] diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index e16cf66..07f6f1c 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -18,6 +18,14 @@ class SymEntry(object): return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model) +class ForwardReference(object): + def __init__(self, name): + self.name = name + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self.name) + + class ParsingContext(object): def __init__(self): self.symbols = {} # token -> SymEntry @@ -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 diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 5e23587..b393e07 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -15,7 +15,7 @@ static analysis rules. Routines must declare their inputs, outputs, and memory locations they trash. - | routine up + | define up routine | inputs a | outputs a | trashes c, z, v, n @@ -27,7 +27,7 @@ Routines must declare their inputs, outputs, and memory locations they trash. Routines may not declare a memory location to be both an output and trashed. - | routine main + | define main routine | outputs a | trashes a | { @@ -37,14 +37,14 @@ Routines may not declare a memory location to be both an output and trashed. If a routine declares it outputs a location, that location should be initialized. - | routine main + | define main routine | outputs a, x, z, n | { | ld x, 0 | } ? UnmeaningfulOutputError: a - | routine main + | define main routine | inputs a | outputs a | { @@ -54,14 +54,14 @@ If a routine declares it outputs a location, that location should be initialized If a routine declares it outputs a location, that location may or may not have been initialized. Trashing is mainly a signal to the caller. - | routine main + | define main routine | trashes x, z, n | { | ld x, 0 | } = ok - | routine main + | define main routine | trashes x, z, n | { | } @@ -69,20 +69,20 @@ been initialized. Trashing is mainly a signal to the caller. If a routine modifies a location, it needs to either output it or trash it. - | routine main + | define main routine | { | ld x, 0 | } ? ForbiddenWriteError: x - | routine main + | define main routine | outputs x, z, n | { | ld x, 0 | } = ok - | routine main + | define main routine | trashes x, z, n | { | ld x, 0 @@ -91,14 +91,14 @@ If a routine modifies a location, it needs to either output it or trash it. This is true regardless of whether it's an input or not. - | routine main + | define main routine | inputs x | { | ld x, 0 | } ? ForbiddenWriteError: x - | routine main + | define main routine | inputs x | outputs x, z, n | { @@ -106,7 +106,7 @@ This is true regardless of whether it's an input or not. | } = ok - | routine main + | define main routine | inputs x | trashes x, z, n | { @@ -116,20 +116,20 @@ This is true regardless of whether it's an input or not. If a routine trashes a location, this must be declared. - | routine foo + | define foo routine | trashes x | { | trash x | } = ok - | routine foo + | define foo routine | { | trash x | } ? ForbiddenWriteError: x - | routine foo + | define foo routine | outputs x | { | trash x @@ -138,39 +138,39 @@ If a routine trashes a location, this must be declared. If a routine causes a location to be trashed, this must be declared in the caller. - | routine trash_x + | define trash_x routine | trashes x, z, n | { | ld x, 0 | } | - | routine foo + | define foo routine | trashes x, z, n | { | call trash_x | } = ok - | routine trash_x + | define trash_x routine | trashes x, z, n | { | ld x, 0 | } | - | routine foo + | define foo routine | trashes z, n | { | call trash_x | } ? ForbiddenWriteError: x - | routine trash_x + | define trash_x routine | trashes x, z, n | { | ld x, 0 | } | - | routine foo + | define foo routine | outputs x | trashes z, n | { @@ -185,7 +185,7 @@ If a routine reads or writes a user-define memory location, it needs to declare | word w1 @ 60001 | word w2 : 2000 | - | routine main + | define main routine | inputs b1, w1 | outputs b2, w2 | trashes a, z, n @@ -196,11 +196,40 @@ If a routine reads or writes a user-define memory location, it needs to declare | } = ok +### call ### + +You can't call a non-routine. + + | byte up + | + | define main routine outputs x, y trashes z, n { + | ld x, 0 + | ld y, 1 + | call up + | } + ? TypeMismatchError: up + + | define main routine outputs x, y trashes z, n { + | ld x, 0 + | ld y, 1 + | call x + | } + ? TypeMismatchError: x + +Nor can you goto a non-routine. + + | byte foo + | + | define main routine { + | goto foo + | } + ? TypeMismatchError: foo + ### ld ### Can't `ld` from a memory location that isn't initialized. - | routine main + | define main routine | inputs a, x | trashes a, z, n | { @@ -208,7 +237,7 @@ Can't `ld` from a memory location that isn't initialized. | } = ok - | routine main + | define main routine | inputs a | trashes a | { @@ -218,14 +247,14 @@ Can't `ld` from a memory location that isn't initialized. Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). - | routine main + | define main routine | trashes a, z, n | { | ld a, 0 | } = ok - | routine main + | define main routine | outputs a | trashes z, n | { @@ -233,7 +262,7 @@ Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). | } = ok - | routine main + | define main routine | outputs z, n | trashes a | { @@ -241,14 +270,14 @@ Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). | } = ok - | routine main + | define main routine | trashes z, n | { | ld a, 0 | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes a, n | { | ld a, 0 @@ -259,7 +288,7 @@ Can't `ld` a `word` type. | word foo | - | routine main + | define main routine | inputs foo | trashes a, n, z | { @@ -272,7 +301,7 @@ Can't `ld` a `word` type. Can't `st` from a memory location that isn't initialized. | byte lives - | routine main + | define main routine | inputs x | trashes lives | { @@ -281,7 +310,7 @@ Can't `st` from a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | trashes x, lives | { | st x, lives @@ -291,7 +320,7 @@ Can't `st` from a memory location that isn't initialized. Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). | byte lives - | routine main + | define main routine | trashes lives | { | st 0, lives @@ -299,7 +328,7 @@ Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | outputs lives | { | st 0, lives @@ -307,7 +336,7 @@ Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | inputs lives | { | st 0, lives @@ -318,7 +347,7 @@ Can't `st` a `word` type. | word foo | - | routine main + | define main routine | outputs foo | trashes a, n, z | { @@ -334,7 +363,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -347,7 +376,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -360,7 +389,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -373,7 +402,7 @@ Storing to a table, you must use an index. | byte one | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -388,7 +417,7 @@ The index must be initialized. | byte one | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -401,7 +430,7 @@ Reading from a table, you must use an index. | byte one | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -413,7 +442,7 @@ Reading from a table, you must use an index. | byte one | - | routine main + | define main routine | outputs one | trashes a, x, n, z | { @@ -425,7 +454,7 @@ Reading from a table, you must use an index. | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -438,7 +467,7 @@ Reading from a table, you must use an index. | byte table[256] many | - | routine main + | define main routine | outputs many | trashes a, x, n, z | { @@ -451,7 +480,7 @@ Reading from a table, you must use an index. | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -465,7 +494,7 @@ The index must be initialized. | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -478,7 +507,7 @@ There are other operations you can do on tables. (1/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z, v @@ -496,7 +525,7 @@ There are other operations you can do on tables. (2/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z @@ -513,7 +542,7 @@ There are other operations you can do on tables. (3/3) | byte table[256] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, c, n, z @@ -533,7 +562,7 @@ Copying to and from a word table. | word one | word table[256] many | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -547,7 +576,7 @@ Copying to and from a word table. | word one | word table[256] many | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -560,7 +589,7 @@ Copying to and from a word table. | word one | word table[256] many | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -575,7 +604,7 @@ You can also copy a literal word to a word table. | word table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -599,7 +628,7 @@ constant value falls inside or outside the range of the table. | byte table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -612,7 +641,7 @@ constant value falls inside or outside the range of the table. | byte table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -624,7 +653,7 @@ constant value falls inside or outside the range of the table. | byte table[32] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -640,7 +669,7 @@ This applies to `copy` as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs many, one | outputs many, one | trashes a, x, n, z @@ -654,7 +683,7 @@ This applies to `copy` as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs many, one | outputs many, one | trashes a, x, n, z @@ -667,7 +696,7 @@ This applies to `copy` as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs many, one | outputs many, one | trashes a, x, n, z @@ -685,7 +714,7 @@ a table. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -702,7 +731,7 @@ Test for "clipping", but not enough. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -720,7 +749,7 @@ no longer be guaranteed. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -737,7 +766,7 @@ no longer be guaranteed. Can't `add` from or to a memory location that isn't initialized. - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -748,7 +777,7 @@ Can't `add` from or to a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -759,7 +788,7 @@ Can't `add` from or to a memory location that isn't initialized. ? UnmeaningfulReadError: lives | byte lives - | routine main + | define main routine | inputs lives | outputs a | trashes c, z, v, n @@ -771,7 +800,7 @@ Can't `add` from or to a memory location that isn't initialized. Can't `add` to a memory location that isn't writeable. - | routine main + | define main routine | inputs a | trashes c | { @@ -783,7 +812,7 @@ Can't `add` to a memory location that isn't writeable. You can `add` a word constant to a word memory location. | word score - | routine main + | define main routine | inputs a, score | outputs score | trashes a, c, z, v, n @@ -796,7 +825,7 @@ You can `add` a word constant to a word memory location. `add`ing a word constant to a word memory location trashes `a`. | word score - | routine main + | define main routine | inputs a, score | outputs score, a | trashes c, z, v, n @@ -809,7 +838,7 @@ You can `add` a word constant to a word memory location. To be sure, `add`ing a word constant to a word memory location trashes `a`. | word score - | routine main + | define main routine | inputs score | outputs score | trashes c, z, v, n @@ -823,7 +852,7 @@ You can `add` a word memory location to another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes a, c, z, v, n @@ -837,7 +866,7 @@ You can `add` a word memory location to another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes c, z, v, n @@ -851,7 +880,7 @@ You can `add` a word memory location, or a constant, to a pointer. | pointer ptr | word delta - | routine main + | define main routine | inputs ptr, delta | outputs ptr | trashes a, c, z, v, n @@ -866,7 +895,7 @@ You can `add` a word memory location, or a constant, to a pointer. | pointer ptr | word delta - | routine main + | define main routine | inputs ptr, delta | outputs ptr | trashes c, z, v, n @@ -881,7 +910,7 @@ You can `add` a word memory location, or a constant, to a pointer. Can't `sub` from or to a memory location that isn't initialized. - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -892,7 +921,7 @@ Can't `sub` from or to a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | inputs a | outputs a | trashes c, z, v, n @@ -903,7 +932,7 @@ Can't `sub` from or to a memory location that isn't initialized. ? UnmeaningfulReadError: lives | byte lives - | routine main + | define main routine | inputs lives | outputs a | trashes c, z, v, n @@ -915,7 +944,7 @@ Can't `sub` from or to a memory location that isn't initialized. Can't `sub` to a memory location that isn't writeable. - | routine main + | define main routine | inputs a | trashes c | { @@ -927,7 +956,7 @@ Can't `sub` to a memory location that isn't writeable. You can `sub` a word constant from a word memory location. | word score - | routine main + | define main routine | inputs a, score | outputs score | trashes a, c, z, v, n @@ -940,7 +969,7 @@ You can `sub` a word constant from a word memory location. `sub`ing a word constant from a word memory location trashes `a`. | word score - | routine main + | define main routine | inputs a, score | outputs score, a | trashes c, z, v, n @@ -954,7 +983,7 @@ You can `sub` a word memory location from another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes a, c, z, v, n @@ -968,7 +997,7 @@ You can `sub` a word memory location from another word memory location. | word score | word delta - | routine main + | define main routine | inputs score, delta | outputs score | trashes c, z, v, n @@ -982,7 +1011,7 @@ You can `sub` a word memory location from another word memory location. Location must be initialized and writeable. - | routine main + | define main routine | outputs x | trashes z, n | { @@ -990,7 +1019,7 @@ Location must be initialized and writeable. | } ? UnmeaningfulReadError: x - | routine main + | define main routine | inputs x | trashes z, n | { @@ -998,7 +1027,7 @@ Location must be initialized and writeable. | } ? ForbiddenWriteError: x - | routine main + | define main routine | inputs x | outputs x | trashes z, n @@ -1011,7 +1040,7 @@ Can't `inc` a `word` type. | word foo | - | routine main + | define main routine | inputs foo | outputs foo | trashes z, n @@ -1024,7 +1053,7 @@ Can't `inc` a `word` type. Location must be initialized and writeable. - | routine main + | define main routine | outputs x | trashes z, n | { @@ -1032,7 +1061,7 @@ Location must be initialized and writeable. | } ? UnmeaningfulReadError: x - | routine main + | define main routine | inputs x | trashes z, n | { @@ -1040,7 +1069,7 @@ Location must be initialized and writeable. | } ? ForbiddenWriteError: x - | routine main + | define main routine | inputs x | outputs x | trashes z, n @@ -1053,7 +1082,7 @@ Can't `dec` a `word` type. | word foo | - | routine main + | define main routine | inputs foo | outputs foo | trashes z, n @@ -1066,7 +1095,7 @@ Can't `dec` a `word` type. Some rudimentary tests for `cmp`. - | routine main + | define main routine | inputs a | trashes z, c, n | { @@ -1074,7 +1103,7 @@ Some rudimentary tests for `cmp`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1082,7 +1111,7 @@ Some rudimentary tests for `cmp`. | } ? ForbiddenWriteError: c - | routine main + | define main routine | trashes z, c, n | { | cmp a, 4 @@ -1093,7 +1122,7 @@ Some rudimentary tests for `cmp`. Some rudimentary tests for `and`. - | routine main + | define main routine | inputs a | outputs a, z, n | { @@ -1101,7 +1130,7 @@ Some rudimentary tests for `and`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1109,7 +1138,7 @@ Some rudimentary tests for `and`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes z, n | { | and a, 4 @@ -1120,7 +1149,7 @@ Some rudimentary tests for `and`. Some rudimentary tests for `or`. - | routine main + | define main routine | inputs a | outputs a, z, n | { @@ -1128,7 +1157,7 @@ Some rudimentary tests for `or`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1136,7 +1165,7 @@ Some rudimentary tests for `or`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes z, n | { | or a, 4 @@ -1147,7 +1176,7 @@ Some rudimentary tests for `or`. Some rudimentary tests for `xor`. - | routine main + | define main routine | inputs a | outputs a, z, n | { @@ -1155,7 +1184,7 @@ Some rudimentary tests for `xor`. | } = ok - | routine main + | define main routine | inputs a | trashes z, n | { @@ -1163,7 +1192,7 @@ Some rudimentary tests for `xor`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | trashes z, n | { | xor a, 4 @@ -1175,7 +1204,7 @@ Some rudimentary tests for `xor`. Some rudimentary tests for `shl`. | byte foo - | routine main + | define main routine | inputs foo, a, c | outputs foo, a, c, z, n | { @@ -1184,7 +1213,7 @@ Some rudimentary tests for `shl`. | } = ok - | routine main + | define main routine | inputs a, c | outputs c, z, n | { @@ -1192,7 +1221,7 @@ Some rudimentary tests for `shl`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | inputs a | outputs a, c, z, n | { @@ -1205,7 +1234,7 @@ Some rudimentary tests for `shl`. Some rudimentary tests for `shr`. | byte foo - | routine main + | define main routine | inputs foo, a, c | outputs foo, a, c, z, n | { @@ -1214,7 +1243,7 @@ Some rudimentary tests for `shr`. | } = ok - | routine main + | define main routine | inputs a, c | outputs c, z, n | { @@ -1222,7 +1251,7 @@ Some rudimentary tests for `shr`. | } ? ForbiddenWriteError: a - | routine main + | define main routine | inputs a | outputs a, c, z, n | { @@ -1234,7 +1263,7 @@ Some rudimentary tests for `shr`. Some rudimentary tests for `nop`. - | routine main + | define main routine | { | nop | } @@ -1247,14 +1276,14 @@ initialized. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | { | call foo | } @@ -1264,14 +1293,14 @@ Note that if you call a routine that trashes a location, you also trash it. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n | { | ld x, 0 @@ -1281,14 +1310,14 @@ Note that if you call a routine that trashes a location, you also trash it. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n | trashes lives | { @@ -1301,14 +1330,14 @@ You can't output a value that the thing you called trashed. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n, lives | { | ld x, 0 @@ -1320,14 +1349,14 @@ You can't output a value that the thing you called trashed. | byte lives | - | routine foo + | define foo routine | inputs x | trashes lives | { | st x, lives | } | - | routine main + | define main routine | outputs x, z, n, lives | { | ld x, 0 @@ -1339,13 +1368,13 @@ You can't output a value that the thing you called trashed. If a routine declares outputs, they are initialized in the caller after calling it. - | routine foo + | define foo routine | outputs x, z, n | { | ld x, 0 | } | - | routine main + | define main routine | outputs a | trashes x, z, n | { @@ -1354,11 +1383,11 @@ calling it. | } = ok - | routine foo + | define foo routine | { | } | - | routine main + | define main routine | outputs a | trashes x | { @@ -1370,20 +1399,20 @@ calling it. If a routine trashes locations, they are uninitialized in the caller after calling it. - | routine foo + | define foo routine | trashes x, z, n | { | ld x, 0 | } = ok - | routine foo + | define foo routine | trashes x, z, n | { | ld x, 0 | } | - | routine main + | define main routine | outputs a | trashes x, z, n | { @@ -1395,12 +1424,12 @@ calling it. Calling an extern is just the same as calling a defined routine with the same constraints. - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 | - | routine main + | define main routine | trashes a, z, n | { | ld a, 65 @@ -1408,24 +1437,24 @@ same constraints. | } = ok - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 | - | routine main + | define main routine | trashes a, z, n | { | call chrout | } ? UnmeaningfulReadError: a - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 | - | routine main + | define main routine | trashes a, x, z, n | { | ld a, 65 @@ -1438,7 +1467,7 @@ same constraints. Trash does nothing except indicate that we do not care about the value anymore. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n @@ -1449,7 +1478,7 @@ Trash does nothing except indicate that we do not care about the value anymore. | } = ok - | routine foo + | define foo routine | inputs a | outputs a, x | trashes z, n @@ -1460,7 +1489,7 @@ Trash does nothing except indicate that we do not care about the value anymore. | } ? UnmeaningfulOutputError: a - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n @@ -1475,7 +1504,7 @@ Trash does nothing except indicate that we do not care about the value anymore. Both blocks of an `if` are analyzed. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1491,7 +1520,7 @@ Both blocks of an `if` are analyzed. If a location is initialized in one block, is must be initialized in the other as well. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1505,7 +1534,7 @@ If a location is initialized in one block, is must be initialized in the other a | } ? InconsistentInitializationError: x - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1519,7 +1548,7 @@ If a location is initialized in one block, is must be initialized in the other a | } ? InconsistentInitializationError: x - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1538,7 +1567,7 @@ initialized, either because it was set previous to the `if`, or is an input to the routine, and it is initialized in one branch, it need not be initialized in the other. - | routine foo + | define foo routine | outputs x | trashes a, z, n, c | { @@ -1553,7 +1582,7 @@ be initialized in the other. | } = ok - | routine foo + | define foo routine | inputs x | outputs x | trashes a, z, n, c @@ -1570,7 +1599,7 @@ be initialized in the other. An `if` with a single block is analyzed as if it had an empty `else` block. - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1582,7 +1611,7 @@ An `if` with a single block is analyzed as if it had an empty `else` block. | } ? InconsistentInitializationError: x - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1595,7 +1624,7 @@ An `if` with a single block is analyzed as if it had an empty `else` block. | } = ok - | routine foo + | define foo routine | inputs a | outputs x | trashes a, z, n, c @@ -1612,7 +1641,7 @@ The cardinal rule for trashes in an `if` is the "union rule": if one branch trashes {`a`} and the other branch trashes {`b`} then the whole `if` statement trashes {`a`, `b`}. - | routine foo + | define foo routine | inputs a, x, z | trashes a, x | { @@ -1624,7 +1653,7 @@ trashes {`a`, `b`}. | } = ok - | routine foo + | define foo routine | inputs a, x, z | trashes a | { @@ -1636,7 +1665,7 @@ trashes {`a`, `b`}. | } ? ForbiddenWriteError: x (in foo, line 10) - | routine foo + | define foo routine | inputs a, x, z | trashes x | { @@ -1652,7 +1681,7 @@ trashes {`a`, `b`}. Repeat loop. - | routine main + | define main routine | outputs x, y, n, z, c | { | ld x, 0 @@ -1667,7 +1696,7 @@ Repeat loop. You can initialize something inside the loop that was uninitialized outside. - | routine main + | define main routine | outputs x, y, n, z, c | { | ld x, 0 @@ -1682,12 +1711,12 @@ You can initialize something inside the loop that was uninitialized outside. But you can't UNinitialize something at the end of the loop that you need initialized at the start. - | routine foo + | define foo routine | trashes y | { | } | - | routine main + | define main routine | outputs x, y, n, z, c | { | ld x, 0 @@ -1707,7 +1736,7 @@ this is an error too. | word one : 0 | word two : 0 | - | routine main + | define main routine | inputs one, two | outputs two | trashes a, z, n @@ -1720,7 +1749,7 @@ this is an error too. The body of `repeat forever` can be empty. - | routine main + | define main routine | { | repeat { | } forever @@ -1729,7 +1758,7 @@ The body of `repeat forever` can be empty. While `repeat` is most often used with `z`, it can also be used with `n`. - | routine main + | define main routine | outputs y, n, z | { | ld y, 15 @@ -1743,7 +1772,10 @@ While `repeat` is most often used with `z`, it can also be used with `n`. Basic "open-faced for" loop. We'll start with the "upto" variant. -In a "for" loop, we know the exact range the loop variable takes on. +#### upward-counting variant + +Even though we do not give the starting value in the "for" construct, +we know the exact range the loop variable takes on. | byte table[16] tab | @@ -1776,6 +1808,21 @@ You need to initialize the loop variable before the loop. | } ? UnmeaningfulReadError +Because routines currently do not include range constraints, +the loop variable may not be useful as an input (the location +is assumed to have the maximum range.) + + | byte table[16] tab + | + | define foo routine + | inputs tab, x + | trashes a, x, c, z, v, n { + | for x up to 15 { + | ld a, 0 + | } + | } + ? RangeExceededError + You cannot modify the loop variable in a "for" loop. | byte table[16] tab @@ -1846,6 +1893,71 @@ If the range isn't known to be smaller than the final value, you can't go up to | } ? RangeExceededError +You can initialize something inside the loop that was uninitialized outside. + + | define main routine + | outputs x, y, n, z + | trashes c + | { + | ld x, 0 + | for x up to 15 { + | ld y, 15 + | } + | } + = ok + +But you can't UNinitialize something at the end of the loop that you need +initialized at the start of that loop. + + | define foo routine + | trashes y + | { + | } + | + | define main routine + | outputs x, y, n, z + | trashes c + | { + | ld x, 0 + | ld y, 15 + | for x up to 15 { + | inc y + | call foo + | } + | } + ? UnmeaningfulReadError: y + +The "for" loop does not preserve the `z` or `n` registers. + + | define foo routine trashes x { + | ld x, 0 + | for x up to 15 { + | } + | } + ? ForbiddenWriteError + +But it does preserve the other registers, such as `c`. + + | define foo routine trashes x, z, n { + | ld x, 0 + | for x up to 15 { + | } + | } + = ok + +In fact it does not strictly trash `z` and `n`, as they are +always set to known values after the loop. TODO: document +what these known values are! + + | define foo routine outputs z, n trashes x { + | ld x, 0 + | for x up to 15 { + | } + | } + = ok + +#### downward-counting variant + In a "for" loop (downward-counting variant), we know the exact range the loop variable takes on. | byte table[16] tab @@ -1903,45 +2015,40 @@ If the range isn't known to be larger than the final value, you can't go down to | } ? RangeExceededError -You can initialize something inside the loop that was uninitialized outside. +The "for" loop does not preserve the `z` or `n` registers. - | routine main - | outputs x, y, n, z - | trashes c - | { - | ld x, 0 - | for x up to 15 { - | ld y, 15 + | define foo routine trashes x { + | ld x, 15 + | for x down to 0 { + | } + | } + ? ForbiddenWriteError + +But it does preserve the other registers, such as `c`. + + | define foo routine trashes x, z, n { + | ld x, 15 + | for x down to 0 { | } | } = ok -But you can't UNinitialize something at the end of the loop that you need -initialized at the start of that loop. +In fact it does not strictly trash `z` and `n`, as they are +always set to known values after the loop. TODO: document +what these known values are! - | routine foo - | trashes y - | { - | } - | - | routine main - | outputs x, y, n, z - | trashes c - | { - | ld x, 0 - | ld y, 15 - | for x up to 15 { - | inc y - | call foo + | define foo routine outputs z, n trashes x { + | ld x, 15 + | for x down to 0 { | } | } - ? UnmeaningfulReadError: y + = ok ### save ### Basic neutral test, where the `save` makes no difference. - | routine main + | define main routine | inputs a, x | outputs a, x | trashes z, n @@ -1956,7 +2063,7 @@ Basic neutral test, where the `save` makes no difference. Saving any location (other than `a`) will trash `a`. - | routine main + | define main routine | inputs a, x | outputs a, x | trashes z, n @@ -1970,7 +2077,7 @@ Saving any location (other than `a`) will trash `a`. Saving `a` does not trash anything. - | routine main + | define main routine | inputs a, x | outputs a, x | trashes z, n @@ -1986,7 +2093,7 @@ Saving `a` does not trash anything. A defined value that has been saved can be trashed inside the block. It will continue to be defined outside the block. - | routine main + | define main routine | outputs x, y | trashes a, z, n | { @@ -2001,7 +2108,9 @@ It will continue to be defined outside the block. A trashed value that has been saved can be used inside the block. It will continue to be trashed outside the block. - | routine main +(Note, both x and a are unmeaningful in this test.) + + | define main routine | inputs a | outputs a, x | trashes z, n @@ -2013,14 +2122,14 @@ It will continue to be trashed outside the block. | ld x, 1 | } | } - ? UnmeaningfulOutputError: x + ? UnmeaningfulOutputError The known range of a value will be preserved outside the block as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -2038,7 +2147,7 @@ The known range of a value will be preserved outside the block as well. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -2058,7 +2167,7 @@ The known properties of a value are preserved inside the block, too. | word one: 77 | word table[32] many | - | routine main + | define main routine | inputs a, many, one | outputs many, one | trashes a, x, n, z @@ -2077,7 +2186,7 @@ The known properties of a value are preserved inside the block, too. A value which is not output from the routine, is preserved by the routine; and can appear in a `save` exactly because a `save` preserves it. - | routine main + | define main routine | outputs y | trashes a, z, n | { @@ -2091,7 +2200,7 @@ routine; and can appear in a `save` exactly because a `save` preserves it. Because saving anything except `a` trashes `a`, a common idiom is to save `a` first in a nested series of `save`s. - | routine main + | define main routine | inputs a | outputs a | trashes z, n @@ -2105,11 +2214,39 @@ first in a nested series of `save`s. | } = ok +There is a shortcut syntax for a nested series of `save`s. + + | define main routine + | inputs a + | outputs a + | trashes z, n + | { + | save a, x { + | ld a, 0 + | ld x, 1 + | } + | } + = ok + +`a` is only preserved if it is the outermost thing `save`d. + + | define main routine + | inputs a + | outputs a + | trashes z, n + | { + | save x, a { + | ld a, 0 + | ld x, 1 + | } + | } + ? UnmeaningfulOutputError: a + Not just registers, but also user-defined locations can be saved. | byte foo | - | routine main + | define main routine | trashes a, z, n | { | save foo { @@ -2122,7 +2259,7 @@ But only if they are bytes. | word foo | - | routine main + | define main routine | trashes a, z, n | { | save foo { @@ -2133,7 +2270,7 @@ But only if they are bytes. | byte table[16] tab | - | routine main + | define main routine | trashes a, y, z, n | { | save tab { @@ -2145,13 +2282,13 @@ But only if they are bytes. A `goto` cannot appear within a `save` block, even if it is otherwise in tail position. - | routine other + | define other routine | trashes a, z, n | { | ld a, 0 | } | - | routine main + | define main routine | trashes a, z, n | { | ld a, 1 @@ -2170,7 +2307,7 @@ A `goto` cannot appear within a `save` block, even if it is otherwise in tail po | trashes z, n | bar | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2178,7 +2315,7 @@ A `goto` cannot appear within a `save` block, even if it is otherwise in tail po | inc x | } | - | routine main + | define main routine | outputs bar | trashes a, n, z | { @@ -2197,7 +2334,7 @@ otherwise in tail position. | trashes z, n | bar | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2205,13 +2342,13 @@ otherwise in tail position. | inc x | } | - | routine other + | define other routine | trashes bar, a, n, z | { | ld a, 0 | } | - | routine main + | define main routine | trashes bar, a, n, z | { | with interrupts off { @@ -2226,7 +2363,7 @@ otherwise in tail position. Can't `copy` from a memory location that isn't initialized. | byte lives - | routine main + | define main routine | inputs x | outputs lives | trashes a, z, n @@ -2236,7 +2373,7 @@ Can't `copy` from a memory location that isn't initialized. = ok | byte lives - | routine main + | define main routine | outputs lives | trashes x, a, z, n | { @@ -2247,7 +2384,7 @@ Can't `copy` from a memory location that isn't initialized. Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). | byte lives - | routine main + | define main routine | trashes lives, a, z, n | { | copy 0, lives @@ -2255,7 +2392,7 @@ Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | outputs lives | trashes a, z, n | { @@ -2264,7 +2401,7 @@ Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). = ok | byte lives - | routine main + | define main routine | inputs lives | trashes a, z, n | { @@ -2272,30 +2409,34 @@ Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). | } ? ForbiddenWriteError: lives -a, z, and n are trashed, and must be declared as such +a, z, and n are trashed, and must be declared as such. + +(Note, both n and z are forbidden writes in this test.) | byte lives - | routine main + | define main routine | outputs lives | { | copy 0, lives | } - ? ForbiddenWriteError: n + ? ForbiddenWriteError a, z, and n are trashed, and must not be declared as outputs. +(Note, both n and a are unmeaningful outputs in this test.) + | byte lives - | routine main + | define main routine | outputs lives, a, z, n | { | copy 0, lives | } - ? UnmeaningfulOutputError: n + ? UnmeaningfulOutputError Unless of course you subsequently initialize them. | byte lives - | routine main + | define main routine | outputs lives, a, z, n | { | copy 0, lives @@ -2308,7 +2449,7 @@ Can `copy` from a `byte` to a `byte`. | byte source : 0 | byte dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2323,7 +2464,7 @@ as the destination of a `copy`. | byte source : 0 | byte dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2337,7 +2478,7 @@ Can `copy` from a `word` to a `word`. | word source : 0 | word dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2351,7 +2492,7 @@ Can't `copy` from a `byte` to a `word`. | byte source : 0 | word dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2365,7 +2506,7 @@ Can't `copy` from a `word` to a `byte`. | word source : 0 | byte dest | - | routine main + | define main routine | inputs source | outputs dest | trashes a, z, n @@ -2396,7 +2537,7 @@ Write literal through a pointer. | buffer[2048] buf | pointer ptr | - | routine main + | define main routine | inputs buf | outputs y, buf | trashes a, z, n, ptr @@ -2412,7 +2553,7 @@ It does use `y`. | buffer[2048] buf | pointer ptr | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, z, n, ptr @@ -2428,7 +2569,7 @@ Write stored value through a pointer. | pointer ptr | byte foo | - | routine main + | define main routine | inputs foo, buf | outputs y, buf | trashes a, z, n, ptr @@ -2445,7 +2586,7 @@ Read through a pointer. | pointer ptr | byte foo | - | routine main + | define main routine | inputs buf | outputs foo | trashes a, y, z, n, ptr @@ -2462,7 +2603,7 @@ Read and write through two pointers. | pointer ptra | pointer ptrb | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, y, z, n, ptra, ptrb @@ -2481,7 +2622,7 @@ not `copy`. | pointer ptr | byte foo | - | routine main + | define main routine | inputs buf | outputs a | trashes y, z, n, ptr @@ -2499,7 +2640,7 @@ not `copy`. | pointer ptr | byte foo | - | routine main + | define main routine | inputs buf | outputs buf | trashes a, y, z, n, ptr @@ -2522,7 +2663,7 @@ as an input to, an output of, or as a trashed value of a routine. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2530,7 +2671,7 @@ as an input to, an output of, or as a trashed value of a routine. | inc x | } | - | routine main + | define main routine | inputs foo | outputs vec | trashes a, z, n @@ -2545,7 +2686,7 @@ as an input to, an output of, or as a trashed value of a routine. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2553,7 +2694,7 @@ as an input to, an output of, or as a trashed value of a routine. | inc x | } | - | routine main + | define main routine | outputs vec, foo | trashes a, z, n | { @@ -2567,7 +2708,7 @@ as an input to, an output of, or as a trashed value of a routine. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2575,7 +2716,7 @@ as an input to, an output of, or as a trashed value of a routine. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n, foo | { @@ -2600,7 +2741,7 @@ If the vector and the routine have the very same signature, that's not an error. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2609,7 +2750,7 @@ If the vector and the routine have the very same signature, that's not an error. | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2627,7 +2768,7 @@ implementation doesn't actually read it.) | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2636,7 +2777,7 @@ implementation doesn't actually read it.) | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2652,7 +2793,7 @@ If the vector fails to take an input that the routine takes, that's an error. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2661,7 +2802,7 @@ If the vector fails to take an input that the routine takes, that's an error. | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2680,7 +2821,7 @@ output.) | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2689,7 +2830,7 @@ output.) | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2705,7 +2846,7 @@ If the vector fails to produce an output that the routine produces, that's an er | trashes z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2714,7 +2855,7 @@ If the vector fails to produce an output that the routine produces, that's an er | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2730,7 +2871,7 @@ If the vector fails to trash something the routine trashes, that's an error. | trashes z | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2739,7 +2880,7 @@ If the vector fails to trash something the routine trashes, that's an error. | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2758,7 +2899,7 @@ but it doesn't know that.) | trashes a, z, n | vec | - | routine foo + | define foo routine | inputs x, y | outputs x, y | trashes z, n @@ -2767,7 +2908,7 @@ but it doesn't know that.) | inc y | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2785,7 +2926,7 @@ Routines are read-only. | trashes z, n | vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -2793,7 +2934,7 @@ Routines are read-only. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -2807,11 +2948,11 @@ Indirect call. | outputs x trashes z, n | foo | - | routine bar outputs x trashes z, n { + | define bar routine outputs x trashes z, n { | ld x, 200 | } | - | routine main outputs x, foo trashes a, z, n { + | define main routine outputs x, foo trashes a, z, n { | copy bar, foo | call foo | } @@ -2821,11 +2962,11 @@ Calling the vector does indeed trash the things the vector says it does. | vector routine trashes x, z, n foo | - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main outputs x, foo trashes z, n { + | define main routine outputs x, foo trashes z, n { | ld x, 0 | copy bar, foo | call foo @@ -2834,31 +2975,31 @@ Calling the vector does indeed trash the things the vector says it does. `goto`, if present, must be in tail position (the final instruction in a routine.) - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | goto bar | } = ok - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | goto bar | ld x, 0 | } ? IllegalJumpError - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2867,11 +3008,11 @@ Calling the vector does indeed trash the things the vector says it does. | } = ok - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2881,11 +3022,11 @@ Calling the vector does indeed trash the things the vector says it does. | } ? IllegalJumpError - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2897,11 +3038,11 @@ Calling the vector does indeed trash the things the vector says it does. | } = ok - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | if z { | ld x, 1 @@ -2914,11 +3055,11 @@ Calling the vector does indeed trash the things the vector says it does. For the purposes of `goto`, the end of a loop is never tail position. - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | repeat { | inc x @@ -2929,22 +3070,22 @@ For the purposes of `goto`, the end of a loop is never tail position. Can't `goto` a routine that outputs or trashes more than the current routine. - | routine bar trashes x, y, z, n { + | define bar routine trashes x, y, z, n { | ld x, 200 | ld y, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | goto bar | } ? IncompatibleConstraintsError - | routine bar outputs y trashes z, n { + | define bar routine outputs y trashes z, n { | ld y, 200 | } | - | routine main trashes x, z, n { + | define main routine trashes x, z, n { | ld x, 0 | goto bar | } @@ -2952,11 +3093,11 @@ Can't `goto` a routine that outputs or trashes more than the current routine. Can `goto` a routine that outputs or trashes less than the current routine. - | routine bar trashes x, z, n { + | define bar routine trashes x, z, n { | ld x, 1 | } | - | routine main trashes a, x, z, n { + | define main routine trashes a, x, z, n { | ld a, 0 | ld x, 0 | goto bar @@ -2967,11 +3108,11 @@ Indirect goto. | vector routine outputs x trashes a, z, n foo | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main outputs x trashes foo, a, z, n { + | define main routine outputs x trashes foo, a, z, n { | copy bar, foo | goto foo | } @@ -2984,19 +3125,19 @@ vector says it does. | trashes a, x, z, n | foo | - | routine bar + | define bar routine | trashes a, x, z, n { | ld x, 200 | } | - | routine sub + | define sub routine | trashes foo, a, x, z, n { | ld x, 0 | copy bar, foo | goto foo | } | - | routine main + | define main routine | outputs a | trashes foo, x, z, n { | call sub @@ -3008,13 +3149,13 @@ vector says it does. | outputs x | trashes a, z, n foo | - | routine bar + | define bar routine | outputs x | trashes a, z, n { | ld x, 200 | } | - | routine sub + | define sub routine | outputs x | trashes foo, a, z, n { | ld x, 0 @@ -3022,7 +3163,7 @@ vector says it does. | goto foo | } | - | routine main + | define main routine | outputs a | trashes foo, x, z, n { | call sub @@ -3043,11 +3184,11 @@ A vector can be copied into a vector table. | trashes a, z, n) | table[256] many | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -3069,11 +3210,11 @@ A vector can be copied out of a vector table. | trashes a, z, n) | table[256] many | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main + | define main routine | inputs one, many | outputs one, many | trashes a, x, n, z @@ -3091,11 +3232,11 @@ A routine can be copied into a vector table. | trashes a, z, n) | table[256] many | - | routine bar outputs x trashes a, z, n { + | define bar routine outputs x trashes a, z, n { | ld x, 200 | } | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -3116,7 +3257,7 @@ A vector in a vector table cannot be directly called. | ld x, 200 | } | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -3140,7 +3281,7 @@ that types have structural equivalence, not name equivalence. | | vector routine_type vec | - | routine foo + | define foo routine | inputs x | outputs x | trashes z, n @@ -3148,7 +3289,7 @@ that types have structural equivalence, not name equivalence. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { @@ -3171,7 +3312,7 @@ The new style routine definitions support typedefs. | inc x | } | - | routine main + | define main routine | outputs vec | trashes a, z, n | { diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 1f72aad..87984df 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -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 shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter Tests for functionality "Compile SixtyPical program" 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 diff --git a/tests/SixtyPical Fallthru.md b/tests/SixtyPical Fallthru.md index 47f0310..75e3779 100644 --- a/tests/SixtyPical Fallthru.md +++ b/tests/SixtyPical Fallthru.md @@ -48,7 +48,7 @@ Treat R as a mutable set and start with an empty list of lists L. Then, - Remove all elements occurring in C, from R. - Repeat until R is empty. -When times comes to generate code, generate it in the order given by L. +When time comes to generate code, generate it in the order given by L. In addition, each sublist in L represents a number of routines to generate; all except the final routine in such a sublist need not have any jump instruction generated for its final `goto`. @@ -65,7 +65,7 @@ to pass these tests to be considered an implementation of SixtyPical. -> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)" -> Functionality "Compile SixtyPical program with fallthru optimization" is implemented by - -> shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter Tests for functionality "Dump fallthru info for SixtyPical program" @@ -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 diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index e2ef953..3409e63 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -16,7 +16,7 @@ but not necessarily sensible programs. Rudimentary program. - | routine main { + | define main routine { | ld a, 0 | add a, 1 | } @@ -26,7 +26,7 @@ Program with comments. | // Welcome to my program. | - | routine main { + | define main routine { | ld a, 0 | add a, 1 // We are adding the thing. | sub a, 1 @@ -40,7 +40,7 @@ Program with comments. Hex literals. - | routine main { + | define main routine { | ld a, $ff | add a, $01 | } @@ -48,7 +48,7 @@ Hex literals. Syntax error. - | routine foo ( + | define foo routine ( | ld a, 0 | add a, 1 | ) @@ -65,12 +65,12 @@ Another syntax error. Extern routines - | routine chrout + | define chrout routine | inputs a | trashes a | @ 65490 | - | routine chrin + | define chrin routine | outputs a | trashes x | @ 65487 @@ -78,7 +78,7 @@ Extern routines Trash. - | routine main { + | define main routine { | trash a | trash n | } @@ -86,7 +86,7 @@ Trash. `nop`. - | routine main + | define main routine | { | nop | } @@ -94,7 +94,7 @@ Trash. If with not - | routine foo { + | define foo routine { | ld y, 0 | cmp y, 10 | if not z { @@ -106,7 +106,7 @@ If with not Repeat loop - | routine foo { + | define foo routine { | ld y, 0 | repeat { | inc y @@ -117,7 +117,7 @@ Repeat loop "While" loop - | routine foo inputs y { + | define foo routine inputs y { | repeat { | cmp y, 10 | if not z { @@ -129,7 +129,7 @@ Repeat loop Repeat forever - | routine foo inputs y { + | define foo routine inputs y { | repeat { | inc y | } forever @@ -138,7 +138,7 @@ Repeat forever Repeat with not - | routine foo inputs y { + | define foo routine inputs y { | repeat { | inc y | } until not z @@ -149,7 +149,7 @@ Basic "open-faced for" loops, up and down. | byte table[256] tab | - | routine foo trashes a, x, c, z, v { + | define foo routine trashes a, x, c, z, v { | ld x, 0 | for x up to 15 { | ld a, tab + x @@ -163,7 +163,7 @@ Basic "open-faced for" loops, up and down. Other blocks. - | routine main trashes a, x, c, z, v { + | define main routine trashes a, x, c, z, v { | with interrupts off { | save a, x, c { | ld a, 0 @@ -183,7 +183,7 @@ User-defined memory addresses of different types. | buffer[2048] buf | pointer ptr | - | routine main { + | define main routine { | } = ok @@ -193,7 +193,7 @@ Tables of different types and some operations on them. | word table[256] wmany | vector (routine trashes a) table[256] vmany | - | routine main { + | define main routine { | ld x, 0 | ld a, 0 | st off, c @@ -215,7 +215,7 @@ greater than 0 and less than or equal to 256. | word table[512] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -227,7 +227,7 @@ greater than 0 and less than or equal to 256. | word table[0] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -239,7 +239,7 @@ greater than 0 and less than or equal to 256. | word table[48] many | - | routine main + | define main routine | inputs many | outputs many | trashes a, x, n, z @@ -256,7 +256,7 @@ Typedefs of different types. | typedef routine trashes a game_routine | vector game_routine start_game | - | routine main { + | define main routine { | } = ok @@ -265,7 +265,7 @@ Can't have two typedefs with the same name. | typedef byte frank | typedef word frank | - | routine main { + | define main routine { | } ? SyntaxError @@ -280,7 +280,7 @@ Constants. | | byte lark: lives | - | routine main { + | define main routine { | ld a, lives | } = ok @@ -290,7 +290,7 @@ Can't have two constants with the same name. | const w1 1000 | const w1 word 0 | - | routine main { + | define main routine { | } ? SyntaxError @@ -298,7 +298,7 @@ Explicit memory address. | byte screen @ 1024 | - | routine main { + | define main routine { | ld a, 100 | st a, screen | shl screen @@ -310,7 +310,7 @@ Initialized memory locations. | byte lives : 3 | - | routine main { + | define main routine { | ld a, lives | st a, lives | } @@ -320,7 +320,7 @@ Cannot have both initial value and explicit address. | byte screen : 3 @ 1024 | - | routine main { + | define main routine { | ld a, lives | st a, lives | } @@ -333,7 +333,7 @@ User-defined locations of other types. | word r2 @ 60000 | word r3 : 2000 | - | routine main { + | define main routine { | } = ok @@ -341,15 +341,15 @@ Initialized byte table, initialized with ASCII string. | byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?" | - | routine main { + | define main routine { | } = ok Can't initialize anything but a byte table with a string. - | word message : "WHAT DO YOU WANT TO DO NEXT?" + | word message : "OUCH! WHAT DO YOU DO?" | - | routine main { + | define main routine { | } ? SyntaxError @@ -357,13 +357,13 @@ Initialized byte table, initialized with list of bytes. | byte table[8] charmap : 0, 255, 129, 192, 0, 1, 2, 4 | - | routine main { + | define main routine { | } = ok Can't access an undeclared memory location. - | routine main { + | define main routine { | ld a, 0 | st a, lives | } @@ -374,7 +374,7 @@ Can't define two memory locations with the same name. | byte lives | byte lives | - | routine main { + | define main routine { | ld a, 0 | st a, lives | } @@ -384,19 +384,19 @@ Can't shadow the name of a register or a flag. | byte a | - | routine main { + | define main routine { | } ? SyntaxError | byte z | - | routine main { + | define main routine { | } ? SyntaxError Can't call routine that hasn't been defined. - | routine main { + | define main routine { | ld x, 0 | ld y, 1 | call up @@ -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 | {