update README; add pali.c source

This commit is contained in:
crc 2024-05-23 15:52:21 +02:00
parent 05e0bc2768
commit 8b24b6886b
3 changed files with 326 additions and 8 deletions

20
LICENSE
View file

@ -1,8 +1,16 @@
ISC License: Copyright (c) Charles Childers
Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") Permission to use, copy, modify, and/or distribute this
Copyright (c) 1995-2003 by Internet Software Consortium software for any purpose with or without fee is hereby
granted, provided that the copyright notice and this
permission notice appear in all copies.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.

View file

@ -1,3 +1,95 @@
# pali # Overview
An assembler for the ilo computer The pali assembler is used to create images for the ilo virtual
computer. It use the literate [unu format](http://unu.retroforth.org),
with the assembly code in dedicated code blocks, and commentary
outside these blocks.
Code blocks start and end with a ~~~ sequence. They contain a
series of lines, each of which consists of a single character
directive, a space, and any parameters the directive requires.
The directives are:
~~~
+---+----------------------------------------------------------+
| i | process parameter as instruction bundle |
| o | set origin/offset in memory space |
| * | reserve parameter cells of data in memory |
| r | parameter is a named item, assemble a pointer to it |
| R | parameter is a named immediate item, assemble a pointer |
| | to it |
| - | alias for `r` |
| d | parameter is a decimal value, assemble it inline |
| c | parameter is a comment to be ignored |
| : | parameter is a label name |
| s | parameter is a string, assemble as length prefixed |
| z | parameter is a string, assemble as null-terminated |
+---+----------------------------------------------------------+
~~~
The pali assembler is a two pass design. The first pass will
scan through the code, recording any labels and their offsets
in the image. The second pass actually assembles the data,
instructions, and resolves any references to labels.
# The ilo Instruction Set
ilo has 30 instructions. In short, these are:
~~~
+----+----+--------+-------------------------------------------+
| Op | Nm | Stack | Description |
+====+====+========+===========================================+
| 00 | .. | - | non-op |
| 01 | li | -n | push value in following cell to stack |
| 02 | du | n-nn | duplicate top stack item |
| 03 | dr | n- | discard top stack item |
| 04 | sw | ab-ba | swap top two stack items |
| 05 | pu | n- | move top stack item to address stack |
| 06 | po | -n | move top address stack item to data stack |
| 07 | ju | a- | jump to an address |
| 08 | ca | a- | call a function |
| 09 | cc | fa- | call a function if the flag is non-zero |
| 10 | cj | fa- | jump to a function if the flag is non-zero|
| 11 | re | - | return from a call or conditional call |
| 12 | eq | ab-f | compare two values for equality. a == b |
| 13 | ne | ab-f | compare two values for inequality. a != b |
| 14 | lt | ab-f | compare two values for less than. a < b |
| 15 | gt | ab-f | compare two values for greater than. a > b|
| 16 | fe | a-n | fetch a stored value in memory |
| 17 | st | na- | store a value into memory |
| 18 | ad | ab-c | add two numbers. a + b |
| 19 | su | ab-c | subtract two numbers. a - b |
| 20 | mu | ab-c | multiply two numbers. a * b |
| 21 | di | ab-cd | divide and get remainder. a % b, a / b |
| 22 | an | ab-c | bitwise and |
| 23 | or | ab-c | bitwise or |
| 24 | xo | ab-c | bitwise xor |
| 25 | sl | ab-c | shift left. a << b |
| 26 | sr | ab-c | shift right. a >> b |
| 27 | cp | sdn-f | compare two memory regions |
| 28 | cy | sdn- | copy memory |
| 29 | io | n- | perform i/o operation |
+----+----+--------+-------------------------------------------+
~~~
A condensed summary table:
~~~
Opode Instruction Names Data Stack Effects
===== ================= ====================================
00-05 .. li du dr sw pu - -n n-nn n- nm-mn n-
06-11 po ju ca cc cj re -n a- a- fa- fa- -
12-17 eq ne lt gt fe st nn-f nn-f nn-f nn-f a-n na-
18-23 ad su mu di an or nn-n nn-n nn-n nn-nn nn-n nn-n
24-29 xo sl sr cp cy io nn-n nn-n nn-n nnn- nnn- n-
===== ================= ====================================
~~~
ilo expects four instructions to be packed into a single memory
location. These are processed in order. Instructions that modify
the instruction pointer (other than `li`) can not be followed by
anything other than a non-op to avoid unpredictable behavior.
Instructions are unpacked from right to left.

218
pali.c Normal file
View file

@ -0,0 +1,218 @@
/***************************************************************
crc's _ _
_ __ __ _| (_)
| '_ \ / _` | | |
| |_) | (_| | | |
| .__/ \__,_|_|_|
|_| assembler
****************************************************************
The pali assembler is used to create images for the ilo virtual
computer. It use the literate unu format, with the assembly code
in dedicated code blocks, and commentary outside these blocks.
Code blocks start and end with a ~~~ sequence. They contain a
series of lines, each of which consists of a single character
directive, a space, and any parameters the directive requires.
The directives are:
+---+----------------------------------------------------------+
| i | process parameter as instruction bundle |
| o | set origin/offset in memory space |
| * | reserve parameter cells of data in memory |
| r | parameter is a named item, assemble a pointer to it |
| R | parameter is a named immediate item, assemble a pointer |
| | to it |
| - | alias for `r` |
| d | parameter is a decimal value, assemble it inline |
| c | parameter is a comment to be ignored |
| : | parameter is a label name |
| s | parameter is a string, assemble as length prefixed |
| z | parameter is a string, assemble as null-terminated |
+---+----------------------------------------------------------+
The pali assembler is a two pass design. The first pass will
scan through the code, recording any labels and their offsets
in the image. The second pass actually assembles the data,
instructions, and resolves any references to labels.
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef void (*Handler)(char *);
void unu(char *, Handler);
char source[1025];
int np, here, Labels[1024], Pointers[1024], target[65536];
void red() { printf("\033[0;31m"); }
void cyan() { printf("\033[0;36m"); }
void plain() { printf("\033[0;0m"); }
void read_line(FILE *file, char *line_buffer) {
int ch = getc(file);
int count = 0;
while ((ch != '\n') && (ch != EOF)) {
line_buffer[count++] = ch;
ch = getc(file);
}
line_buffer[count] = '\0';
}
void unu(char *fname, Handler handler) {
int inBlock = 0;
char buffer[4096];
FILE *fp;
fp = fopen(fname, "r");
if (fp == NULL) {
red(); printf("Unable to load file\n"); plain();
exit(2);
}
while (!feof(fp)) {
read_line(fp, buffer);
if (strcmp(buffer, "~~~") == 0) {
inBlock = (inBlock == 0 ? 1 : 0);
} else {
if (inBlock == 1) {
handler(buffer);
}
}
}
fclose(fp);
}
int hash(char *s) {
int c, h = 5381;
while ((c = *s++)) h = (h * 33) + c;
return h;
}
void save() {
FILE *fp;
if ((fp = fopen("ilo.rom", "wb")) == NULL) {
red(); printf("Unable to save the image!\n"); plain();
exit(2);
}
fwrite(&target, sizeof(int), 65536, fp);
fclose(fp);
}
int lookup(char *name) {
int n = np;
int h = hash(name);
while (n > 0) {
n--;
if (Labels[n] == h) return Pointers[n];
}
return -1;
}
void add_label(char *name, int slice) {
if (lookup(name) == -1) {
Labels[np] = hash(name);
Pointers[np] = slice;
np++;
return;
}
red(); printf("Fatal error: ");
cyan(); printf("%s", name);
red(); printf(" already defined\n");
plain(); exit(0);
}
int encode(char *s) {
int ops[] = { 5861473, 5863578, 5863326,
5863323, 5863823, 5863722,
5863716, 5863524, 5863273,
5863275, 5863282, 5863772,
5863355, 5863640, 5863589,
5863424, 5863376, 5863820,
5863210, 5863821, 5863623,
5863314, 5863220, 5863686,
5863980, 5863812, 5863818,
5863288, 5863297, 5863485, };
int op = hash(s);
int i = 0;
for (i = 0; i <= 30; i++) if (ops[i] == op) return i;
return 0;
}
void pass1(char *buffer) {
switch (buffer[0]) {
case 'c': break;
case 'o': here = atoi(buffer+2); break;
case '*': here += atoi(buffer+2); break;
case 's': here = here + strlen(buffer) - 1; break;
case 'z': here = here + strlen(buffer) - 1; break;
case ':': add_label(buffer+2, here); break;
default: if (strlen(buffer) > 0) here++; break;
}
}
void pass2(char *buffer) {
unsigned int opcode;
char inst[3] = { 0, 0, 0 };
switch (buffer[0]) {
case 'c': break;
case 'o': here = atoi(buffer+2); break;
case 'i': memcpy(inst, buffer + 8, 2);
opcode = encode(inst) << 8;
memcpy(inst, buffer + 6, 2);
opcode += encode(inst);
opcode = opcode << 8;
memcpy(inst, buffer + 4, 2);
opcode += encode(inst);
opcode = opcode << 8;
memcpy(inst, buffer + 2, 2);
opcode += encode(inst);
target[here++] = opcode;
break;
case 'd': target[here++] = atoi(buffer+2); break;
case '*': here += atoi(buffer+2); break;
case 's': opcode = 2;
target[here++] = strlen(buffer) - 2;
while (opcode < strlen(buffer))
target[here++] = buffer[opcode++];
break;
case 'z': opcode = 2;
while (opcode < strlen(buffer))
target[here++] = buffer[opcode++];
target[here++] = 0;
break;
case 'r':
case '-': target[here++] = lookup(buffer+2);
if (lookup(buffer+2) == -1) {
red(); printf("Lookup failed: ");
cyan(); printf("%s\n", buffer+2);
plain();
}
break;
case 'R': target[here++] = lookup(buffer+2) * -1;
if (lookup(buffer+2) == -1) {
red(); printf("Lookup failed: ");
cyan(); printf("%s\n", buffer+2);
plain();
}
break;
case ':': break;
default: if (strlen(buffer) > 0) here++; break;
}
}
int main(int argc, char **argv) {
if (argc > 1) {
np = 0;
here = 0; unu(argv[1], &pass1);
here = 0; unu(argv[1], &pass2);
save();
printf("%d words (%d bytes) used\n", here, here * 4);
return 0;
}
red(); printf("No file specified.\n"); plain();
return -1;
}