git @ Cat's Eye Technologies SixtyPical / 0.9 doc / SixtyPical.md
0.9

Tree @0.9 (Download .tar.gz)

SixtyPical.md @0.9

8ccabdf
f92056d
 
22c336d
8ccabdf
 
f92056d
8ccabdf
 
f92056d
4d61f43
 
 
f92056d
 
 
9874b11
f92056d
 
 
f2f716d
c98e446
 
9874b11
f92056d
9874b11
0e9a887
7323927
 
9874b11
0e9a887
f92056d
 
 
f2f716d
 
 
f92056d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ccabdf
 
 
f92056d
 
 
 
 
 
f2f716d
f92056d
 
 
 
 
 
f2f716d
 
22cc7bf
 
f2f716d
22cc7bf
f2f716d
22cc7bf
 
f2f716d
f92056d
 
 
f2f716d
c98e446
f92056d
 
 
6cf8b5f
522c771
c98e446
522c771
6cf8b5f
 
 
7b1ee60
6cf8b5f
f2f716d
 
 
 
f0b8942
 
 
 
 
 
 
0e9a887
 
 
 
 
 
 
 
 
 
 
feb5729
9874b11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f92056d
 
 
f2f716d
 
 
 
 
 
 
 
 
 
f92056d
 
 
 
 
 
 
 
c79bc56
 
f2f716d
f92056d
2dc4dd9
 
 
f92056d
8ccabdf
 
 
f2f716d
8ccabdf
 
 
 
f2f716d
f92056d
c79bc56
 
 
49d90df
 
 
 
 
 
f92056d
 
 
f2f716d
 
 
 
 
 
 
f92056d
 
41414b0
f92056d
 
 
 
f2f716d
f92056d
 
 
 
f2f716d
8ccabdf
f92056d
41414b0
 
f92056d
 
 
 
 
41414b0
f92056d
 
 
 
f2f716d
f92056d
 
 
 
 
 
41414b0
 
c79bc56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9874b11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f92056d
 
 
 
 
 
 
 
f2f716d
f92056d
f2f716d
8ccabdf
f92056d
8ccabdf
f92056d
0120a4e
 
 
 
 
 
f92056d
 
8ccabdf
 
 
 
 
 
f2f716d
8ccabdf
f2f716d
8ccabdf
 
f92056d
 
 
 
 
 
8ccabdf
 
f2f716d
8ccabdf
f2f716d
8ccabdf
 
 
f92056d
 
 
4d61f43
8ccabdf
 
 
 
 
f2f716d
8ccabdf
f2f716d
8ccabdf
 
f92056d
 
 
 
4d61f43
 
f92056d
8ccabdf
 
f2f716d
8ccabdf
f92056d
4fc38be
f92056d
 
 
4fc38be
f92056d
4fc38be
 
f92056d
4fc38be
 
f2f716d
f92056d
f2f716d
4fc38be
f92056d
4fc38be
f92056d
4fc38be
f92056d
 
4fc38be
f92056d
4fc38be
f92056d
 
4fc38be
 
 
f92056d
 
 
f2f716d
f92056d
f2f716d
4fc38be
 
f92056d
 
2dc4dd9
 
 
 
646ec38
 
f92056d
f2f716d
 
f92056d
 
 
f2f716d
 
 
9874b11
f92056d
646ec38
 
 
 
 
 
 
 
 
 
 
 
 
 
c79bc56
 
646ec38
 
 
f2f716d
 
646ec38
f92056d
 
4d61f43
 
f92056d
4d61f43
f92056d
 
4d61f43
 
 
f92056d
4d61f43
 
 
 
f92056d
feb5729
 
f7eb0d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c79bc56
c98e446
c79bc56
 
 
 
 
c98e446
c79bc56
c98e446
c79bc56
 
 
 
 
c98e446
f92056d
 
 
 
9d6ca0b
f0b8942
 
9d6ca0b
f92056d
563aaeb
f92056d
 
9d6ca0b
49d90df
 
f92056d
4287bf8
 
f92056d
 
 
 
 
 
 
 
 
 
be76b9a
 
f7eb0d4
49e42af
c98e446
f7eb0d4
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
SixtyPical
==========

This document describes the SixtyPical programming language version 0.9,
both its execution aspect and its static analysis aspect (even though
these are, technically speaking, separate concepts.)

This document is nominally normative, but the tests in the `tests` directory
are even more normative.

Refer to the bottom of this document for an EBNF grammar of the syntax of
the language.

Types
-----

There are six *primitive types* in SixtyPical:

*   bit (2 possible values)
*   byte (256 possible values)
*   word (65536 possible values)
*   routine (code stored somewhere in memory, read-only)
*   vector (address of a routine)
*   pointer (address of a byte in a buffer)

There are also two *type constructors*:

*   T table (256 entries, each holding a value of type T, where T is either
            `byte` or `word`)
*   buffer[N] (N entries; each entry is a byte; N is a power of 2, ≤ 64K)

Memory locations
----------------

A primary concept in SixtyPical is the *memory location*.  At any given point
in time during execution, each memory location is either *uninitialized* or
*initialized*.  At any given point in the program text, too, each memory
location is either uninitialized or initialized.  Where-ever it is one or
the other during execution, it is the same in the corresponding place in
the program text; thus, it is a static property.

There are four general kinds of memory location.  The first three are
pre-defined and built-in.

### Registers ###

Each of these hold a byte.  They are initially uninitialized.

    a
    x
    y

### Flags ###

Each of these hold a bit.  They are initially uninitialized.

    c (carry)
    z (zero)
    v (overflow)
    n (negative)

### Constants ###

It may be strange to think of constants as memory locations, but keep in mind
that a memory location in SixtyPical need not map to a memory location in the
underlying hardware.  All constants are read-only.  Each is initially
initialized with the value that corresponds with its name.

They come in bit and byte types.  There are two bit constants,

    off
    on

two hundred and fifty-six byte constants,

    0
    1
    ...
    255

and sixty-five thousand five hundred and thirty-six word constants,

    word 0
    word 1
    ...
    word 65535

Note that if a word constant is between 256 and 65535, the leading `word`
token can be omitted.

### User-defined ###

There may be any number of user-defined memory locations.  They are defined
by giving the type (which may be any type except `bit` and `routine`) and the
name.

    byte pos

An address in memory may be given explicitly on a user-defined memory location.

    byte table screen @ 1024

Or, a user-defined memory location may be given an initial value.  But in this
case, an explicit address in memory cannot be given.

    byte pos : 0

A user-defined vector memory location is decorated with `inputs`, `outputs`
and `trashes` lists like a routine (see below), and it may only hold addresses
of routines which are compatible.  (Meaning, the routine's inputs (resp. outputs,
trashes) must be a subset of the vector's inputs (resp. outputs, trashes.))

    vector actor_logic
      inputs a, score
      outputs x
      trashes y
      @ $c000

Note that in the code of a routine, if a memory location is named by a
user-defined symbol, it is an address in memory, and can be read and written.
But if it is named by a literal integer, either decimal or hexadecimal, it
is a constant and can only be read (and when read always yields that constant
value.  So, for instance, to read the value at `screen` above, in the code,
you would need to reference the symbol `screen`; attempting to read 1024
would not work.

This is actually useful, at least at this point, as you can rely on the fact
that literal integers in the code are always immediate values.  (But this
may change at some point.)

### Buffers and Pointers ###

Roughly speaking, a `buffer` is a table that can be longer than 256 bytes,
and a `pointer` is an address within a buffer.

A `pointer` is implemented as a zero-page memory location, and accessing the
buffer pointed to is implemented with "indirect indexed" addressing, as in

    LDA ($02), Y
    STA ($02), Y

There are extended modes of `copy` for using these types of memory location.
See `copy` below, but here is some illustrative example code:

    copy ^buf, ptr           // this is the only way to initialize a pointer
    add ptr, 4               // ok, but only if it does not exceed buffer's size
    ld y, 0                  // you must set this to something yourself
    copy [ptr] + y, byt      // read memory through pointer, into byte
    copy 100, [ptr] + y      // write memory through pointer (still trashes a)

where `ptr` is a user-defined storage location of `pointer` type, and the
`+ y` part is mandatory.

Routines
--------

Every routine must list all the memory locations it *reads from*, which we
call its `inputs`, and all the memory locations it *writes to*.  The latter
we divide into two groups: its `outputs` which it intentionally initializes,
and its `trashes`, which it does not care about, and leaves uninitialized.
For example, if it uses a register to temporarily store an intermediate
value used in a multiplication, that register has no meaning outside of
the multiplication, and is one of the routine's `trashes`.

It is common to say that the `trashes` are the memory locations that are
*not preserved* by the routine.

    routine foo
      inputs a, score
      outputs x
      trashes y {
        ...
    }

The union of the `outputs` and `trashes` is sometimes collectively called
"the WRITES" of the routine, for historical reasons and as shorthand.

Routines may call only routines previously defined in the program source.
Thus, directly recursive routines are not allowed.  (However, routines may
also call routines via vectors, which are dynamically assigned.  In this
case, there is, for the time being, no check for recursive calls.)

For a SixtyPical program to be run, there must be one routine called `main`.
This routine is executed when the program is run.

The memory locations given as inputs to a routine are considered to be initialized
at the beginning of the routine.  Various instructions cause memory locations
to be initialized after they are executed.  Calling a routine which trashes
some memory locations causes those memory locations to be uninitialized after
that routine is called.  At the end of a routine, all memory locations listed
as outputs must be initialized.

A literal word can given instead of the body of the routine.  This word is the
absolute address of an "external" routine located in memory but not defined by
the SixtyPical program.

    routine chrout
      inputs a
      trashes a
      @ 65490

Instructions
------------

Instructions are inspired by, and in many cases closely resemble, the 6502
instruction set.  However, in many cases they do not map 1:1 to 6502 instructions.
If a SixtyPical instruction cannot be translated validly to one more more 6502
instructions while retaining all the stated constraints, that's a static error
in a SixtyPical program, and technically any implementation of SixtyPical, even
an interpreter, should flag it up.

### ld ###

    ld <dest-memory-location>, <src-memory-location> [+ <index-memory-location>]

Reads from src and writes to dest.

*   It is illegal if dest is not a register.
*   It is illegal if dest does not occur in the WRITES of the current routine.
*   It is illegal if src is not of same type as dest (i.e., is not a byte.)
*   It is illegal if src is uninitialized.

After execution, dest is considered initialized.  The flags `z` and `n` may be
changed by this instruction; they must be named in the WRITES, and they
are considered initialized after it has executed.

If and only if src is a byte table, the index-memory-location must be given.

Some combinations, such as `ld x, y`, are illegal because they do not map to
underlying opcodes.

### st ###

    st <src-memory-location>, <dest-memory-location> [+ <index-memory-location>]

Reads from src and writes to dest.

*   It is illegal if dest is a register or if dest is read-only.
*   It is illegal if dest does not occur in the WRITES of the current routine.
*   It is illegal if src is not of same type as dest.
*   It is illegal if src is uninitialized.

After execution, dest is considered initialized.  No flags are
changed by this instruction (unless of course dest is a flag.)

If and only if dest is a byte table, the index-memory-location must be given.

### copy ###

    copy <src-memory-location>, <dest-memory-location>

Reads from src and writes to dest.  Differs from `st` in that is able to
copy more general types of data (for example, vectors,) and it trashes the
`z` and `n` flags and the `a` register.

*   It is illegal if dest is read-only.
*   It is illegal if dest does not occur in the WRITES of the current routine.
*   It is illegal if src is not of same type as dest.
*   It is illegal if src is uninitialized.

After execution, dest is considered initialized, and `z` and `n`, and
`a` are considered uninitialized.

There are two extra modes that this instruction can be used in.  The first is
to load an address into a pointer:

    copy ^<src-memory-location>, <dest-memory-location>

This copies the address of src into dest.  In this case, src must be
of type buffer, and dest must be of type pointer.  src will not be
considered a memory location that is read, since it is only its address
that is being retrieved.

The second is to read or write indirectly through a pointer.

    copy [<src-memory-location>] + y, <dest-memory-location>
    copy <src-memory-location>, [<dest-memory-location>] + y

In both of these, the memory location in the `[]+y` syntax must be
a pointer.

The first copies the contents of memory at the pointer (offset by the `y`
register) into a byte memory location.

The second copies a literal byte, or a byte memory location, into
the contents of memory at the pointer (offset by the `y` register).

In addition to the constraints above, `y` must be initialized before
this mode is used.

### add dest, src ###

    add <dest-memory-location>, <src-memory-location>

Adds the contents of src to dest and stores the result in dest.

*   It is illegal if src OR dest OR c is uninitialized.
*   It is illegal if dest is read-only.
*   It is illegal if dest does not occur in the WRITES of the current routine.

Affects n, z, c, and v flags, requiring that they be in the WRITES,
and initializing them afterwards.

dest and src continue to be initialized afterwards.

In addition, if dest is of `word` type, then src must also be of `word`
type, and in this case this instruction trashes the `a` register.

NOTE: If dest is a pointer, the addition does not check if the result of
the pointer arithmetic continues to be valid (within a buffer) or not.

### inc ###

    inc <dest-memory-location>

Increments the value in dest.  Does not honour carry.

*   It is illegal if dest is uninitialized.
*   It is illegal if dest is read-only.
*   It is illegal if dest does not occur in the WRITES of the current routine.

Affects n and z flags, requiring that they be in the WRITES,
and initializing them afterwards.

### sub ###

    sub <dest-memory-location>, <src-memory-location>

Subtracts the contents of src from dest and stores the result in dest.

*   It is illegal if src OR dest OR c is uninitialized.
*   It is illegal if dest is read-only.
*   It is illegal if dest does not occur in the WRITES of the current routine.

Affects n, z, c, and v flags, requiring that they be in the WRITES,
and initializing them afterwards.

dest and src continue to be initialized afterwards.

### dec ###

    dec <dest-memory-location>

Decrements the value in dest.  Does not honour carry.

*   It is illegal if dest is uninitialized.
*   It is illegal if dest is read-only.
*   It is illegal if dest does not occur in the WRITES of the current routine.

Affects n and z flags, requiring that they be in the WRITES,
and initializing them afterwards.

### cmp ###

    cmp <dest-memory-location>, <src-memory-location>

Subtracts the contents of src from dest (without considering carry) but
does not store the result anywhere, only sets the resulting flags.

*   It is illegal if src OR dest is uninitialized.

Affects n, z, and c flags, requiring that they be in the WRITES,
and initializing them afterwards.

### and, or, xor ###

    and <dest-memory-location>, <src-memory-location>
    or <dest-memory-location>, <src-memory-location>
    xor <dest-memory-location>, <src-memory-location>

Applies the given bitwise Boolean operation to src and dest and stores
the result in dest.

*   It is illegal if src OR dest OR is uninitialized.
*   It is illegal if dest is read-only.
*   It is illegal if dest does not occur in the WRITES of the current routine.

Affects n and z flags, requiring that they be in the WRITES of the
current routine, and sets them as initialized afterwards.

dest and src continue to be initialized afterwards.

### shl, shr ###

    shl <dest-memory-location>
    shr <dest-memory-location>

`shl` shifts the dest left one bit position.  The rightmost position becomes `c`,
and `c` becomes the bit that was shifted off the left.

`shr` shifts the dest right one bit position.  The leftmost position becomes `c`,
and `c` becomes the bit that was shifted off the right.

*   It is illegal if dest is a register besides `a`.
*   It is illegal if dest is read-only.
*   It is illegal if dest OR c is uninitialized.
*   It is illegal if dest does not occur in the WRITES of the current routine.

Affects the c flag, requiring that it be in the WRITES of the
current routine, and it continues to be initialized afterwards.

### call ###

    call <executable-name>

Transfers execution to the given executable, whether that is a previously-
defined routine, or a vector location which contains the address of a routine
which will be called indirectly.  Execution will be transferred back to the
current routine, when execution of the executable is finished.

*   It is illegal if any of the memory locations listed in the called routine's
    `inputs` are uninitialized immediately before the call.

Just after the call,

*   All memory locations listed in the called routine's `trashes` are considered
    to now be uninitialized.
*   All memory locations listed in the called routine's `outputs` are considered
    to now be initialized.

### goto ###

    goto <executable-name>

Unilaterally transfers execution to the given executable.  Execution will not
be transferred back to the current routine when execution of the executable is
finished; rather, it will be transferred back to the caller of the current
routine.

If `goto` is used in a routine, it must be in tail position.  That is, it
must be the final instruction in the routine.

Just before the goto,

*   It is illegal if any of the memory locations in the target routine's
    `inputs` list is uninitialized.

In addition,

*   The target executable's WRITES must not include any locations
    that are not already included in the current routine's WRITES.

### if ###

    if <src-memory-location> {
        <true-branch>
    } else {
        <false-branch>
    }

Executes the true-branch if the value in src is nonzero, otherwise executes
the false-branch.  The false-branch is optional may be omitted; in this case
it is treated like an empty block.

*   It is illegal if src is not z, c, n, or v.
*   It is illegal if src is not initialized.
*   It is illegal if any location initialized at the end of the true-branch
    is not initialized at the end of the false-branch, and vice versa.

The sense of the test can be inverted with `not`.

### repeat ###

    repeat {
        <block>
    } until <src-memory-location>

Executes the block repeatedly until the src (observed at the end of the
execution of the block) is non-zero.  The block is always executed as least
once.

*   It is illegal if any memory location is uninitialized at the exit of
    the loop when that memory location is initialized at the start of
    the loop.

To simulate a "while" loop, use an `if` internal to the block, like

    repeat {
        cmp y, 25
        if z {
        }
    } until z

"until" is optional, but if omitted, must be replaced with "forever":

    repeat {
        cmp y, 25
        if z {
        }
    } forever

The sense of the test can be inverted with `not`.

    repeat {
        cmp y, 25
        if z {
        }
    } until not z

Grammar
-------

    Program ::= {Defn} {Routine}.
    Defn    ::= Type Ident<new> [Constraints] (":" Literal | "@" LitWord).
    Type    ::= "byte" ["table"] | "vector"
    Constrnt::= ["inputs" LocExprs] ["outputs" LocExprs] ["trashes" LocExprs].
    Routine ::= "routine" Ident<new> Constraints (Block | "@" LitWord).
    LocExprs::= LocExpr {"," LocExpr}.
    LocExpr ::= Register | Flag | Literal | Ident.
    Register::= "a" | "x" | "y".
    Flag    ::= "c" | "z" | "n" | "v".
    Literal ::= LitByte | LitWord.
    LitByte ::= "0" ... "255".
    LitWord ::= "0" ... "65535".
    Block   ::= "{" {Instr} "}".
    Instr   ::= "ld" LocExpr "," LocExpr ["+" LocExpr]
              | "st" LocExpr "," LocExpr ["+" LocExpr]
              | "add" LocExpr "," LocExpr
              | "sub" LocExpr "," LocExpr
              | "cmp" LocExpr "," LocExpr
              | "and" LocExpr "," LocExpr
              | "or" LocExpr "," LocExpr
              | "xor" LocExpr "," LocExpr
              | "shl" LocExpr
              | "shr" LocExpr
              | "inc" LocExpr
              | "dec" LocExpr
              | "call" Ident<routine>
              | "goto" Ident<executable>
              | "if" ["not"] LocExpr Block ["else" Block]
              | "repeat" Block ("until" ["not"] LocExpr | "forever")
              | "copy" LocExpr "," LocExpr ["+" LocExpr]
              .