1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-26 10:28:05 +11:00

Compare commits

..

No commits in common. "66dacd1816b2fe1086a4e614df37d587b2b7ae2d" and "105acedab8f70c5301e98414f378842a247d4d91" have entirely different histories.

11 changed files with 323 additions and 431 deletions

View File

@ -13,11 +13,3 @@
* [Using block devices](blockdev.md)
* [Using the filesystem](fs.md)
* [Assembling z80 source from the shell](zasm.md)
## Hardware
Some consolidated documentation about various hardware supported by Collapse OS.
Most of that information can already be found elsewhere, but the goal is to have
the most vital documentation in one single place.
* [TI-83+/TI-84+](ti8x.md)

View File

@ -1,38 +0,0 @@
# TI-83+/TI-84+
Texas Instruments is well known for its calculators. Among those, two models
are particularly interesting to us because they have a z80 CPU: the TI-83+ and
TI-84+ (the "+" is important).
They lack accessible I/O ports, but they have plenty of flash and RAM. Collapse
OS runs on it (see `recipes/ti84`).
I haven't opened one up yet, but apparently, they have limited scavenging value
because its z80 CPU is packaged in a TI-specific chip. Due to its sturdy design,
and its ample RAM and flash, we could imagine it becoming a valuable piece of
equipment if found intact.
The best pre-collapse ressource about it is
[WikiTI](http://wikiti.brandonw.net/index.php).
## Getting software on it
Getting software to run on it is a bit tricky because it needs to be signed
with TI-issued private keys. Those keys have long been found and are included
in `recipes/ti84`. With the help of the
[mktiupgrade](https://github.com/KnightOS/mktiupgrade), an upgrade file can be
prepared and then sent through the USB port with the help of
[tilp](http://lpg.ticalc.org/prj_tilp/).
That, however, requires a modern computing environment. As of now, there is no
way of installing Collapse OS on a TI-8X+ calculator from another Collapse OS
system.
Because it is not on the roadmap to implement complex cryptography in Collapse
OS, the plan is to build a series of pre-signed bootloader images. The
bootloader would then receive data through either the Link jack or the USB port
and write that to flash (I haven't verified that yet, but I hope that data
written to flash this way isn't verified cryptographically by the calculator).
As modern computing fades away, those pre-signed binaries would become opaque,
but at least, would allow bootstrapping from post-modern computers.

View File

@ -8,8 +8,6 @@ screen. With a tiny font, the best we can get is a 24x10 console.
There is, however, a built-in USB controller that might prove very handy.
[Further reading](../../doc/ti8x.md)
## Recipe
This recipe gets the Collapse OS BASIC shell to run on the TI-84+, using its LCD

View File

@ -8,4 +8,3 @@
/cfsin/ed
/cfsin/basic
/cfsin/user.h
*.o

View File

@ -31,18 +31,15 @@ zasm/zasm-bin.h: zasm/zasm.bin
shell/shell: shell/shell.c libz80/libz80.o shell/kernel-bin.h
bshell/shell: bshell/shell.c libz80/libz80.o bshell/shell-bin.h
$(ZASMBIN): zasm/zasm.c emul.o libz80/libz80.o zasm/kernel-bin.h zasm/zasm-bin.h $(CFSPACK)
$(ZASMBIN): zasm/zasm.c libz80/libz80.o zasm/kernel-bin.h zasm/zasm-bin.h $(CFSPACK)
runbin/runbin: runbin/runbin.c libz80/libz80.o
$(TARGETS):
$(CC) $< emul.o libz80/libz80.o -o $@
$(CC) $< libz80/libz80.o -o $@
libz80/libz80.o: libz80/z80.c
$(MAKE) -C libz80/codegen opcodes
$(CC) -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c
emul.o: emul.c
$(CC) -c -o emul.o emul.c
$(CFSPACK):
$(MAKE) -C ../cfspack
@ -60,4 +57,4 @@ updatebootstrap: $(ZASMBIN) $(INCCFS)
.PHONY: clean
clean:
rm -f $(TARGETS) $(SHELLAPPS) emul.o zasm/*-bin.h shell/*-bin.h
rm -f $(TARGETS) $(SHELLAPPS) zasm/*-bin.h shell/*-bin.h

View File

@ -1,7 +1,7 @@
#include <stdint.h>
#include <stdio.h>
#include <termios.h>
#include "../emul.h"
#include "../libz80/z80.h"
#include "shell-bin.h"
/* Collapse OS shell with filesystem
@ -39,6 +39,8 @@
// 3 means incomplete addr setting
#define FS_ADDR_PORT 0x02
static Z80Context cpu;
static uint8_t mem[0x10000] = {0};
static uint8_t fsdev[MAX_FSDEV_SIZE] = {0};
static uint32_t fsdev_size = 0;
static uint32_t fsdev_ptr = 0;
@ -46,17 +48,16 @@ static uint32_t fsdev_ptr = 0;
static int fsdev_addr_lvl = 0;
static int running;
static uint8_t iord_stdio()
static uint8_t io_read(int unused, uint16_t addr)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
int c = getchar();
if (c == EOF) {
running = 0;
}
return (uint8_t)c;
}
static uint8_t iord_fsdata()
{
} else if (addr == FS_DATA_PORT) {
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return 0;
@ -73,10 +74,7 @@ static uint8_t iord_fsdata()
}
return 0;
}
}
static uint8_t iord_fsaddr()
{
} else if (addr == FS_ADDR_PORT) {
if (fsdev_addr_lvl != 0) {
return 3;
} else if (fsdev_ptr > fsdev_size) {
@ -87,19 +85,22 @@ static uint8_t iord_fsaddr()
} else {
return 0;
}
} else {
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
return 0;
}
}
static void iowr_stdio(uint8_t val)
static void io_write(int unused, uint16_t addr, uint8_t val)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
if (val == 0x04) { // CTRL+D
running = 0;
} else {
putchar(val);
}
}
static void iowr_fsdata(uint8_t val)
{
} else if (addr == FS_DATA_PORT) {
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return;
@ -119,10 +120,7 @@ static void iowr_fsdata(uint8_t val)
} else {
fprintf(stderr, "Out of bounds FSDEV write at %d\n", fsdev_ptr);
}
}
static void iowr_fsaddr(uint8_t val)
{
} else if (addr == FS_ADDR_PORT) {
if (fsdev_addr_lvl == 0) {
fsdev_ptr = val << 16;
fsdev_addr_lvl = 1;
@ -133,6 +131,22 @@ static void iowr_fsaddr(uint8_t val)
fsdev_ptr |= val;
fsdev_addr_lvl = 0;
}
} else {
fprintf(stderr, "Out of bounds I/O write: %d / %d (0x%x)\n", addr, val, val);
}
}
static uint8_t mem_read(int unused, uint16_t addr)
{
return mem[addr];
}
static void mem_write(int unused, uint16_t addr, uint8_t val)
{
if (addr < RAMSTART) {
fprintf(stderr, "Writing to ROM (%d)!\n", addr);
}
mem[addr] = val;
}
int main()
@ -165,27 +179,25 @@ int main()
tcsetattr(0, TCSAFLUSH, &termInfo);
Machine *m = emul_init();
m->ramstart = RAMSTART;
m->iord[STDIO_PORT] = iord_stdio;
m->iord[FS_DATA_PORT] = iord_fsdata;
m->iord[FS_ADDR_PORT] = iord_fsaddr;
m->iowr[STDIO_PORT] = iowr_stdio;
m->iowr[FS_DATA_PORT] = iowr_fsdata;
m->iowr[FS_ADDR_PORT] = iowr_fsaddr;
// initialize memory
for (int i=0; i<sizeof(KERNEL); i++) {
m->mem[i] = KERNEL[i];
mem[i] = KERNEL[i];
}
// Run!
running = 1;
Z80RESET(&cpu);
cpu.ioRead = io_read;
cpu.ioWrite = io_write;
cpu.memRead = mem_read;
cpu.memWrite = mem_write;
while (running && emul_step());
while (running && !cpu.halted) {
Z80Execute(&cpu);
}
printf("Done!\n");
termInfo.c_lflag |= ECHO;
termInfo.c_lflag |= ICANON;
tcsetattr(0, TCSAFLUSH, &termInfo);
emul_printdebug();
return 0;
}

View File

@ -1,87 +0,0 @@
/* Common code between shell, zasm and runbin.
They all run on the same kind of virtual machine: A z80 CPU, 64K of RAM/ROM.
*/
#include <string.h>
#include "emul.h"
static Machine m;
static uint8_t io_read(int unused, uint16_t addr)
{
addr &= 0xff;
IORD fn = m.iord[addr];
if (fn != NULL) {
return fn();
} else {
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
return 0;
}
}
static void io_write(int unused, uint16_t addr, uint8_t val)
{
addr &= 0xff;
IOWR fn = m.iowr[addr];
if (fn != NULL) {
fn(val);
} else {
fprintf(stderr, "Out of bounds I/O write: %d / %d (0x%x)\n", addr, val, val);
}
}
static uint8_t mem_read(int unused, uint16_t addr)
{
return m.mem[addr];
}
static void mem_write(int unused, uint16_t addr, uint8_t val)
{
if (addr < m.ramstart) {
fprintf(stderr, "Writing to ROM (%d)!\n", addr);
}
m.mem[addr] = val;
}
Machine* emul_init()
{
memset(m.mem, 0, 0x10000);
m.ramstart = 0;
m.minsp = 0xffff;
for (int i=0; i<0x100; i++) {
m.iord[i] = NULL;
m.iowr[i] = NULL;
}
Z80RESET(&m.cpu);
m.cpu.memRead = mem_read;
m.cpu.memWrite = mem_write;
m.cpu.ioRead = io_read;
m.cpu.ioWrite = io_write;
return &m;
}
bool emul_step()
{
if (!m.cpu.halted) {
Z80Execute(&m.cpu);
ushort newsp = m.cpu.R1.wr.SP;
if (newsp != 0 && newsp < m.minsp) {
m.minsp = newsp;
}
return true;
} else {
return false;
}
}
void emul_loop()
{
while (emul_step());
}
void emul_printdebug()
{
fprintf(stderr, "Min SP: %04x\n", m.minsp);
}

View File

@ -1,25 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include "libz80/z80.h"
typedef byte (*IORD) ();
typedef void (*IOWR) (byte data);
typedef struct {
Z80Context cpu;
byte mem[0x10000];
// Set to non-zero to specify where ROM ends. Any memory write attempt
// below ramstart will trigger a warning.
ushort ramstart;
// The minimum value reached by SP at any point during execution.
ushort minsp;
// Array of 0x100 function pointers to IO read and write routines. Leave to
// NULL when IO port is unhandled.
IORD iord[0x100];
IOWR iowr[0x100];
} Machine;
Machine* emul_init();
bool emul_step();
void emul_loop();
void emul_printdebug();

View File

@ -1,19 +1,44 @@
#include <stdint.h>
#include <stdio.h>
#include "../emul.h"
#include "../libz80/z80.h"
/* runbin loads binary from stdin directly in memory address 0 then runs it
* until it halts. The return code is the value of the register A at halt time.
*/
static Z80Context cpu;
static uint8_t mem[0x10000];
static uint8_t io_read(int unused, uint16_t addr)
{
addr &= 0xff;
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
return 0;
}
static void io_write(int unused, uint16_t addr, uint8_t val)
{
addr &= 0xff;
fprintf(stderr, "Out of bounds I/O write: %d / %d\n", addr, val);
}
static uint8_t mem_read(int unused, uint16_t addr)
{
return mem[addr];
}
static void mem_write(int unused, uint16_t addr, uint8_t val)
{
mem[addr] = val;
}
int main()
{
Machine *m = emul_init();
// read stdin in mem
int i = 0;
int c = getchar();
while (c != EOF) {
m->mem[i] = c & 0xff;
mem[i] = c & 0xff;
i++;
c = getchar();
}
@ -21,7 +46,15 @@ int main()
fprintf(stderr, "No input, aborting\n");
return 1;
}
emul_loop();
return m->cpu.R1.br.A;
Z80RESET(&cpu);
cpu.ioRead = io_read;
cpu.ioWrite = io_write;
cpu.memRead = mem_read;
cpu.memWrite = mem_write;
while (!cpu.halted) {
Z80Execute(&cpu);
}
return cpu.R1.br.A;
}

View File

@ -1,7 +1,7 @@
#include <stdint.h>
#include <stdio.h>
#include <termios.h>
#include "../emul.h"
#include "../libz80/z80.h"
#include "kernel-bin.h"
/* Collapse OS shell with filesystem
@ -39,6 +39,8 @@
// 3 means incomplete addr setting
#define FS_ADDR_PORT 0x02
static Z80Context cpu;
static uint8_t mem[0x10000] = {0};
static uint8_t fsdev[MAX_FSDEV_SIZE] = {0};
static uint32_t fsdev_size = 0;
static uint32_t fsdev_ptr = 0;
@ -46,17 +48,16 @@ static uint32_t fsdev_ptr = 0;
static int fsdev_addr_lvl = 0;
static int running;
static uint8_t iord_stdio()
static uint8_t io_read(int unused, uint16_t addr)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
int c = getchar();
if (c == EOF) {
running = 0;
}
return (uint8_t)c;
}
static uint8_t iord_fsdata()
{
} else if (addr == FS_DATA_PORT) {
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return 0;
@ -73,10 +74,7 @@ static uint8_t iord_fsdata()
}
return 0;
}
}
static uint8_t iord_fsaddr()
{
} else if (addr == FS_ADDR_PORT) {
if (fsdev_addr_lvl != 0) {
return 3;
} else if (fsdev_ptr > fsdev_size) {
@ -87,19 +85,22 @@ static uint8_t iord_fsaddr()
} else {
return 0;
}
} else {
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
return 0;
}
}
static void iowr_stdio(uint8_t val)
static void io_write(int unused, uint16_t addr, uint8_t val)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
if (val == 0x04) { // CTRL+D
running = 0;
} else {
putchar(val);
}
}
static void iowr_fsdata(uint8_t val)
{
} else if (addr == FS_DATA_PORT) {
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return;
@ -119,10 +120,7 @@ static void iowr_fsdata(uint8_t val)
} else {
fprintf(stderr, "Out of bounds FSDEV write at %d\n", fsdev_ptr);
}
}
static void iowr_fsaddr(uint8_t val)
{
} else if (addr == FS_ADDR_PORT) {
if (fsdev_addr_lvl == 0) {
fsdev_ptr = val << 16;
fsdev_addr_lvl = 1;
@ -133,6 +131,22 @@ static void iowr_fsaddr(uint8_t val)
fsdev_ptr |= val;
fsdev_addr_lvl = 0;
}
} else {
fprintf(stderr, "Out of bounds I/O write: %d / %d (0x%x)\n", addr, val, val);
}
}
static uint8_t mem_read(int unused, uint16_t addr)
{
return mem[addr];
}
static void mem_write(int unused, uint16_t addr, uint8_t val)
{
if (addr < RAMSTART) {
fprintf(stderr, "Writing to ROM (%d)!\n", addr);
}
mem[addr] = val;
}
int main()
@ -165,27 +179,25 @@ int main()
tcsetattr(0, TCSAFLUSH, &termInfo);
Machine *m = emul_init();
m->ramstart = RAMSTART;
m->iord[STDIO_PORT] = iord_stdio;
m->iord[FS_DATA_PORT] = iord_fsdata;
m->iord[FS_ADDR_PORT] = iord_fsaddr;
m->iowr[STDIO_PORT] = iowr_stdio;
m->iowr[FS_DATA_PORT] = iowr_fsdata;
m->iowr[FS_ADDR_PORT] = iowr_fsaddr;
// initialize memory
for (int i=0; i<sizeof(KERNEL); i++) {
m->mem[i] = KERNEL[i];
mem[i] = KERNEL[i];
}
// Run!
running = 1;
Z80RESET(&cpu);
cpu.ioRead = io_read;
cpu.ioWrite = io_write;
cpu.memRead = mem_read;
cpu.memWrite = mem_write;
while (running && emul_step());
while (running && !cpu.halted) {
Z80Execute(&cpu);
}
printf("Done!\n");
termInfo.c_lflag |= ECHO;
termInfo.c_lflag |= ICANON;
tcsetattr(0, TCSAFLUSH, &termInfo);
emul_printdebug();
return 0;
}

View File

@ -1,7 +1,7 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "../emul.h"
#include "../libz80/z80.h"
#include "kernel-bin.h"
#include "zasm-bin.h"
@ -48,6 +48,8 @@
// you want to spit this content to stderr.
//#define VERBOSE
static Z80Context cpu;
static uint8_t mem[0x10000];
// STDIN buffer, allows us to seek and tell
static uint8_t inpt[STDIN_BUFSIZE];
static int inpt_size;
@ -59,17 +61,16 @@ static uint32_t fsdev_size = 0;
static uint32_t fsdev_ptr = 0;
static uint8_t fsdev_seek_tell_cnt = 0;
static uint8_t iord_stdio()
static uint8_t io_read(int unused, uint16_t addr)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
if (inpt_ptr < inpt_size) {
return inpt[inpt_ptr++];
} else {
return 0;
}
}
static uint8_t iord_stdin_seek()
{
} else if (addr == STDIN_SEEK_PORT) {
if (middle_of_seek_tell) {
middle_of_seek_tell = 0;
return inpt_ptr & 0xff;
@ -80,19 +81,13 @@ static uint8_t iord_stdin_seek()
middle_of_seek_tell = 1;
return inpt_ptr >> 8;
}
}
static uint8_t iord_fsdata()
{
} else if (addr == FS_DATA_PORT) {
if (fsdev_ptr < fsdev_size) {
return fsdev[fsdev_ptr++];
} else {
return 0;
}
}
static uint8_t iord_fsseek()
{
} else if (addr == FS_SEEK_PORT) {
if (fsdev_seek_tell_cnt != 0) {
return fsdev_seek_tell_cnt;
} else if (fsdev_ptr >= fsdev_size) {
@ -100,18 +95,21 @@ static uint8_t iord_fsseek()
} else {
return 0;
}
} else {
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
return 0;
}
}
static void iowr_stdio(uint8_t val)
static void io_write(int unused, uint16_t addr, uint8_t val)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
// When mem-dumping, we don't output regular stuff.
#ifndef MEMDUMP
putchar(val);
#endif
}
static void iowr_stdin_seek(uint8_t val)
{
} else if (addr == STDIN_SEEK_PORT) {
if (middle_of_seek_tell) {
inpt_ptr |= val;
middle_of_seek_tell = 0;
@ -122,17 +120,11 @@ static void iowr_stdin_seek(uint8_t val)
inpt_ptr = (val << 8) & 0xff00;
middle_of_seek_tell = 1;
}
}
static void iowr_fsdata(uint8_t val)
{
} else if (addr == FS_DATA_PORT) {
if (fsdev_ptr < fsdev_size) {
fsdev[fsdev_ptr++] = val;
}
}
static void iowr_fsseek(uint8_t val)
{
} else if (addr == FS_SEEK_PORT) {
if (fsdev_seek_tell_cnt == 0) {
fsdev_ptr = val << 16;
fsdev_seek_tell_cnt = 1;
@ -146,13 +138,23 @@ static void iowr_fsseek(uint8_t val)
fprintf(stderr, "FS seek %d\n", fsdev_ptr);
#endif
}
}
static void iowr_stderr(uint8_t val)
{
} else if (addr == STDERR_PORT) {
#ifdef VERBOSE
fputc(val, stderr);
#endif
} else {
fprintf(stderr, "Out of bounds I/O write: %d / %d (0x%x)\n", addr, val, val);
}
}
static uint8_t mem_read(int unused, uint16_t addr)
{
return mem[addr];
}
static void mem_write(int unused, uint16_t addr, uint8_t val)
{
mem[addr] = val;
}
int main(int argc, char *argv[])
@ -161,22 +163,12 @@ int main(int argc, char *argv[])
fprintf(stderr, "Too many args\n");
return 1;
}
Machine *m = emul_init();
m->iord[STDIO_PORT] = iord_stdio;
m->iord[STDIN_SEEK_PORT] = iord_stdin_seek;
m->iord[FS_DATA_PORT] = iord_fsdata;
m->iord[FS_SEEK_PORT] = iord_fsseek;
m->iowr[STDIO_PORT] = iowr_stdio;
m->iowr[STDIN_SEEK_PORT] = iowr_stdin_seek;
m->iowr[FS_DATA_PORT] = iowr_fsdata;
m->iowr[FS_SEEK_PORT] = iowr_fsseek;
m->iowr[STDERR_PORT] = iowr_stderr;
// initialize memory
for (int i=0; i<sizeof(KERNEL); i++) {
m->mem[i] = KERNEL[i];
mem[i] = KERNEL[i];
}
for (int i=0; i<sizeof(USERSPACE); i++) {
m->mem[i+USER_CODE] = USERSPACE[i];
mem[i+USER_CODE] = USERSPACE[i];
}
char *init_org = "00";
if (argc >= 2) {
@ -186,8 +178,8 @@ int main(int argc, char *argv[])
}
}
// glue.asm knows that it needs to fetch these arguments at this address.
m->mem[0xff00] = init_org[0];
m->mem[0xff01] = init_org[1];
mem[0xff00] = init_org[0];
mem[0xff01] = init_org[1];
fsdev_size = 0;
if (argc == 3) {
FILE *fp = fopen(argv[2], "r");
@ -217,18 +209,25 @@ int main(int argc, char *argv[])
}
inpt_size = inpt_ptr;
inpt_ptr = 0;
Z80RESET(&cpu);
cpu.ioRead = io_read;
cpu.ioWrite = io_write;
cpu.memRead = mem_read;
cpu.memWrite = mem_write;
emul_loop();
while (!cpu.halted) {
Z80Execute(&cpu);
}
#ifdef MEMDUMP
for (int i=0; i<0x10000; i++) {
putchar(mem[i]);
}
#endif
fflush(stdout);
int res = m->cpu.R1.br.A;
int res = cpu.R1.br.A;
if (res != 0) {
int lineno = m->cpu.R1.wr.HL;
int inclineno = m->cpu.R1.wr.DE;
int lineno = cpu.R1.wr.HL;
int inclineno = cpu.R1.wr.DE;
if (inclineno) {
fprintf(
stderr,