summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2023-01-31 21:55:33 +0100
committerAnton Luka Šijanec <anton@sijanec.eu>2023-01-31 21:55:33 +0100
commit258c9ef71e45c8e6dff9ee708ce51681dc90ecf9 (patch)
tree78a99cc9919ee1a9c8d586c2ebbe168dfb5193d2
downloadebs-258c9ef71e45c8e6dff9ee708ce51681dc90ecf9.tar
ebs-258c9ef71e45c8e6dff9ee708ce51681dc90ecf9.tar.gz
ebs-258c9ef71e45c8e6dff9ee708ce51681dc90ecf9.tar.bz2
ebs-258c9ef71e45c8e6dff9ee708ce51681dc90ecf9.tar.lz
ebs-258c9ef71e45c8e6dff9ee708ce51681dc90ecf9.tar.xz
ebs-258c9ef71e45c8e6dff9ee708ce51681dc90ecf9.tar.zst
ebs-258c9ef71e45c8e6dff9ee708ce51681dc90ecf9.zip
-rw-r--r--.gitignore7
-rw-r--r--disass.c45
-rw-r--r--ebs.c196
-rw-r--r--main.c137
-rw-r--r--makefile26
-rw-r--r--spec.txt48
6 files changed, 459 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..02161f0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+ebs
+tmp/
+valgrind-out.*
+a.out
+core
+.gdb_history
+disass
diff --git a/disass.c b/disass.c
new file mode 100644
index 0000000..c52c154
--- /dev/null
+++ b/disass.c
@@ -0,0 +1,45 @@
+#include <ebs.c>
+#include <sys/mman.h>
+#include <error.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define S0(x) ((x) ? (x) : "")
+int main (int argc, char ** argv) {
+ int r = 0;
+ struct ebs ebs;
+ ebs_init(&ebs);
+ if (argc != 1+1)
+ error_at_line(1, 0, __FILE__, __LINE__, "%s program.ebs", S0(argv[0]));
+ int pfd = open(argv[1], O_RDONLY | O_CLOEXEC);
+ if (pfd == -1)
+ error_at_line(2, errno, __FILE__, __LINE__, "open(%s)", argv[1]);
+ struct stat statbuf;
+ if (fstat(pfd, &statbuf) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "fstat(pfd, &statbuf)");
+ r = 3;
+ goto r;
+ }
+ if (!(ebs.pm_velikost = statbuf.st_size)) {
+ error_at_line(0, errno, __FILE__, __LINE__, "!(ebs.pm_velikost = statbuf.st_size)");
+ r = 4;
+ goto r;
+ }
+ if (!(ebs.pm = mmap(NULL, ebs.pm_velikost, PROT_READ, MAP_SHARED, pfd, 0))) {
+ error_at_line(0, errno, __FILE__, __LINE__, "!mmap(NULL, ebs.pm_velikost, PROT_READ, MAP_SHARED, pfd, 0)");
+ r = 5;
+ goto r;
+ }
+ for (unsigned i = 0; i <= 0xffff; i++) {
+ struct inštrukcija š = inštrukcija(&ebs, i);
+ printf("0x%04x\t%s\t0x%02x\t0x%02x\t0x%02x%02x\n", i, operacija_str[š.operacija], š.vir, š.destinacija, š.dobesedno[0], š.dobesedno[1]);
+ }
+ r:
+ if (ebs.pm && munmap(ebs.pm, ebs.pm_velikost) == -1)
+ error_at_line(0, errno, __FILE__, __LINE__, "munmap(ebs.pm, ebs.pm_velikost)");
+ close(pfd);
+ pfd = -1;
+ return r;
+}
diff --git a/ebs.c b/ebs.c
new file mode 100644
index 0000000..975136d
--- /dev/null
+++ b/ebs.c
@@ -0,0 +1,196 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <string.h>
+#define RAM_VELIKOST 16
+#ifndef NO_HOOKS
+#define HOOK e->
+#else
+#define HOOK
+#endif
+
+typedef uint16_t naslov;
+
+/**
+ * držalo za enobitni stroj
+ */
+
+struct ebs {
+ unsigned char ram[RAM_VELIKOST-2+2]; /**< ne vsebuje programskega števca, ampak vsebuje magic bite */
+ unsigned char * pm;
+ unsigned pm_velikost;
+ uint16_t pc;
+#ifndef NO_HOOKS
+ bool (* peek)(struct ebs *, naslov);
+ void (* poke)(struct ebs *, naslov, bool);
+ struct inštrukcija (* inštrukcija)(struct ebs *, naslov);
+#endif
+ void * userdata;
+ unsigned char vhod_medp;
+ unsigned char vhod_medp_indeks;
+ unsigned char izhod_medp;
+ unsigned char izhod_medp_indeks;
+ void (* vhod_prazen)(struct ebs *);
+ void (* izhod_poln)(struct ebs *);
+};
+
+enum operacija {
+ copy_ram = 0,
+ copy_pm = 1,
+ nand = 2,
+ xor = 3
+};
+
+char * operacija_str[] = {"copyRAM", "copyPM", "nand", "xor"};
+
+struct inštrukcija {
+ unsigned vir;
+ unsigned destinacija;
+ enum operacija operacija;
+ unsigned char * dobesedno;
+};
+
+/**
+ * bralec rama
+ *
+ * @param [in] držalo
+ * @param a [in] naslov
+ * @return vrednost v ramu na tem naslovu
+ */
+
+static bool peek (struct ebs * e, naslov a) {
+ assert(a < RAM_VELIKOST*8+15);
+ if (a < 16)
+ return e->pc & (1 << (15-a));
+ return e->ram[a/8-2] & (1 << (a % 8));
+}
+
+/**
+ * pisalec rama
+ *
+ * @param e [in] držalo
+ * @param a [in] naslov
+ * @param v [in] vrednost
+ */
+
+static void poke (struct ebs * e, naslov a, bool v) {
+ assert(a < RAM_VELIKOST*8+15);
+ if (a < 16) {
+ if (v)
+ e->pc |= (1 << (15-a));
+ else
+ e->pc &= ~(1 << (15-a));
+ return;
+ }
+ if (v)
+ e->ram[a/8-2] |= (1 << (a % 8));
+ else
+ e->ram[a/8-2] &= ~(1 << (a % 8));
+}
+
+static struct inštrukcija inštrukcija (struct ebs * e, naslov a) {
+ struct inštrukcija r = { 0 };
+ if (!(a*2 < e->pm_velikost))
+ return r;
+ r.vir = (e->pm[a*2] & ~1) >> 1;
+ r.destinacija = ((e->pm[a*2] & 1) << 6) | ((e->pm[a*2+1] & 0xfc) >> 2);
+ r.operacija = e->pm[a*2+1] & 3;
+ r.dobesedno = e->pm+a*2;
+ return r;
+}
+
+static void vhod_prazen (struct ebs * e __attribute__((unused))) {
+ return;
+}
+
+static void izhod_poln (struct ebs * e) {
+ putchar(e->izhod_medp);
+ fflush(stdout);
+ e->izhod_medp_indeks = 0;
+}
+
+void ebs_init (struct ebs * e) {
+ memset(e, '\0', sizeof *e);
+ e->peek = peek;
+ e->poke = poke;
+ e->inštrukcija = inštrukcija;
+ e->vhod_medp_indeks = 8;
+ e->izhod_medp_indeks = 0;
+ e->vhod_prazen = vhod_prazen;
+ e->izhod_poln = izhod_poln;
+}
+
+/**
+ * stanje izvajanja
+ */
+
+enum stanje {
+ nadaljuj,
+ konec,
+ čaka_vhod,
+ čaka_izhod
+};
+
+/**
+ * požene eno inštrukcijo
+ *
+ * @param e [in] držalo
+ * @return
+ */
+
+enum stanje ebs_delo (struct ebs * e) {
+ uint16_t prejšnji_pc = e->pc;
+ if (!HOOK peek(e, 16+1) && e->vhod_medp_indeks < 8) {
+ HOOK poke(e, 16+1, 1);
+ HOOK poke(e, 16+0, e->vhod_medp & (1 << (7-e->vhod_medp_indeks++)));
+ }
+ if (HOOK peek(e, 16+3) && e->izhod_medp_indeks < 8) {
+ HOOK poke(e, 16+3, 0);
+ if (HOOK peek(e, 16+2))
+ e->izhod_medp |= (1 << (7-e->izhod_medp_indeks++));
+ else
+ e->izhod_medp &= ~(1 << (7-e->izhod_medp_indeks++));
+
+ }
+ if (e->izhod_medp_indeks > 7)
+ e->izhod_poln(e);
+ struct inštrukcija š = HOOK inštrukcija(e, e->pc++);
+ switch (š.operacija) {
+ case nand:
+ HOOK poke(e, š.destinacija, !(HOOK peek(e, š.destinacija) && HOOK peek(e, š.vir)));
+ break;
+ case xor:
+ HOOK poke(e, š.destinacija, HOOK peek(e, š.destinacija) != HOOK peek(e, š.vir));
+ break;
+ case copy_ram:
+ ;
+ unsigned char buffer[16];
+ for (int i = 0; i < 16; i++)
+ buffer[i] = HOOK peek(e, š.vir+i);
+ for (int i = 0; i < 16; i++)
+ HOOK poke(e, š.destinacija+i, buffer[i]);
+ break;
+ case copy_pm:
+ ;
+ struct inštrukcija vir = HOOK inštrukcija(e, š.vir);
+ for (int i = 0; i < 16; i++)
+ HOOK poke(e, š.destinacija+i, vir.dobesedno ? vir.dobesedno[i/8] & (1 << (7-i%8)) : 0);
+ break;
+ }
+ /* fprintf(stderr, "0x%04x\t%s\t0x%02x\t0x%02x\t0x%02x%02x ", e->pc-1, operacija_str[š.operacija], š.vir, š.destinacija, š.dobesedno[0], š.dobesedno[1]);
+ for (int i = 0; i < 32; i++) {
+ if (peek(e, i))
+ fprintf(stderr, "1");
+ else
+ fprintf(stderr, "0");
+ if (i == 15)
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "\n"); */
+ if (e->pc == prejšnji_pc)
+ return konec;
+ return nadaljuj;
+}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..53ca54b
--- /dev/null
+++ b/main.c
@@ -0,0 +1,137 @@
+#include <ebs.c>
+#include <sys/mman.h>
+#include <error.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <time.h>
+#define S0(x) ((x) ? (x) : "")
+static unsigned long long micros (void) {
+ struct timespec tp;
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ return tp.tv_sec*1000000+tp.tv_nsec/1000;
+}
+struct nit {
+ atomic_int možen;
+ int pipe;
+};
+static void * pričakuj (void * ptr) {
+ struct nit * nit = (struct nit *) ptr;
+ struct pollfd pollfd[] = {
+ {
+ .fd = STDIN_FILENO,
+ .events = POLLIN
+ },
+ {
+ .fd = nit->pipe,
+ .events = POLLIN
+ }
+ };
+ while (1) {
+ int ret = poll(pollfd, 2, -1);
+ if (ret == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "poll");
+ nit->možen = 2;
+ return NULL;
+ }
+ if (pollfd[1].revents & POLLIN)
+ return NULL;
+ if (pollfd[0].revents & POLLIN)
+ nit->možen = 1;
+ }
+}
+int main (int argc, char ** argv) {
+ if (argc != 1+1)
+ error_at_line(1, 0, __FILE__, __LINE__, "%s program.ebs", S0(argv[0]));
+ int r = 0;
+ struct ebs ebs;
+ ebs_init(&ebs);
+ pthread_t vhod;
+ int pipefd[2];
+ int threadret = -1;
+ int pfd = -1;
+ if (pipe(pipefd) == -1)
+ error_at_line(2, errno, __FILE__, __LINE__, "pipe");
+ struct nit nit = {
+ .možen = 0,
+ .pipe = pipefd[0]
+ };
+ if ((threadret = pthread_create(&vhod, NULL, pričakuj, &nit))) {
+ error_at_line(0, threadret, __FILE__, __LINE__, "pthread_create");
+ r = 3;
+ goto r;
+ }
+ if ((pfd = open(argv[1], O_RDONLY | O_CLOEXEC)) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "open(%s)", argv[1]);
+ r = 4;
+ goto r;
+ }
+ struct stat statbuf;
+ if (fstat(pfd, &statbuf) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "fstat(pfd, &statbuf)");
+ r = 5;
+ goto r;
+ }
+ if (!(ebs.pm_velikost = statbuf.st_size)) {
+ error_at_line(0, errno, __FILE__, __LINE__, "!(ebs.pm_velikost = statbuf.st_size)");
+ r = 6;
+ goto r;
+ }
+ if (!(ebs.pm = mmap(NULL, ebs.pm_velikost, PROT_READ, MAP_SHARED, pfd, 0))) {
+ error_at_line(0, errno, __FILE__, __LINE__, "!mmap(NULL, ebs.pm_velikost, PROT_READ, MAP_SHARED, pfd, 0)");
+ r = 7;
+ goto r;
+ }
+ unsigned long long int začetek = micros();
+ unsigned long long inštrukcij = 0;
+ while (ebs_delo(&ebs) == nadaljuj) {
+ inštrukcij++;
+ if (!(inštrukcij % 100000000))
+ fprintf(stderr, "hitrost izvajanja: %llu op/us (MHz)\r", inštrukcij/(micros()-začetek));
+ switch (nit.možen) {
+ case 0: // ni vhoda, ni napake
+ break;
+ case 1: // imamo vsaj en bajt
+ if (ebs.vhod_medp_indeks > 7) {
+ char buf;
+ int got = read(STDIN_FILENO, &buf, 1);
+ if (got == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "read");
+ r = 8;
+ goto r;
+ }
+ ebs.vhod_medp_indeks = 0;
+ ebs.vhod_medp = buf;
+ nit.možen = 0;
+ }
+ break;
+ case 2: // napaka
+ error_at_line(0, 0, __FILE__, __LINE__, "napaka v vhodni niti");
+ r = 8;
+ goto r;
+ break;
+ }
+ }
+ fprintf(stderr, "konec izvajanja");
+ r:
+ if (!threadret) {
+ if (write(pipefd[1], "", 1) == -1)
+ error_at_line(0, errno, __FILE__, __LINE__, "write");
+ threadret = pthread_join(vhod, NULL);
+ if (threadret)
+ error_at_line(0, threadret, __FILE__, __LINE__, "pthread_join");
+ }
+ if (close(pipefd[0]) == -1)
+ error_at_line(0, errno, __FILE__, __LINE__, "close(pipefd[0])");
+ if (close(pipefd[1]) == -1)
+ error_at_line(0, errno, __FILE__, __LINE__, "close(pipefd[1])");
+ if (ebs.pm && munmap(ebs.pm, ebs.pm_velikost) == -1)
+ error_at_line(0, errno, __FILE__, __LINE__, "munmap(ebs.pm, ebs.pm_velikost)");
+ close(pfd);
+ pfd = -1;
+ return r;
+}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..5b5889b
--- /dev/null
+++ b/makefile
@@ -0,0 +1,26 @@
+DESTDIR=/
+CC=cc
+MYCFLAGS=-Ofast -march=native -Wall -Wextra -Wformat -pedantic -g -I. # -fsanitize=address
+MYLDFLAGS=-lpthread
+
+default: ebs disass
+
+ebs: main.c ebs.c
+ $(CC) $(MYCFLAGS) $(CFLAGS) main.c -o$@ $(MYLDFLAGS) $(LDFLAGS)
+
+disass: disass
+ $(CC) $(MYCFLAGS) $(CFLAGS) disass.c -o$@ $(MYLDFLAGS) $(LDFLAGS)
+
+install:
+ mkdir -p $(DESTDIR)/usr/bin/
+ cp ebs $(DESTDIR)/usr/bin/
+
+distclean: clean
+
+clean:
+ rm -rf ebs tmp
+
+valgrind:
+ valgrind --error-exitcode=59 --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt $(CMD)
+
+.PHONY: default install distclean clean valgrind ebs disass
diff --git a/spec.txt b/spec.txt
new file mode 100644
index 0000000..916fde6
--- /dev/null
+++ b/spec.txt
@@ -0,0 +1,48 @@
+specifikacija enobitnega stroja:
+
+to je stroj/procesor z ločenim programskim spominom in delovnim spominom.
+
+bit v delovnem spominu se naslavlja s sedmimi biti, zato je na voljo 128 bitov.
+
+programski spomin je velik za 65536 inštrukcij. ena inštrukcija je dolga 2 bajta in je sledeče oblike:
+
+ naslov-vir naslov-destinacija operacija
+
+naslova sta sedem bitna, operacija pa je dvobitna. inštrukcija je dolga 16 bitov, zato je lahko program velik do 128 kibibajtov.
+
+možne operacije so:
+
+ 00 kopiraj 16 bitov iz delovnega spomina v delovni spomin iz vira na destinacijo, bit po bit od +0 do +15
+ 01 kopiraj 16 bitov iz programskega spomina v delovni spomin iz vira na destinacijo, bit po bit od +0 do +15
+ 10 nand v naslov-destinacija shrani rezultat logične ne-in operacije med dvema bitoma na podanih naslovih v programskem spominu
+ 11 xor v naslov-destinacija shrani rezultat logične ekskluzivni-ali operacije med dvema bitoma na podanih naslovih v programskem spominu
+
+stroj ima števec, ki hrani lokacijo, od koder se izvaja program. ko se ena inštrukcija izvaja, kaže programski števec na naslednjo. izvajanje se začne na inštrukciji 0. programski števec je 16 bitna vrednost, shranjena v delovnem spominu na naslovih od 0 do 15 v obliki z najpomembnejšim bitom najprej.
+
+z delovnim spominom se zato operira na nivoju bitov in sklopov 16 bitov. operacija za kopiranje 16 bitov obstaja zato, da lahko v eni inštrukciji naredimo skok na poljuben naslov, tako da neko vrednost prekopiramo v delovni spomin na naslov 0 (programski števec). s to operacijo je moč brati iz programskega spomina zato, da se tam lahko hrani neke statične podatke/dobesedne vrednosti. seveda je mogoče delati tudi relativne skoke z bitnimi operacijami na območju programskega števca.
+
+izvorni naslov operacije za kopiranje 16 bitov iz programskega spomina naslavlja glede na 16 bajtov/glede na eno inštrukcijo, torej lahko dostopa samo do prvih 128 inštrukcij oziroma 256 bajtov, destinacijski naslov pa naslavlja glede na bit programskega spomina.
+
+operacija za kopiranje 16 bitov iz delovnega spomina pa naslavlja glede na bite, tako ko gre za izvorni kot tudi destinacijski naslov. če naslov nastavimo recimo na 1111111, bomo prebrali (ali napisali na) zadnji bit spomina in še 15 bitov izven spomina. seveda beremo preko spomina tudi če nastavimo naslov na recimo 1111110, tedaj preberemo 2 bita delovnega spomina in prvih 14 zunanjih bitov.
+
+obe operaciji najprej prekopirata v šestnajstbitni medpomnilnik, da lahko kopirata tudi območja, ki se prekrivajo.
+
+zemljevid naslavljanja
+ 16 bitov programskega števca
+ 4 vhodno/izhodni biti
+ 108 bitov delovenga spomina
+ 15 zunanjih bitov - delovni spomin
+
+vhodno/izhodni biti
+ in en bit vhoda v stroj
+ ina nov vhod v stroj je možen, program naj nastavi na 0, da dobi nov bit vhoda
+ out en bit izhoda iz stroja
+ outa program naj nastavi na 1, ko je v out napisal bit, ki ga želi poslati na izhod. emulator/strojna oprema bit počisti na 0, ko je prebrala izhod in je pripravljena na nov izhod
+
+branje iz out vrne zadnjo napisano vrednost
+ina in outa ob branju vedno vrneta resnično stanje, pisanje v ina in outa ne sme spremeniti tega stanja. to je zato, ker bi lahko vhod postal mogoč med izvajanjem kode, ki piše na izhod, s čimer bi se prepisalo z1 z ničlo - race condition, ki vodi v dos
+branje iz in vedno vrne zadnji sprejeti bit, pisanje v in iz istega razloga kot zgoraj ne prepiše te vrednosti
+
+po dogovoru, ko gre za bajte, se jih pošilja v vhod/izhod z najpomembnejšim bitom na začetku - big endian
+
+delovni spomin mora biti pred začetkom izvajanja nastavljen na same ničle, izjema je programski števec, ki je med izvajanjem prve inštrukcije 1, oba vhodna bita in bit, ki pove, da je izhod mogoč.