1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-02 16:20:55 +11:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Virgil Dupras
66dacd1816 tools/emul: add "Min SP" debug value
This gives the maximum size of the stack at any given moment during the
execution of the program. It's useful to figure out if the stack gets
dangerously close to the heap.
2019-12-02 17:44:54 -05:00
Virgil Dupras
2f07d849a8 tools/emul: deduplicate a little bit of C code 2019-12-02 17:28:01 -05:00
Virgil Dupras
15e44ec524 doc: add TI83+/TI84+ doc page 2019-12-02 13:51:36 -05:00
11 changed files with 432 additions and 324 deletions

View File

@ -13,3 +13,11 @@
* [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)

38
doc/ti8x.md Normal file
View File

@ -0,0 +1,38 @@
# 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,6 +8,8 @@ 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,3 +8,4 @@
/cfsin/ed
/cfsin/basic
/cfsin/user.h
*.o

View File

@ -31,15 +31,18 @@ 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 libz80/libz80.o zasm/kernel-bin.h zasm/zasm-bin.h $(CFSPACK)
$(ZASMBIN): zasm/zasm.c emul.o libz80/libz80.o zasm/kernel-bin.h zasm/zasm-bin.h $(CFSPACK)
runbin/runbin: runbin/runbin.c libz80/libz80.o
$(TARGETS):
$(CC) $< libz80/libz80.o -o $@
$(CC) $< emul.o 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
@ -57,4 +60,4 @@ updatebootstrap: $(ZASMBIN) $(INCCFS)
.PHONY: clean
clean:
rm -f $(TARGETS) $(SHELLAPPS) zasm/*-bin.h shell/*-bin.h
rm -f $(TARGETS) $(SHELLAPPS) emul.o zasm/*-bin.h shell/*-bin.h

View File

@ -1,7 +1,7 @@
#include <stdint.h>
#include <stdio.h>
#include <termios.h>
#include "../libz80/z80.h"
#include "../emul.h"
#include "shell-bin.h"
/* Collapse OS shell with filesystem
@ -39,8 +39,6 @@
// 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;
@ -48,16 +46,17 @@ static uint32_t fsdev_ptr = 0;
static int fsdev_addr_lvl = 0;
static int running;
static uint8_t io_read(int unused, uint16_t addr)
static uint8_t iord_stdio()
{
addr &= 0xff;
if (addr == STDIO_PORT) {
int c = getchar();
if (c == EOF) {
running = 0;
}
return (uint8_t)c;
} else if (addr == FS_DATA_PORT) {
}
static uint8_t iord_fsdata()
{
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return 0;
@ -74,7 +73,10 @@ static uint8_t io_read(int unused, uint16_t addr)
}
return 0;
}
} else if (addr == FS_ADDR_PORT) {
}
static uint8_t iord_fsaddr()
{
if (fsdev_addr_lvl != 0) {
return 3;
} else if (fsdev_ptr > fsdev_size) {
@ -85,22 +87,19 @@ static uint8_t io_read(int unused, uint16_t addr)
} else {
return 0;
}
} 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)
static void iowr_stdio(uint8_t val)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
if (val == 0x04) { // CTRL+D
running = 0;
} else {
putchar(val);
}
} else if (addr == FS_DATA_PORT) {
}
static void iowr_fsdata(uint8_t val)
{
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return;
@ -120,7 +119,10 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
} else {
fprintf(stderr, "Out of bounds FSDEV write at %d\n", fsdev_ptr);
}
} else if (addr == FS_ADDR_PORT) {
}
static void iowr_fsaddr(uint8_t val)
{
if (fsdev_addr_lvl == 0) {
fsdev_ptr = val << 16;
fsdev_addr_lvl = 1;
@ -131,22 +133,6 @@ static void io_write(int unused, uint16_t addr, 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()
@ -179,25 +165,27 @@ 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++) {
mem[i] = KERNEL[i];
m->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 && !cpu.halted) {
Z80Execute(&cpu);
}
while (running && emul_step());
printf("Done!\n");
termInfo.c_lflag |= ECHO;
termInfo.c_lflag |= ICANON;
tcsetattr(0, TCSAFLUSH, &termInfo);
emul_printdebug();
return 0;
}

87
tools/emul/emul.c Normal file
View File

@ -0,0 +1,87 @@
/* 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);
}

25
tools/emul/emul.h Normal file
View File

@ -0,0 +1,25 @@
#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,44 +1,19 @@
#include <stdint.h>
#include <stdio.h>
#include "../libz80/z80.h"
#include "../emul.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) {
mem[i] = c & 0xff;
m->mem[i] = c & 0xff;
i++;
c = getchar();
}
@ -46,15 +21,7 @@ int main()
fprintf(stderr, "No input, aborting\n");
return 1;
}
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;
emul_loop();
return m->cpu.R1.br.A;
}

View File

@ -1,7 +1,7 @@
#include <stdint.h>
#include <stdio.h>
#include <termios.h>
#include "../libz80/z80.h"
#include "../emul.h"
#include "kernel-bin.h"
/* Collapse OS shell with filesystem
@ -39,8 +39,6 @@
// 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;
@ -48,16 +46,17 @@ static uint32_t fsdev_ptr = 0;
static int fsdev_addr_lvl = 0;
static int running;
static uint8_t io_read(int unused, uint16_t addr)
static uint8_t iord_stdio()
{
addr &= 0xff;
if (addr == STDIO_PORT) {
int c = getchar();
if (c == EOF) {
running = 0;
}
return (uint8_t)c;
} else if (addr == FS_DATA_PORT) {
}
static uint8_t iord_fsdata()
{
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return 0;
@ -74,7 +73,10 @@ static uint8_t io_read(int unused, uint16_t addr)
}
return 0;
}
} else if (addr == FS_ADDR_PORT) {
}
static uint8_t iord_fsaddr()
{
if (fsdev_addr_lvl != 0) {
return 3;
} else if (fsdev_ptr > fsdev_size) {
@ -85,22 +87,19 @@ static uint8_t io_read(int unused, uint16_t addr)
} else {
return 0;
}
} 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)
static void iowr_stdio(uint8_t val)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
if (val == 0x04) { // CTRL+D
running = 0;
} else {
putchar(val);
}
} else if (addr == FS_DATA_PORT) {
}
static void iowr_fsdata(uint8_t val)
{
if (fsdev_addr_lvl != 0) {
fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return;
@ -120,7 +119,10 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
} else {
fprintf(stderr, "Out of bounds FSDEV write at %d\n", fsdev_ptr);
}
} else if (addr == FS_ADDR_PORT) {
}
static void iowr_fsaddr(uint8_t val)
{
if (fsdev_addr_lvl == 0) {
fsdev_ptr = val << 16;
fsdev_addr_lvl = 1;
@ -131,22 +133,6 @@ static void io_write(int unused, uint16_t addr, 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()
@ -179,25 +165,27 @@ 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++) {
mem[i] = KERNEL[i];
m->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 && !cpu.halted) {
Z80Execute(&cpu);
}
while (running && emul_step());
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 "../libz80/z80.h"
#include "../emul.h"
#include "kernel-bin.h"
#include "zasm-bin.h"
@ -48,8 +48,6 @@
// 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;
@ -61,16 +59,17 @@ static uint32_t fsdev_size = 0;
static uint32_t fsdev_ptr = 0;
static uint8_t fsdev_seek_tell_cnt = 0;
static uint8_t io_read(int unused, uint16_t addr)
static uint8_t iord_stdio()
{
addr &= 0xff;
if (addr == STDIO_PORT) {
if (inpt_ptr < inpt_size) {
return inpt[inpt_ptr++];
} else {
return 0;
}
} else if (addr == STDIN_SEEK_PORT) {
}
static uint8_t iord_stdin_seek()
{
if (middle_of_seek_tell) {
middle_of_seek_tell = 0;
return inpt_ptr & 0xff;
@ -81,13 +80,19 @@ static uint8_t io_read(int unused, uint16_t addr)
middle_of_seek_tell = 1;
return inpt_ptr >> 8;
}
} else if (addr == FS_DATA_PORT) {
}
static uint8_t iord_fsdata()
{
if (fsdev_ptr < fsdev_size) {
return fsdev[fsdev_ptr++];
} else {
return 0;
}
} else if (addr == FS_SEEK_PORT) {
}
static uint8_t iord_fsseek()
{
if (fsdev_seek_tell_cnt != 0) {
return fsdev_seek_tell_cnt;
} else if (fsdev_ptr >= fsdev_size) {
@ -95,21 +100,18 @@ static uint8_t io_read(int unused, uint16_t addr)
} else {
return 0;
}
} 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)
static void iowr_stdio(uint8_t val)
{
addr &= 0xff;
if (addr == STDIO_PORT) {
// When mem-dumping, we don't output regular stuff.
#ifndef MEMDUMP
putchar(val);
#endif
} else if (addr == STDIN_SEEK_PORT) {
}
static void iowr_stdin_seek(uint8_t val)
{
if (middle_of_seek_tell) {
inpt_ptr |= val;
middle_of_seek_tell = 0;
@ -120,11 +122,17 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
inpt_ptr = (val << 8) & 0xff00;
middle_of_seek_tell = 1;
}
} else if (addr == FS_DATA_PORT) {
}
static void iowr_fsdata(uint8_t val)
{
if (fsdev_ptr < fsdev_size) {
fsdev[fsdev_ptr++] = val;
}
} else if (addr == FS_SEEK_PORT) {
}
static void iowr_fsseek(uint8_t val)
{
if (fsdev_seek_tell_cnt == 0) {
fsdev_ptr = val << 16;
fsdev_seek_tell_cnt = 1;
@ -138,23 +146,13 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
fprintf(stderr, "FS seek %d\n", fsdev_ptr);
#endif
}
} else if (addr == STDERR_PORT) {
}
static void iowr_stderr(uint8_t val)
{
#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[])
@ -163,12 +161,22 @@ 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++) {
mem[i] = KERNEL[i];
m->mem[i] = KERNEL[i];
}
for (int i=0; i<sizeof(USERSPACE); i++) {
mem[i+USER_CODE] = USERSPACE[i];
m->mem[i+USER_CODE] = USERSPACE[i];
}
char *init_org = "00";
if (argc >= 2) {
@ -178,8 +186,8 @@ int main(int argc, char *argv[])
}
}
// glue.asm knows that it needs to fetch these arguments at this address.
mem[0xff00] = init_org[0];
mem[0xff01] = init_org[1];
m->mem[0xff00] = init_org[0];
m->mem[0xff01] = init_org[1];
fsdev_size = 0;
if (argc == 3) {
FILE *fp = fopen(argv[2], "r");
@ -209,25 +217,18 @@ 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;
while (!cpu.halted) {
Z80Execute(&cpu);
}
emul_loop();
#ifdef MEMDUMP
for (int i=0; i<0x10000; i++) {
putchar(mem[i]);
}
#endif
fflush(stdout);
int res = cpu.R1.br.A;
int res = m->cpu.R1.br.A;
if (res != 0) {
int lineno = cpu.R1.wr.HL;
int inclineno = cpu.R1.wr.DE;
int lineno = m->cpu.R1.wr.HL;
int inclineno = m->cpu.R1.wr.DE;
if (inclineno) {
fprintf(
stderr,