• Stars
    star
    228
  • Rank 175,267 (Top 4 %)
  • Language
    Scheme
  • License
    MIT License
  • Created almost 6 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An assembler and operating system for the TI-84+ written in Scheme, Forth and Z80 assembly.

zkeme80 - a Forth-based OS for the TI-84+ calculator

Build Status

OS screenshot OS animation

TLDR: assembler.scm is the assembler, zkeme80.scm is the OS. To build the rom, run make build. There are no dependencies apart from a recent version of Guile, supporting the modules bytevectors and srfi-9 records. Other Scheme implementations have not been tested.

Alternatively, if you're using the Nix package manager on macOS or Linux, running nix-build && ./result in the root of this repository builds the OS and emulator, then runs it.

Why another OS for the TI-84+?

The TI tinkering community has long loathed the proprietary nature of the default TI-OS. Few projects have attempted to create a viable alternative, fewer have matured to a usable state, and none are currently able to actually let you use the calculator as a calculator.

If you've been looking at operating systems for the TI-84+, chances are you've come across KnightOS. It's well developed and has plenty of Unix-like features such as filesystems and tasks, and even a C compiler. But maybe that's not what you want. You want a minimal operating system that allows you to extend it in any way you wish, bonus points if you don't need to know Z80 assembly to do so.

zkeme80 is that operating system, a minimal core with a mostly ANS standard conforming Forth interpreter/compiler. From words covering sprites and graphics, to text and memory access, everything you need to make the next hit Snake clone or RPN-based math layer is already there. zkeme80 lowers the barrier of entry for customizing an operating system and enable rapid development cycles. Below the Forth layer, you'll find two lowest level and highest level languages, Z80 assembly and Scheme. The best assembler is an extensible one, where writing macros should be a joy, not a pain, and Scheme has that macro system.

On my MacBook Pro 11,1 running NixOS it takes around 13.5 seconds (real time) to compile the operating system for the first time with make build, and subsequent builds involving only changes to .fs files take around 0.5 seconds (real time).

Why Forth?

OS development is hard, doubly so if you're using assembly. Keep track of calling conventions, or which routines preserve which registers is a tedious and error-prone task. Nested loops and switch statements are out of the window. And most importantly, it isn't easy to allow the user to extend the operating system. Forth changes that. It's just as low level as assembly, but it can be as high level as you want. Want exceptions? They're already there! Want garbage collection and memory safety? Roll your own! See forth.scm for more than 200 examples of Forth words. If you're not familiar with Forth, I highly recommend Starting Forth by Leo Brodie. Get it here.

Notes on standard-compliance

Some words are not standard. This is because I copied them from my other Forth/Z80 project, which itself is based on jonesforth. However, I did consult the ANS standard to incorporate some of their good ideas. For instance, the test suite currently found in bootstrap-flash4.fs is only a very slight (sans the floating point stuff) adaptation of the offical test suite. The current version of the operating system runs a series of tests to check the correctness of the word environment. As time goes on I may consider making more words standard-conforming.

Did you write all of this?

Most of the assembly code outside of forth.scm was taken from SmileyOS, which itself is based on an older version of the KnightOS Kernel. I chose SmileyOS because it was the most "minimal" needed to get nasty stuff such as locking/unlocking flash, display routines, key routines etc. out of the way. Code here that doesn't exist in SmileyOS was taken from public sources, including the current version of KnightOS. The rest of the operating system is of my own design.

Building and running the operating system

Using the Makefile

Running make build should make generate a file called zkeme80.rom in the same directory. Simply pass that file to an emulator such as jsTIfied (works in the browser) and start playing around!

Running just make builds and runs the project, but assumes that you have already properly built tielm and can run it with tielm2 on the shell, and have Guile installed. Be warned, though, tilem is tricky to build and you have to enable all sorts of flags and install dependencies. If anyone knows a good emulator for macOS, please let me know.

Using the Nix package manager (macOS or Linux)

If you're using the Nix package manager, just clone the repository and run the following to compile and build the assembler, operating system, and emulator. It will automatically run the ROM when done. Props to clever on #nixos for figuring out how to build tilem.

# With flakes
$ nix run
# Without flakes
$ nix-build && ./result

Files included

  • assembler.scm assembles s-exp style assembly code into binary. Simply run (load "assembler.scm") into your Scheme REPL and run(assemble-prog sample-prog) to see the binary data. Run (assemble-to-file sample-prog "out.bin") to write a binary file.
  • zkeme80.scm is the Forth-based operating system. Load zkeme80.scm then run (make-rom "zkeme80.rom") to output binary to a file zkeme80.rom.

Design of the assembler

The assembler's core uses pattern matching. The program counter is implemented as a mutable Scheme object *pc*. Labels are kept in a global alist *labels*. To allow for the use of jumps that refer to labels declared after it, we use multiple passes. The assembler is designed to be extensible from various levels; the source code of the assembler, pass 1 and pass 2. Each layer can be extended using the full power of Scheme.

The extensible nature of the assembler means that users can add whatever features they desire that were not built in already, for instance, re-targeting the assembler or adding missing instructions.

Structure of assembly programs

Assembly programs consist of a list of elements that are either expressions or procedures.

Pass 1

Handling expressions

Each expression of a program is passed to assemble-expr (which also checks if they're well-formed). assemble-expr returns a record type that has the following fields (for a normal instruction):

Record entry Type Description
length integer The length of the instruction, in bytes.
gen-instr lambda Thunk that computes the actual instruction bytes.

The use of converting expressions into record types like this allows us to compute the length of the program (and resolve look ahead labels).

Handling procedures

Procedures (Scheme objects that satisfy the predicate procedure?) that are embedded in a program must be able to be run without any arguments, and return either () or an instruction record. This is the main extension mechanism for the assembler. For instance, in macros.scm there is a procedure called fill-until-end which creates a list of bytes so that the total binary is #x100000 bytes long.

Pass 2

Once the program makes it through Pass 1, we perform code generation and label resolution. All instruction records are required to have a length property that tells in advance how many bytes will be generated from the thunk. Consistency between this number and what the thunk outputs is checked. Each instruction record is also checked that it generates only unsigned 8-bit integers. The result is flattened into a list of unsigned numbers, which can be manipulated as the user wishes.

Debugging

The debugging process is pretty simple. One just has to write a valid Z80 assembly program in my s-exp format and run it through a disassembler then compare the output. If you're feeling particularly brave you may skip this step and try your program out on a Z80 chip.

Assembler Limitations

There is currently no instruction encoding (like the z80data.tab file) that the assembler accepts, so to add new instructions the current workflow is to look at relevant portions of the Z80 data sheet and write new cases in the pattern matcher. Adding such an encoding would allow the assembler to be retargeted.

More Repositories

1

freenode-exodus

Projects and channels that have decided to leave Freenode. (Final leave count: 1056)
Haskell
135
star
2

ti84-forth

A Forth implementation for the TI-84+ calculator.
Assembly
86
star
3

mini-haskell

A self-hosting mini Haskell compiler with a mini C runtime.
Haskell
70
star
4

fp-notes

Notes on Functional Programming and related topics
Haskell
28
star
5

heptapod-diffusion

Generating Heptapod writing with Stable Diffusion
25
star
6

r216-forth

A Forth implementation for the R216K8B Powder Toy computer.
Assembly
24
star
7

dotfiles

A collection of my dotfiles
Emacs Lisp
23
star
8

nix-sudoku

Sudoku solver in Nix
Nix
22
star
9

r5rs-denot

A correct Scheme interpreter derived from the R5RS spec's formal semantics, written in Haskell.
Haskell
20
star
10

meta-yacc

Self-hosting parser generators in Forth and C.
Forth
18
star
11

meta-II

META II compiler, assembler and bytecode interpreter.
TeX
17
star
12

eopl

Implementation of the languages from the EOPL textbook in Haskell and Standard ML.
Haskell
15
star
13

awk-vm

A virtual machine and assembler written in AWK.
Awk
13
star
14

evm-assembler

No-dependencies assembler for the EVM with examples (Ξ»-calculus, linked lists, etc.)
Python
13
star
15

haoc-2020

Haskell Advent of Code 2020
Haskell
8
star
16

monadic-parsing

Monadic parsing in Scheme.
Scheme
8
star
17

coq-wigderson

Formalization of Wigderson's graph coloring algorithm in Coq
Coq
8
star
18

tree-sitter-promela

Promela grammar for tree-sitter
C
7
star
19

hasktran

A compiler for FRACTRAN written in Haskell.
Haskell
7
star
20

haoc-2021

Haskell Advent of Code 2021
Haskell
7
star
21

ts-lint-example

Minimal linting example with tree-sitter
JavaScript
6
star
22

nix-workshop

Outline for a workshop on Nix, to be given sometime in the 2020-2021 academic calendar.
6
star
23

mes-overlay

Nix overlay for MES-related packages
Nix
6
star
24

wordle

Wordle solver
Haskell
5
star
25

emotivoice-cli

CLI wrapper around Emotivoice TTS Synthesis
JavaScript
5
star
26

sicp-to-z80

A SICP register machine to TI-84 Z80 compiler.
Scheme
4
star
27

lisp-to-js

Compiling Lisp to JavaScript
Haskell
4
star
28

bf

Fast, simple brainfuck interpreter in C
C
4
star
29

tree-sitter-imp

Imp grammar for tree-sitter
C
4
star
30

scheme-to-c

A toy Scheme to C compiler
Scheme
3
star
31

brownies

This a game I hacked together with Biwascheme.
HTML
3
star
32

tree-sitter-formula

Formula grammar for tree-sitter
C
3
star
33

nix-challenges

Increasingly difficult building challenges in Nix with solutions
Nix
3
star
34

vpl

VPL (Visual Programming Language) is an educational Turtle language using Haskell and Gloss
Haskell
3
star
35

website

My website
Nix
2
star
36

siraben-overlay

@siraben's eclectic collection of software
Nix
2
star
37

remarkable-hello

Example of compiling Rust to a static ARM executable. Target here is the reMarkable tablet.
Nix
2
star
38

senior-thesis

LaTeX sources for my undergraduate thesis
TeX
2
star
39

llama-bot

Discord bot for interacting with the LLaMA language model
Python
2
star
40

knightos-nix

Compiling KnightOS end-to-end using Nix
Nix
1
star
41

coq-rings

Ring Theory in Coq from Scratch
Coq
1
star
42

proplogic

Exploration of theorems in propositional logic in Coq
Coq
1
star
43

functional-images

Replicating the Functional Images paper by Conal Elliott using Haskell, C and SDL2.
Haskell
1
star
44

linear-lc

A typechecker for linear lambda calculus.
Haskell
1
star
45

vu-catalog

Fetch and parse Vanderbilt course information
Python
1
star
46

ti84-forthos

A Forth-based OS for the TI-84+ calculator. Now succeeded by https://github.com/siraben/zkeme80
Assembly
1
star