The Simple8 Environment
Before you write your first line of code, this chapter introduces the tools: what the compiler does, the machines it knows, its libraries, how to install and run it, and then how to set up Simple8 IDE.
1.1What Simple8 is
Simple8 is both a language and a compiler.
The language looks like Java and C: blocks between braces, types
(byte, int, uint…), functions, for
and while loops. The compiler translates this code into native
assembly for the chosen machine, assembles it, and produces the file you load into an
emulator or onto the real machine: a PC Engine ROM, a Commodore 64 .prg,
an Amiga floppy disk…
In its current version, Simple8 was designed for small 8-bit processors: the 6502 and its derivatives (HuC6280, 6510, 2A03), the Z80, and the Game Boy's SM83. Its types, its memory management and its generated code are tailored for machines that have few registers, a zero page and a few kilobytes of RAM. The 68000 machines are supported on an experimental basis (see the architectures).
A Simple8 program always states, on its first line, which machine it is written for:
architecture nec.pce; // this program is for the PC Engine
Everything else follows from it: the target processor, how memory is organized, the available libraries and the format of the output file.
The complete environment is made of:
- the compiler, a Java program shipped as a
LitleCompilator.jarfile, which you run from the command line; - the libraries, the
libs8/folder, which give access to each machine's hardware (display, sprites, sound, joypads…); - Simple8 IDE, the editor that drives the compiler and launches the emulators;
- and, for graphics, SquareDot and 3DSpriteMaker, covered in their own chapters.
1.2How the compiler works
The compiler starts from the main file (main.s8p) and ends with a file
ready to run. In between, the program goes through a chain of steps:
In detail:
- ReadingThe main file, its libraries
(
addlib) and its units (addunit) are read and split into a syntax tree. - AnalysisTypes, variables, scopes and function calls are checked; errors are reported with their line number.
- Intermediate representationThe program is rewritten as simple instructions that are still independent of the processor.
- Dead codeFunctions that are never called, including those in libraries, are discarded.
- GenerationThe processor's generator (6502, Z80, SM83, 68000) writes the assembly, then optimizes it.
- AssemblingThe built-in assembler produces the final file, the listings and the reports.
Everything happens in the same program: there is no external tool to
install, neither an assembler nor a linker. The compiler writes its output
into an asm/ folder, next to the compiled file:
| File | Contents |
|---|---|
main.asm | the generated assembly, readable, with the code of the libraries used |
main.lst | the listing: each instruction with its address and its bytes |
main.sym | the symbol table, read by debugging emulators |
main_report.html | the compilation report: memory used, functions, discarded code |
main.pce, main.prg… | the program to run, in the machine's format |
Reading the generated assembly is the best way to understand what a line of Simple8 costs. The following chapters will come back to it often.
When abstraction is not enough, the language lets you go down to the hardware level:
an _asm { … } block inserts assembly in the middle of the code, and the
compiler checks the instructions against the machine's processor.
1.3The architectures
An architecture designates a specific machine. Its name is written
manufacturer.machine, and it is what follows the architecture
keyword at the top of the program.
| Architecture | Processor | Machine | Output file |
|---|---|---|---|
nec.pce | HuC6280 | PC Engine / TurboGrafx-16 | .pce |
nec.sgx | HuC6280 | PC Engine SuperGrafX | .pce and .sgx |
commodore.c64 | MOS 6510 | Commodore 64 | .prg, .d64 |
nintendo.nes | Ricoh 2A03 | NES / Famicom | .nes |
mos.6502 | MOS 6502 | Generic 6502 | raw binary |
amstrad.cpc6128 | Z80A | Amstrad CPC 464 / 664 / 6128 | .bin, .dsk |
zilog.z80 | Z80 | Generic Z80 (MSX, Master System, Spectrum) | .bin |
nintendo.dmg | Sharp SM83 | Game Boy | .gb |
sega.megadrive | 68000 | Mega Drive / Genesis (experimental) | .md |
commodore.amiga | 68000 | Amiga 500 PAL / OCS (experimental) | .adf (floppy disk) |
The language is the same everywhere. What changes from one machine to another is the libraries (a PC Engine and an Amstrad CPC do not have the same hardware sprites), the available memory and the size of the numbers the processor handles effortlessly.
The 68000 architectures are experimental. Today, Simple8 is designed for 8-bit processors. The Mega Drive and the Amiga 500 work, but their support is still experimental: the language and the generated code do not yet take advantage of a 16/32-bit processor. A future version of Simple8 should improve development on the 68000.
motorola.68000, for its part, is recognized but produces no code:
for the 68000, you choose a concrete machine, sega.megadrive or
commodore.amiga.
1.4The libraries
Libraries are .s8l files, written in Simple8 (often with
a bit of assembly). They are stored in the libs8/ folder, one branch
per machine:
libs8/
├── nec/pce/ PC Engine
│ ├── boot/boot.asm machine startup
│ ├── system/ the system library
│ ├── config.json the machine's memory map
│ ├── vdc/ display: tiles, sprites, scrolling
│ ├── vce/ colors
│ ├── psg/ sound
│ └── io/ the joypad
├── commodore/c64/
├── nintendo/nes/
├── …
└── math/ libraries shared by all machines
A program loads a library with addlib, followed by the folder and the file
name, separated by a dot:
architecture nec.pce;
addlib vdc.base; // libs8/nec/pce/vdc/base.s8l
addlib io.base; // libs8/nec/pce/io/base.s8l
The compiler looks for the file in this order, and takes the first one it finds:
- the machine's folder:
libs8/nec/pce/vdc/base.s8l; - the shared libraries:
libs8/vdc/base.s8l; - your personal libraries, in the folder given by the
S8_USER_LIBSvariable; - the project's libraries, in its
userlibs/folder.
Thanks to dead code elimination, loading a library costs nothing: only the functions that are actually called end up in the ROM.
1.5Installing Simple8
What you need
| Tool | What it is for |
|---|---|
| Java (JDK 21) | building and running the compiler |
| Git | getting the sources |
| Maven or Ant | building the compiler |
Getting and building the compiler
-
Get the repository:
git clone https://github.com/beddy70/Simple8.git cd Simple8 -
Build the compiler, with either Ant or Maven:
# Ant: the file is written to dist/ ant compile jar # Maven: the file is written to target/, and the tests are run mvn package -
Check that it responds:
java -jar dist/LitleCompilator.jar --help
Telling it where the libraries are
The compiler needs to know where the libs8/ folder is. The simplest way
is to declare it once and for all in the S8_LIBS_PATH
environment variable:
# macOS, Linux: add to ~/.zshrc or ~/.bashrc
export S8_LIBS_PATH=/chemin/vers/Simple8/libs8
export S8_USER_LIBS=~/mes_libs_simple8 # optional
# Windows
setx S8_LIBS_PATH C:\Simple8\libs8
Without S8_LIBS_PATH or the -l option, the
compiler does not look for the libraries in the current folder: it cannot find
them, and reports Library not found.
1.6Compiling from the command line
You run the compiler with Java, giving it the project's main file:
java -jar LitleCompilator.jar -f main.s8p
| Option | Effect |
|---|---|
-f, --file file | the .s8p file to compile (you can also give it on its own, without -f) |
-l, --libs folder | the libs8/ folder; takes precedence over S8_LIBS_PATH |
-d, --debug | shows the internal steps: tree, intermediate representation, assembly |
-h, --help | help |
Run the command from the project folder. The
asm/ folder is created in the current folder: compiling from somewhere else
would put the generated files there.
Trying an example
The repository contains example programs, sorted by machine in
Examples/. To compile the first NES program:
cd Examples/nintendo/nes/HelloNES
java -jar ../../../../dist/LitleCompilator.jar -l ../../../../libs8 main.s8p
If all goes well, the compilation finishes without errors and the asm/ folder
contains main.nes, a cartridge that runs directly in Mesen,
FCEUX or Nestopia.
1.7Simple8 IDE
The command line is enough, but Simple8 IDE makes the work more comfortable: you write, compile and launch the emulator without leaving the application. Compilation errors are underlined in the code, and the generated assembly can be read in a neighboring tab.
Installing it
Ready-to-install versions coming soon. Compiled versions of Simple8 IDE for macOS, Windows and Linux will be available. An online version of the Simple8 suite that runs in a browser is also on the way. In the meantime, the IDE is built from source, as described below.
The IDE is built from the repository's ide/ folder. In addition to
Java, it requires Rust (stable version) and Node.js 20 or
later:
cd ide
npm install
npm run tauri build # the application, ready to install
npm run tauri dev # or: run the IDE directly from source
Opening a project
File → Open Project… (⌘O): choose the project's folder. The IDE then looks there for:
- the compiler:
dist/LitleCompilator.jarin the project folder, then in each of its parent folders. A project stored inside the repository therefore finds it on its own. Otherwise: Project → Choose Compiler…; - the main file:
main.s8p, or failing that the first.s8pin the folder. It is always the one that gets compiled, whichever tab is open; - the libraries: the
libs8/folder next todist/, or the one inS8_LIBS_PATH.
File → New Project… (⇧⌘N) asks for a name,
an architecture and a folder, then creates a main.s8p ready to fill in.
On macOS, an application opened from the Finder or the Dock does not see
the terminal's variables (S8_LIBS_PATH, S8_USER_LIBS).
If you need them, launch the IDE from a terminal.
Essential shortcuts
| Action | Shortcut |
|---|---|
| Compile the main file | ⌘B |
| Compile, then run in the emulator | ⌘R |
| Open the generated assembly | ⌥⌘A |
| Open the listing | ⌥⌘L |
| Open the compilation report | ⌥⌘R |
| Configure the emulators | ⌘, |
During compilation, the compiler's output scrolls in the Output tab. Errors and warnings are gathered in the Problems tab: a click opens the file at the offending line.
Git
Simple8 IDE also manages the project's Git repository, without going through the terminal. Everything is set up in Settings → Git…:
- the repository: if it does not exist, the IDE offers to create it, with the
mainbranch and a suitable.gitignore. It then shows the branch, the commits ahead or behind and the number of modified files; - the identity: the name and e-mail for this project's commits;
- the
.gitignore: the Simple8 model button adds what should not be tracked, starting with the generatedasm/folder; - the servers: add, edit or remove the remote repositories, and choose the ones to push your work to. Passwords and access tokens are handed to Git's credential manager, never to an IDE file;
- commit and push: the list of changed files, a message, then Commit or Commit and push.
| Action | Shortcut |
|---|---|
Commit all changes in the project (git commit) | ⇧⌘K |
Push the branch to the chosen servers (git push) | ⇧⌘P |
1.8Configuring the emulators
The ▶ Run button compiles the program and then, if the compilation succeeds, opens the output file in the chosen emulator. The list only shows the emulators compatible with the program's machine.
The IDE knows a default one for each machine:
| Machine | Default emulator | Other supported choices |
|---|---|---|
| PC Engine | Geargrafx | ares, Mednafen |
| SuperGrafX | Geargrafx | Mednafen |
| Commodore 64 | VirtualC64 | VICE |
| Amstrad CPC | Caprice32 | CPCemu |
| NES | Mesen | |
| Game Boy | Gearboy | |
| Mega Drive | ares | |
| Amiga 500 | FS-UAE |
The emulators themselves are installed separately. Then the Settings → Emulators… window (⌘,) shows for each one whether its executable was found (●) or not (○), and lets you choose its path and the default emulator for each machine.
The emulators.json file
To go further, the configuration is a JSON file, opened with
Settings → Edit emulators.json. A project can also have its
own simple8-emulators.json file, which overrides the general
configuration entry by entry:
{
"defaults": { "nec.pce": "ares-pce" },
"emulators": {
"ares-pce": {
"name": "ares (PC Engine)",
"command": "/Applications/ares.app/Contents/MacOS/ares",
"args": ["--system", "PC Engine", "--no-file-prompt", "${rom}"],
"architectures": ["nec.pce"],
"rom": "${asmDir}/${name}.pce"
}
}
}
| Variable | Value |
|---|---|
${projectDir} | the folder of the compiled file |
${name} | its name, without the extension |
${asmDir} | the project's asm/ folder |
${rom} | the file to run |
${sym} | the symbol table, for debugging emulators |
1.9How a project is organized
A Simple8 project is just a folder:
MonJeu/
├── main.s8p the main program
├── Joueur.s8u other units, added with addunit (optional)
├── userlibs/ libraries specific to the project (optional)
├── simple8-emulators.json the project's emulators (optional)
└── asm/ created by the compiler
| Extension | Role |
|---|---|
.s8p | the program: it declares the architecture and contains the main unit, with main() |
.s8u | a unit in its own file, to split up a growing program |
.s8l | a library, loaded with addlib |
The asm/ folder is fully regenerated on every compilation: you can
delete it safely, and it does not need to be kept in Git (the IDE's .gitignore template excludes it).
The environment is ready. In the next chapter, you will write your first program.
