{"id":34970710,"url":"https://github.com/howerj/lfsr","last_synced_at":"2026-05-22T01:01:35.001Z","repository":{"id":255542371,"uuid":"850807020","full_name":"howerj/lfsr","owner":"howerj","description":"A VM that uses LFSR instead of a normal program counter that runs Forth","archived":false,"fork":false,"pushed_at":"2026-04-11T15:50:52.000Z","size":293,"stargazers_count":15,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-11T17:26:30.409Z","etag":null,"topics":["7400","c","forth","fpga","ic","lfsr","vm"],"latest_commit_sha":null,"homepage":"","language":"Forth","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"0bsd","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/howerj.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-09-01T20:38:38.000Z","updated_at":"2026-04-11T15:50:56.000Z","dependencies_parsed_at":"2024-09-06T01:12:50.986Z","dependency_job_id":"67b8b243-2535-4a87-b8bd-cb7a06b73cb7","html_url":"https://github.com/howerj/lfsr","commit_stats":null,"previous_names":["howerj/lfsr"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/howerj/lfsr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Flfsr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Flfsr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Flfsr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Flfsr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/howerj","download_url":"https://codeload.github.com/howerj/lfsr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Flfsr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33322665,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-21T12:23:38.849Z","status":"ssl_error","status_checked_at":"2026-05-21T12:22:11.673Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["7400","c","forth","fpga","ic","lfsr","vm"],"created_at":"2025-12-26T23:44:50.422Z","updated_at":"2026-05-22T01:01:34.986Z","avatar_url":"https://github.com/howerj.png","language":"Forth","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LFSR CPU/VM running Forth\n\n* Author: Richard James Howe\n* License: 0BSD / Public Domain\n* Email: \u003cmailto:howe.r.j.89@gmail.com\u003e\n* Repo: \u003chttps://github.com/howerj/lfsr\u003e\n\nThis project contains a Virtual Machine (VM), which will be referred\nto as a CPU from now one to avoid confusion with a second VM,\nand a program image containing an implementation of the programming\nlanguage Forth for this CPU that uses a Linear Feedback Shift Register\n(LFSR) instead of a normal program counter. The reason for doing this\n(historically) is that a normal program counter requires an adder\nwhich use more gates than a LFSR. The problem with using an LFSR as\na counter is that the program is scrambled.\n\nTo see an implementation of this system on an FPGA visit\n\u003chttps://github.com/howerj/lfsr-vhdl\u003e. It is written in VHDL.\n\nFrom a software point of view a LFSR (both the CPU and the tool-chain)\nappear more complex, only in hardware are LFSR simpler.\n\nUsing a LFSR is quite a good choice when gates are at a premium and\nthe counter value does not have to be visible to a user of it (such\nas a delay counter, or a counter for a stack).\n\nNote that because of the pedigree of this Forth (which assumes you are\ntalking to it over a UART) that it repeats every character typed into\nit, it is also does not halt on EOF (as EOF/-1 is used to indicate\nthat there is no character currently available in the hardware). This\ncould be changed if needed.\n\nUsing a LFSR register instead of a normal counter creates a level of\nobfuscation, if the LFSR polynomial had to be keyed in at startup it\nwould form a key. This is more of an idea than anything practical.\n\n# Use cases\n\n* Just for fun. \n* As a proof of concept.\n* Extremely small CPUs in constrained environments, it has a similar\nset of possible uses as \u003chttps://github.com/howerj/bit-serial\u003e.\n\n# Usage\n\nType `make run` (requires `make` and a C compiler), an examples session:\n\n\t2 2 + . cr\n\t: ahoy cr .\" HELLO, WORLD\" ;\n\tahoy\n\tbye\n\nType `gforth lfsr.fth` to rebuild `lfsr.hex`, a pre-built hex file\ncontaining an implementation of Forth is already provided.\n\nThis `readme.md` will not contain a Forth tutorial, look elsewhere\nfor one.\n\nFor a list of all defined functions in Forth type `words`.\n\nHitting `CTRL-D` **Will not** cause the system to exit.\n\n# Instruction Set and System Design\n\nA short summary of the instruction set and design:\n\n* The machine is an accumulator based machine.\n* All instructions are 16-bits in length.\n* The top four bits determine the instruction.\n* The lowest four bits are the operand parameter.\n* Addresses are in number of 16-bit cells, not bytes.\n* The program counter is 8-bits in size and is advanced with a LFSR.\n* The top most bit determined whether the operand is used directly or \nwhether or it loaded first.\n\nThe instruction set has been carefully chosen so that it\nshould be very simple to implement in a traditional manner,\nor in a bit-serial fashion (much like my other CPU project at\n\u003chttps://github.com/howerj/bit-serial\u003e that runs on an FPGA). It has\nalso been designed so that it should be possible to implement in 7400\nseries logic ICs (excluding ROM and RAM) as well as on an FPGA. There\nmay be no savings in logic on an FPGA due to the fact that slices\nhave a built in carry chain (usually), but a comparison could be made\nwhen the system is implemented on an FPGA (This has been done, there\nis very little difference).\n\nAs the Program Counter does not use addition, it was decided that\naddition should be removed from the instruction set. It would be a\nbit weird to worry about all the gates the Program Counter is using and\nthen just include an adder elsewhere. The ADD instruction is sorely\nmissed and makes the Forth code more complex and slows it down. The\nonly other instruction that feels like it should be present (to me)\nis bit-wise OR, in practice it is not used that much within the\ninterpreter so it not missed, it is reimplemented using bitwise AND\nand INVERT (XOR against all bits set).\n\nThe instruction layout is:\n\n\t+----------+---------------+-------------------+\n\t|  BIT 15  | BITS 12 to 14 |    BITS 0 to 11   |\n\t+----------+---------------+-------------------+\n\t| INDIRECT | INSTRUCTION   | VALUE   / ADDRESS |\n\t+----------+---------------+-------------------+\n\nThe `INDIRECT` flag determined whether bits 0 to 11 are treated as\na value (not set) or an address (`INDIRECT` is set).\n\nThe `INSTRUCTION` field is 3-bits in size, the instructions are:\n\n\t0 : XOR   : ACC = ACC ^ ARG\n\t1 : AND   : ACC = ACC \u0026 ARG\n\t2 : LLS1  : ACC = ARG \u003c\u003c 1\n\t3 : LRS1  : ACC = ARG \u003e\u003e 1\n\t4 : LOAD  : ACC = MEM[ARG]\n\t5 : STORE : MEM[ARG] = ACC\n\t6 : JUMP  : PC = ARG\n\t7 : JUMPZ : IF (ACC == 0) { PC = ARG }\n\nAll instructions advance the program counter except `JUMP`, and\n(conditionally) `JUMPZ`. All instruction affect or use the accumulator\nexcept the JUMP instruction, and all arguments use the `ARG` value.\n`MEM` consists of a linear array of 16-bit values.\n\nThere is one special address, address 0. This address is never\nincremented after an instruction is run as this is a lock up state for\nthe LFSR, the current instruction will be executed indefinitely. A way\nto exit this condition is for the first instruction to be a jump to\naddress `1`, however any non-zero address will do. The system starts\nup executing from address zero. Conditional jumps could be used to\ndetermine whether to reset or halt the system if needed. Alternatively\na LFSR that used XNOR could have been used (the lockup state for which \nis all ones) but it was not.\n\n`ACC` is the 16-bit accumulator.\n\n`ARG` is either the zero extended 12-bit operand (the lowest 12-bits\nof the instruction) as is, or the 16-bit value `MEM[operand]` if the\ntopmost bit of the instruction is set.\n\nInput and Output is memory mapped, reading from a negative address\n(high bit set) causes a byte to be read or output. This is triggered\nfrom reading or writing to any negative address, if multiple\nperipherals are to be added the address will have to be decoded\ncorrectly.\n\nFor the purposes of simulation `JUMP` will cause the CPU to halt if\nthe jump address is the same as the program counter. This will not\nbe implemented in hardware.\n\nThe program counter uses a 8-bit LFSR to advance, that means only 256\n16-bit values can be directly addressed by this CPU, this is not a\nlimitation that matters for the purpose of the software running on this\nsystem, a complete Forth Programming Language image, as the Virtual\nMachine that supports Forth can be written in under 256 instructions\nfor this system. That Forth Virtual Machine can address more memory\nby using LOAD/STORE to access values outside the 256 instruction range.\n\nThis instruction set might change depending on the implementation,\nor when it comes to implementation, to make things easier and smaller\nstill.\n\n# To Do\n\n* [x] Get basic implementation working\n* [x] Port a Forth implementation to the machine.\n  - [ ] Make a build target for a VM that is exactly the same except\n  it uses normal program counter. (optional)\n* [x] Optimize machine (remove Add instruction, put in indirect bit)\n* [x] Port to an FPGA\n  - [x] See \u003chttps://github.com/howerj/lfsr-vhdl\u003e\n  - [ ] make a bit-serial version? (optional), it is not clear this will\n  save space.\n* [ ] Implement in 7400 series logic?\n  - [ ] Separate the eForth image into `ROM` and `RAM` sections. We\n  should able to make the first X KiB of the image read-only. This might\n  mean cutting the image down to get it under a 4KiB limit and moving\n  variables.\n\n# References\n\n* \u003chttps://en.wikipedia.org/wiki/Linear-feedback_shift_register\u003e\n* \u003chttps://github.com/howerj/subleq\u003e\n* \u003chttps://github.com/howerj/7400\u003e\n* \u003chttps://github.com/howerj/bit-serial\u003e\n* \u003chttps://en.wikipedia.org/wiki/Forth_(programming_language)\u003e\n* \u003chttps://stackoverflow.com/questions/1149929\u003e\n* \u003chttps://www.fpga4fun.com/Counters3.html\u003e\n* \u003chttps://groups.google.com/g/comp.lang.vhdl/c/GiFdsYfaeHA\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhowerj%2Flfsr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhowerj%2Flfsr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhowerj%2Flfsr/lists"}