From 8b24b6886b4659f971cb1b72a379a658f125411e Mon Sep 17 00:00:00 2001 From: crc Date: Thu, 23 May 2024 15:52:21 +0200 Subject: [PATCH] update README; add pali.c source --- LICENSE | 20 +++-- README.md | 96 +++++++++++++++++++++++- pali.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 326 insertions(+), 8 deletions(-) create mode 100644 pali.c diff --git a/LICENSE b/LICENSE index b9c199c..e5059fe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,8 +1,16 @@ -ISC License: +Copyright (c) Charles Childers -Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") -Copyright (c) 1995-2003 by Internet Software Consortium +Permission to use, copy, modify, and/or distribute this +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 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. +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS +ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +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. diff --git a/README.md b/README.md index fa54ed6..583f26d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,95 @@ -# pali +# Overview -An assembler for the ilo computer \ No newline at end of file +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. diff --git a/pali.c b/pali.c new file mode 100644 index 0000000..831202b --- /dev/null +++ b/pali.c @@ -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 +#include +#include + +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; +}