From ca84b5dac86052fe759a7d3d031f76e2ee088d26 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Thu, 7 Nov 2019 11:52:29 -0500 Subject: [PATCH] recipes/ti84: first step This was mostly lifted from my "tihello" example, but it required significant adjustments. This commit also introduces a font management system. A lot of fonts are available online, but sources aren't always clear so to avoid copyright landmines, I re-created my first 5x7 font from scratch. As it is now, this resulting ROM gets "Collapse OS>" to be displayed on the LCD screen. Much work still left to do. ref #41 --- fonts/5x7.txt | 658 +++++++++++++++++++++++++++++++++++++++++ fonts/README.md | 15 + kernel/fnt/5x7.bin | Bin 0 -> 665 bytes kernel/fnt/mgm.asm | 32 ++ kernel/ti/kbd.asm | 27 ++ kernel/ti/lcd.asm | 139 +++++++++ recipes/.gitignore | 2 + recipes/ti84/Makefile | 10 + recipes/ti84/README.md | 32 ++ recipes/ti84/glue.asm | 96 ++++++ tools/font_compile.pl | 30 ++ tools/zasm.sh | 1 + 12 files changed, 1042 insertions(+) create mode 100644 fonts/5x7.txt create mode 100644 fonts/README.md create mode 100644 kernel/fnt/5x7.bin create mode 100644 kernel/fnt/mgm.asm create mode 100644 kernel/ti/kbd.asm create mode 100644 kernel/ti/lcd.asm create mode 100644 recipes/ti84/Makefile create mode 100644 recipes/ti84/README.md create mode 100644 recipes/ti84/glue.asm create mode 100755 tools/font_compile.pl diff --git a/fonts/5x7.txt b/fonts/5x7.txt new file mode 100644 index 0000000..ab674d3 --- /dev/null +++ b/fonts/5x7.txt @@ -0,0 +1,658 @@ + . + . + . + . + + . + . + . . + . . + + + + + + + . . +..... + . . +..... + . . + + . + .... +. + ... + . +.... + . + +. . + . + . + . +. . + + .. +. . + .. + .. . +. . +... . + + . + . + + + + + + . + . +. +. +. + . + . + . + . + . + . + . + . + . + +. . . + ... +..... + ... +. . . + + + . + . +..... + . + . + + . + . + + + + + + + + +..... + + + + + + + + + .. + .. + + + . + . + . +. + + ... +. . +. .. +. . . +.. . +. . + ... + .. + . . + . + . + . + . + .... + ... +. . + . + . + . + . +..... + ... +. . + . + .. + . +. . + ... + .. + . . +. . +..... + . + . + . +..... +. +.... + . + . +. . + ... + ... +. +. + ... +. . +. . + ... +..... + . + . + . + . + . +. + ... +. . +. . + ... +. . +. . + ... + ... +. . +. . + ... + . + . + ... + + . + . + + . + . + + + . + . + + . + . + + . + .. + .. +. + .. + .. + . + + +..... + +..... + + +. + .. + .. + . + .. + .. +. + ... +. . + . + . + . + + . + ... +. . +. .. +. .. +. +. . + ... + ... +. . +. . +..... +. . +. . +. . +.... +. . +. . +.... +. . +. . +.... + ... +. . +. +. +. +. . + ... +.... +. . +. . +. . +. . +. . +.... +..... +. +. +.... +. +. +..... +..... +. +. +.... +. +. +. + ... +. . +. +. .. +. . +. . + ... +. . +. . +. . +..... +. . +. . +. . + ... + . + . + . + . + . + ... + .. + . + . + . + . +. . + ... +. . +. . +.. +. +.. +. . +. . +. +. +. +. +. +. +..... +. . +.. .. +. . . +. . +. . +. . +. . +. . +.. . +.. . +. . . +. .. +. .. +. . + ... +. . +. . +. . +. . +. . + ... +.... +. . +. . +.... +. +. +. + ... +. . +. . +. . +. . . +. .. + .... +.... +. . +. . +.... +. . +. . +. . + ... +. . +. + ... + . +. . + ... +..... + . + . + . + . + . + . +. . +. . +. . +. . +. . +. . + ... +. . +. . +. . +. . +. . + . . + . +. . +. . +. . +. . +. . . +. . . + . . +. . +. . + . . + . + . . +. . +. . +. . +. . + . . + . + . + . + . +..... +. . + . + . + . +. . +..... +... +. +. +. +. +. +... + +. + . + . + . + . + + ... + . + . + . + . + . + ... + . + . . +. . + + + + + + + + + + +..... + . + . + + + + + + ... +. . +. . +..... +. . +. . +. . +.... +. . +. . +.... +. . +. . +.... + ... +. . +. +. +. +. . + ... +.... +. . +. . +. . +. . +. . +.... +..... +. +. +.... +. +. +..... +..... +. +. +.... +. +. +. + ... +. . +. +. .. +. . +. . + ... +. . +. . +. . +..... +. . +. . +. . + ... + . + . + . + . + . + ... + .. + . + . + . + . +. . + ... +. . +. . +.. +. +.. +. . +. . +. +. +. +. +. +. +..... +. . +.. .. +. . . +. . +. . +. . +. . +. . +.. . +.. . +. . . +. .. +. .. +. . + ... +. . +. . +. . +. . +. . + ... +.... +. . +. . +.... +. +. +. + ... +. . +. . +. . +. . . +. .. + .... +.... +. . +. . +.... +. . +. . +. . + ... +. . +. + ... + . +. . + ... +..... + . + . + . + . + . + . +. . +. . +. . +. . +. . +. . + ... +. . +. . +. . +. . +. . + . . + . +. . +. . +. . +. . +. . +.. .. +. . . +. . +. . + . . + . + . . +. . +. . +. . +. . + . . + . + . + . + . +..... +. . + . + . + . +. . +..... + .. + . + . +. + . + . + .. + . + . + . + . + . + . + . + .. + . + . + . + . + . + .. + + + . . +. . + + + diff --git a/fonts/README.md b/fonts/README.md new file mode 100644 index 0000000..a1db672 --- /dev/null +++ b/fonts/README.md @@ -0,0 +1,15 @@ +# fonts + +This folder contains bitmap fonts that are then converted to ASM data tables. + +The format for them is straightforward: dots and spaces. Each line is a line in +the letter (for example, in a 6x8 font, each character is 8 lines of 6 +characters each, excluding newline). + +They cover the 0x21 to 0x7e range and are placed sequentially in the file. + +Dots and spaces allow easy visualisation of the result and is thus rather handy. + +Padding is excluded from fonts. For example, 5x7.txt is actually a 6x8 font, but +because characters are always padded, it's useless to keep systematic blank +lines or rows around. diff --git a/kernel/fnt/5x7.bin b/kernel/fnt/5x7.bin new file mode 100644 index 0000000000000000000000000000000000000000..f6d7c44e9658ac03d5d62f06475fde4595049f9c GIT binary patch literal 665 zcmd5)(Qd;q2m}wMZsZY2VyV%(@c)0g!)f380V7fH0cYS8qG19fk^B9EbKoIaO!*Y@ zBGsv_?R))2(M70cRz(Uk7oo19osbOVfnOHyLu-U^HB#UD;X2I)wy1+#18(9Bu7POS z$v|qR?izW29Y>&yFl$bcgQTm(Q?oWPQQ)CGA~3EdLBGugFWmjSUUdxb3Y4)hJI&&9 z;Ms;INF+g17|}0miILX*VUK+U85aK2lkdmTw{0*`pB<1)e&3cv`-&Wd#46)58KhVJ zJW%hm`z-jRgT;_Mo}g8%>k literal 0 HcmV?d00001 diff --git a/kernel/fnt/mgm.asm b/kernel/fnt/mgm.asm new file mode 100644 index 0000000..37f13c1 --- /dev/null +++ b/kernel/fnt/mgm.asm @@ -0,0 +1,32 @@ +; Font management +; +; There can only ever be one active font. +; +; *** Defines *** +; FNT_DATA: Pointer to the beginning of the binary font data to work with. +; FNT_WIDTH: Width of the font. +; FNT_HEIGHT: Height of the font. +; +; *** Code *** + +; If A is in the range 0x20-0x7e, make HL point to the beginning of the +; corresponding glyph and set Z to indicate success. +; If A isn't in the range, do nothing and unset Z. +fntGet: + cp 0x20 + ret c ; A < 0x20. Z was unset by cp + cp 0x7f + jr nc, unsetZ ; A >= 0x7f. Z might be set + + push af ; --> lvl 1 + push bc ; --> lvl 2 + sub 0x20 + ld hl, FNT_DATA + ld b, FNT_HEIGHT +.loop: + call addHL + djnz .loop + pop bc ; <-- lvl 2 + pop af ; <-- lvl 1 + cp a ; set Z + ret diff --git a/kernel/ti/kbd.asm b/kernel/ti/kbd.asm new file mode 100644 index 0000000..e41e389 --- /dev/null +++ b/kernel/ti/kbd.asm @@ -0,0 +1,27 @@ +; kbd +; +; Control TI-84+'s keyboard. +; +; *** Constants *** +.equ KBD_PORT 0x01 + +; *** Code *** +; Sending 0xff to the port resets the keyboard, and then we have to send groups +; we want to "listen" to, with a 0 in the group bit. Thus, to know if *any* key +; is pressed, we send 0xff to reset the keypad, then 0x00 to select all groups, +; if the result isn't 0xff, at least one key is pressed. +waitForKey: + push af + + ld a, 0xff + out (KBD_PORT), a + ld a, 0x00 + out (KBD_PORT), a + +.loop: + in a, (KBD_PORT) + inc a ; if a was 0xff, will become 0 (z test) + jr z, .loop ; zero? nothing pressed + + pop af + ret diff --git a/kernel/ti/lcd.asm b/kernel/ti/lcd.asm new file mode 100644 index 0000000..305bb6e --- /dev/null +++ b/kernel/ti/lcd.asm @@ -0,0 +1,139 @@ +; lcd +; +; Implement PutC on TI-84+ (for now)'s LCD screen. +; +; *** Requirements *** +; fnt/mgm +; +; *** Constants *** +.equ LCD_PORT_CMD 0x10 +.equ LCD_PORT_DATA 0x11 + +.equ LCD_CMD_6BIT 0x00 +.equ LCD_CMD_DISABLE 0x02 +.equ LCD_CMD_ENABLE 0x03 +.equ LCD_CMD_XINC 0x05 +.equ LCD_CMD_YINC 0x07 +.equ LCD_CMD_COL 0x20 +.equ LCD_CMD_ROW 0x80 +.equ LCD_CMD_CONTRAST 0xc0 + +; *** Code *** +lcdInit: + ; Enable the LCD + ld a, LCD_CMD_ENABLE + call lcdWait + out (LCD_PORT_CMD), a + + ; Hack to get LCD to work. According to WikiTI, we're to sure why TIOS + ; sends these, but it sends it, and it is required to make the LCD + ; work. So... + ld a, 0x17 + call lcdWait + out (LCD_PORT_CMD), a + ld a, 0x0b + call lcdWait + out (LCD_PORT_CMD), a + + ; Set some usable contrast + ld a, LCD_CMD_CONTRAST+0x34 + call lcdWait + out (LCD_PORT_CMD), a + + ; Enable 6-bit mode. + ld a, LCD_CMD_6BIT + call lcdWait + out (LCD_PORT_CMD), a + + ; Enable X-increment mode + ld a, LCD_CMD_XINC + call lcdWait + out (LCD_PORT_CMD), a + + ret + +; Wait until the lcd is ready to receive a command +lcdWait: + push af +.loop: + in a, (LCD_PORT_CMD) + ; When 7th bit is cleared, we can send a new command + rla + jr c, .loop + pop af + ret + +; Turn LCD off +lcdOff: + ld a, LCD_CMD_DISABLE + call lcdWait + out (LCD_PORT_CMD), a + ret + +; Set LCD's current column to A +lcdSetCol: + ; The col index specified in A is compounded with LCD_CMD_COL + add a, LCD_CMD_COL + call lcdWait + out (LCD_PORT_CMD), a + ret + +; Set LCD's current row to A +lcdSetRow: + ; The col index specified in A is compounded with LCD_CMD_COL + add a, LCD_CMD_ROW + call lcdWait + out (LCD_PORT_CMD), a + ret + +; Send the 5x7 glyph that HL points to to the LCD, at its current position. +; After having called this, the LCD's position will have advanced by one +; position +lcdSendGlyph: + push af + push bc + push hl + + ; For the purpose of this program, we only write on the first line. + ; We can assume that we always start at row 0. + xor a + call lcdSetRow + + ld b, 7 +.loop: + ld a, (hl) + inc hl + call lcdWait + out (LCD_PORT_DATA), a + djnz .loop + + ; Now that we've sent our 7 rows of pixels, let's go in "Y-increment" + ; mode to let the LCD increase by one column after we've sent our 8th + ; line, which is blank (padding). + ld a, LCD_CMD_YINC + call lcdWait + out (LCD_PORT_CMD), a + + ; send blank line + xor a + call lcdWait + out (LCD_PORT_DATA), a + + ; go back in X-increment mode + ld a, LCD_CMD_XINC + call lcdWait + out (LCD_PORT_CMD), a + + pop hl + pop bc + pop af + ret + +lcdPutC: + push hl + call fntGet + jr nz, .end + call lcdSendGlyph +.end: + pop hl + ret diff --git a/recipes/.gitignore b/recipes/.gitignore index 91337ae..e5730d0 100644 --- a/recipes/.gitignore +++ b/recipes/.gitignore @@ -2,3 +2,5 @@ *.cfs *.hex *.obj +*.rom +*.sms diff --git a/recipes/ti84/Makefile b/recipes/ti84/Makefile new file mode 100644 index 0000000..ca7ed63 --- /dev/null +++ b/recipes/ti84/Makefile @@ -0,0 +1,10 @@ +TARGET = os.rom +ZASM = ../../tools/zasm.sh +KERNEL = ../../kernel + +.PHONY: all +all: $(TARGET) +$(TARGET): glue.asm + $(ZASM) $(KERNEL) < $< > $@ + truncate -s 1M $@ + diff --git a/recipes/ti84/README.md b/recipes/ti84/README.md new file mode 100644 index 0000000..3023662 --- /dev/null +++ b/recipes/ti84/README.md @@ -0,0 +1,32 @@ +# TI-84+ + +**This is a work-in-progress, this is far from complete.** + +## Recipe + +This recipe gets the Collapse OS shell to run on the TI-84+, using its LCD +screen as output and its builtin keyboard as input. + +## Build the ROM + +Running `make` will result in `os.rom` being created. + +## Emulate through z80e + +[KnightOS][knightos] has a handy emulator, [z80e][z80e] for TI calculators and +it also emulates the screen. It is recommended to use this tool. + +Once z80e is installed (build it with SDL support) and `os.rom` is created, +you can run the emulator with: + + z80e-sdl -d TI84p --no-rom-check os.rom + +You will start with a blank screen, it's normal, you haven't pressed the "ON" +key yet. This key is mapped to F12 in the emulator. Once you press it, the +Collapse OS prompt will appear. + +**WIP: the keyboard does nothing else than halting the CPU for now.** + +## Upload to the calculator + +TODO diff --git a/recipes/ti84/glue.asm b/recipes/ti84/glue.asm new file mode 100644 index 0000000..22ff13f --- /dev/null +++ b/recipes/ti84/glue.asm @@ -0,0 +1,96 @@ +.equ RAMSTART 0x8000 +.equ RAMEND 0xbfff +.equ PORT_INT_MASK 0x03 +.equ INT_MASK_ON 0x00 +.equ PORT_INT_TRIG 0x04 +.equ INT_TRIG_ON 0x00 +.equ PORT_BANKB 0x07 + + jp boot + +.fill 0x18-$ + jp boot ; reboot + +.fill 0x38-$ + jp handleInterrupt + +.fill 0x53-$ + jp boot +; 0x0056 +.db 0xFF, 0xA5, 0xFF + +.fill 0x64-$ + +.inc "err.h" +.inc "core.asm" +.equ FNT_WIDTH 5 +.equ FNT_HEIGHT 7 +.inc "fnt/mgm.asm" +.inc "ti/lcd.asm" +.inc "ti/kbd.asm" +.equ STDIO_RAMSTART RAMSTART +.equ STDIO_GETC GetC +.equ STDIO_PUTC lcdPutC +.inc "stdio.asm" + +.inc "parse.asm" +.equ SHELL_RAMSTART STDIO_RAMEND +.equ SHELL_EXTRA_CMD_COUNT 0 +.inc "shell.asm" + +boot: + di + ld hl, RAMEND + ld sp, hl + im 1 + + ; enable ON key interrupt + in a, (PORT_INT_MASK) + set INT_MASK_ON, a + out (PORT_INT_MASK), a + ld a, 0x80 + out (PORT_BANKB), a + + ei + + call lcdOff + + ; sleep until we press ON + halt + +main: + call lcdInit + xor a + call lcdSetCol + call shellInit + jp shellLoop + +GetC: + call waitForKey + jr boot + +handleInterrupt: + di + push af + + ; did we push the ON button? + in a, (PORT_INT_TRIG) + bit INT_TRIG_ON, a + jp z, .done ; no? we're done + + ; yes? acknowledge and boot + in a, (PORT_INT_MASK) + res INT_MASK_ON, a ; acknowledge interrupt + out (PORT_INT_MASK), a + + pop af + ei + jp main + +.done: + pop af + ei + reti + +FNT_DATA: +.bin "fnt/5x7.bin" diff --git a/tools/font_compile.pl b/tools/font_compile.pl new file mode 100755 index 0000000..b923108 --- /dev/null +++ b/tools/font_compile.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl +use strict; + +# This script converts "space-dot" fonts to binary "glyph rows". One byte for +# each row. In a 5x7 font, each glyph thus use 7 bytes. + +my $fn = @ARGV[0]; +unless ($fn =~ /.*(\d)x(\d)\.txt/) { die "$fn isn't a font filename" }; +my ($width, $height) = ($1, $2); + +print STDERR "Reading a $width x $height font.\n"; + +my $handle; +unless (open($handle, '<', $fn)) { die "Can't open $fn"; } + +# We start the binary data with our first char, space, which is not in our input +# but needs to be in our output. +print pack('C*', (0) x $height); + +while (<$handle>) { + unless (/( |\.){${width}}\n/) { die "Invalid line format '$_'"; } + my @line = split //, $_; + my $num = 0; + for (my $i=$width-1; $i>=0; $i--) { + if (@line[$width-$i-1] eq '.') { + $num += (1 << $i); + } + } + printf pack('C', $num); +} diff --git a/tools/zasm.sh b/tools/zasm.sh index b2ef6fd..dd16e2a 100755 --- a/tools/zasm.sh +++ b/tools/zasm.sh @@ -13,6 +13,7 @@ INCCFS=$(mktemp) for p in "$@"; do "${CFSPACK}" "${p}" "*.h" >> "${INCCFS}" "${CFSPACK}" "${p}" "*.asm" >> "${INCCFS}" + "${CFSPACK}" "${p}" "*.bin" >> "${INCCFS}" done "${ZASMBIN}" "${INCCFS}"