I implement the screen using XCB which is much more friendly than z80e's SDL+CMake for development machines that want to install minimal dependencies (for example, a port-less OpenBSD rig).pull/85/head
@@ -76,6 +76,17 @@ bool emul_step() | |||
} | |||
} | |||
bool emul_steps(unsigned int steps) | |||
{ | |||
while (steps) { | |||
if (!emul_step()) { | |||
return false; | |||
} | |||
steps--; | |||
} | |||
return true; | |||
} | |||
void emul_loop() | |||
{ | |||
while (emul_step()); | |||
@@ -21,5 +21,6 @@ typedef struct { | |||
Machine* emul_init(); | |||
bool emul_step(); | |||
bool emul_steps(unsigned int steps); | |||
void emul_loop(); | |||
void emul_printdebug(); |
@@ -49,7 +49,7 @@ int main(int argc, char *argv[]) | |||
} | |||
FILE *fp = fopen(argv[1], "r"); | |||
if (fp == NULL) { | |||
fprintf(stderr, "Can't open %s\n", optarg); | |||
fprintf(stderr, "Can't open %s\n", argv[1]); | |||
return 1; | |||
} | |||
Machine *m = emul_init(); | |||
@@ -0,0 +1 @@ | |||
/ti84 |
@@ -0,0 +1,6 @@ | |||
OBJS = ti84.o t6a04.o kbd.o ../../emul.o ../../libz80/libz80.o | |||
CFLAGS += `pkg-config --cflags xcb` | |||
ti84: $(OBJS) | |||
$(CC) `pkg-config --libs xcb` $(OBJS) -o $@ | |||
@@ -0,0 +1,26 @@ | |||
# TI-84+ emulator | |||
This emulates a TI-84+ with its screen and keyboard. This is suitable for | |||
running the `ti84` recipe. | |||
## Build | |||
You need `xcb` and `pkg-config` to build this. If you have them, run `make`. | |||
You'll get a `ti84` executable. | |||
## Usage | |||
Launch the emulator with `./ti84 /path/to/rom` (you can use the binary from the | |||
`ti84` recipe. Use the small one, not the one having been filled to 1MB). | |||
This will show a window with the LCD screen's content on it. Most applications, | |||
upon boot, halt after initialization and stay halted until the ON key is | |||
pressed. The ON key is mapped to the tilde (~) key. | |||
Press ESC to quit. | |||
As for the rest of the mappings, they map at the key level. For example, the 'Y' | |||
key maps to '1' (which yields 'y' when in alpha mode). Therefore, '1' and 'Y' | |||
map to the same calculator key. Backspace maps to DEL. | |||
Left Shift maps to 2nd. Left Ctrl maps to Alpha. |
@@ -0,0 +1,101 @@ | |||
#include <string.h> | |||
#include <ctype.h> | |||
#include "kbd.h" | |||
void kbd_init(KBD *kbd) | |||
{ | |||
memset(kbd->pressed, 0xff, 8); | |||
kbd->selected = 0xff; | |||
} | |||
uint8_t kbd_rd(KBD *kbd) | |||
{ | |||
uint8_t res = 0xff; | |||
for (int i=0; i<8; i++) { | |||
if ((kbd->selected & (1<<i)) == 0) { | |||
res &= kbd->pressed[i]; | |||
} | |||
} | |||
return res; | |||
} | |||
void kbd_wr(KBD *kbd, uint8_t val) | |||
{ | |||
kbd->selected = val; | |||
} | |||
// The key is separated in two nibble. High nibble is group, low nibble is key. | |||
void kbd_setkey(KBD *kbd, uint8_t key, bool pressed) | |||
{ | |||
uint8_t group = kbd->pressed[key>>4]; | |||
if (pressed) { | |||
group &= ~(1<<(key&0x7)); | |||
} else { | |||
group |= 1<<(key&0x7); | |||
} | |||
kbd->pressed[key>>4] = group; | |||
} | |||
// Attempts to returns a key code corresponding to the specified char. 0 if | |||
// nothing matches. | |||
uint8_t kbd_trans(char c) | |||
{ | |||
c = toupper(c); | |||
switch (c) { | |||
case 0x0a: | |||
case 0x0d: return 0x10; // ENTER | |||
case '+': return 0x11; | |||
case '-': return 0x12; | |||
case '*': return 0x13; | |||
case '/': return 0x14; | |||
case '^': return 0x15; | |||
case '3': return 0x21; | |||
case '6': return 0x22; | |||
case '9': return 0x23; | |||
case ')': return 0x24; | |||
case '.': return 0x30; | |||
case '2': return 0x31; | |||
case '5': return 0x32; | |||
case '8': return 0x33; | |||
case '(': return 0x34; | |||
case '0': return 0x40; | |||
case '1': return 0x41; | |||
case '4': return 0x42; | |||
case '7': return 0x43; | |||
case ',': return 0x44; | |||
case 0x7f: return 0x67; // DEL | |||
case '"': return 0x11; | |||
case 'W': return 0x12; | |||
case 'R': return 0x13; | |||
case 'M': return 0x14; | |||
case 'H': return 0x15; | |||
case '?': return 0x20; | |||
case 'V': return 0x22; | |||
case 'Q': return 0x23; | |||
case 'L': return 0x24; | |||
case 'G': return 0x25; | |||
case ':': return 0x30; | |||
case 'Z': return 0x31; | |||
case 'U': return 0x32; | |||
case 'P': return 0x33; | |||
case 'K': return 0x34; | |||
case 'F': return 0x35; | |||
case 'C': return 0x36; | |||
case ' ': return 0x40; | |||
case 'Y': return 0x41; | |||
case 'T': return 0x42; | |||
case 'O': return 0x43; | |||
case 'J': return 0x44; | |||
case 'E': return 0x45; | |||
case 'B': return 0x46; | |||
case 'X': return 0x51; | |||
case 'S': return 0x52; | |||
case 'N': return 0x53; | |||
case 'I': return 0x54; | |||
case 'D': return 0x55; | |||
case 'A': return 0x56; | |||
default: return 0; | |||
} | |||
} | |||
@@ -0,0 +1,25 @@ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
// These are keycodes for special keys | |||
#define KBD_ALPHA 0x57 | |||
#define KBD_2ND 0x65 | |||
// NOTE: We don't manage the ON key here. | |||
typedef struct { | |||
// Bitmask of pressed keys. Like on real hardware, 0xff means nothing | |||
// pressed. The group 7 has no key, but for code simplicity, we have a neat | |||
// array of 8 bytes. | |||
uint8_t pressed[8]; | |||
// Selected groups. Active low. | |||
uint8_t selected; | |||
} KBD; | |||
void kbd_init(KBD *kbd); | |||
uint8_t kbd_rd(KBD *kbd); | |||
void kbd_wr(KBD *kbd, uint8_t val); | |||
// The key is separated in two nibble. High nibble is group, low nibble is key. | |||
void kbd_setkey(KBD *kbd, uint8_t key, bool pressed); | |||
// Attempts to returns a key code corresponding to the specified char. 0 if | |||
// nothing matches. | |||
uint8_t kbd_trans(char c); |
@@ -0,0 +1,141 @@ | |||
#include <string.h> | |||
#include "t6a04.h" | |||
void t6a04_init(T6A04 *lcd) | |||
{ | |||
memset(lcd->ram, 0, T6A04_RAMSIZE); | |||
lcd->enabled = false; | |||
lcd->incmode = T6A04_XINC; | |||
lcd->offset = 0; | |||
lcd->currow = 0; | |||
lcd->curcol = 0; | |||
lcd->just_moved = true; | |||
} | |||
uint8_t t6a04_cmd_rd(T6A04 *lcd) | |||
{ | |||
return 0; // we are always ready for a new cmd | |||
} | |||
/* | |||
* 0x00/0x01: 6/8 bit mode | |||
* 0x02/0x03: enable/disable | |||
* 0x04-0x07: incmodes | |||
* 0x20-0x34: set col | |||
* 0x40-0x7f: set Z offset | |||
* 0x80-0xbf: set row | |||
* 0xc0-0xff: set contrast | |||
*/ | |||
void t6a04_cmd_wr(T6A04 *lcd, uint8_t val) | |||
{ | |||
if ((val & 0xc0) == 0xc0) { | |||
// contrast, ignoring | |||
} else if (val & 0x80) { | |||
lcd->currow = val & 0x3f; | |||
lcd->just_moved = true; | |||
} else if (val & 0x40) { | |||
lcd->offset = val & 0x3f; | |||
} else if (val & 0x20) { | |||
lcd->curcol = val & 0x1f; | |||
lcd->just_moved = true; | |||
} else if (val & 0x18) { | |||
// stuff we don't emulate | |||
} else if (val & 0x04) { | |||
lcd->incmode = val & 0x03; | |||
} else if (val & 0x02) { | |||
lcd->enabled = val & 0x01; | |||
} else { | |||
lcd->has8bitmode = val; | |||
} | |||
} | |||
// Advance current position according to current incmode | |||
static void _advance(T6A04 *lcd) | |||
{ | |||
uint8_t maxY = lcd->has8bitmode ? 14 : 19; | |||
switch (lcd->incmode) { | |||
case T6A04_XDEC: | |||
lcd->currow = (lcd->currow-1) & 0x3f; | |||
break; | |||
case T6A04_XINC: | |||
lcd->currow = (lcd->currow+1) & 0x3f; | |||
break; | |||
case T6A04_YDEC: | |||
if (lcd->curcol == 0) { | |||
lcd->curcol = maxY; | |||
} else { | |||
lcd->curcol--; | |||
} | |||
break; | |||
case T6A04_YINC: | |||
if (lcd->curcol < maxY) { | |||
lcd->curcol++; | |||
} else { | |||
lcd->curcol = 0; | |||
} | |||
break; | |||
} | |||
} | |||
uint8_t t6a04_data_rd(T6A04 *lcd) | |||
{ | |||
uint8_t res; | |||
if (lcd->just_moved) { | |||
// After a move command, the first read op is a noop. | |||
lcd->just_moved = false; | |||
return 0; | |||
} | |||
if (lcd->has8bitmode) { | |||
int pos = lcd->currow * T6A04_ROWSIZE + lcd->curcol; | |||
res = lcd->ram[pos]; | |||
} else { | |||
// 6bit mode is a bit more complicated because the 6-bit number often | |||
// spans two bytes. We manage this by loading two bytes into a uint16_t | |||
// and then shift it right properly. | |||
// bitpos represents the leftmost bit of our 6bit number. | |||
int bitpos = lcd->curcol * 6; | |||
// offset represents the shift right we need to perform from the two | |||
// bytes following bitpos/8 so that we can have our number with a 6-bit | |||
// mask. | |||
// Example, col 3 has a bitpos of 18, which means that it loads bytes 2 | |||
// and 3. Its bits would be in bit pos 14:8, which means it has an | |||
// offset of 8. There is always an offset and its always in the 3-10 | |||
// range | |||
int offset = 10 - (bitpos % 8); // 10 is for 16bit - 6bit | |||
int pos = (lcd->currow * T6A04_ROWSIZE) + (bitpos / 8); | |||
uint16_t word = lcd->ram[pos] << 8; | |||
word |= lcd->ram[pos+1]; | |||
res = (word >> offset) & 0x3f; | |||
} | |||
_advance(lcd); | |||
return res; | |||
} | |||
void t6a04_data_wr(T6A04 *lcd, uint8_t val) | |||
{ | |||
lcd->just_moved = false; | |||
if (lcd->has8bitmode) { | |||
int pos = lcd->currow * T6A04_ROWSIZE + lcd->curcol; | |||
lcd->ram[pos] = val; | |||
} else { | |||
// See comments in t6a04_data_rd(). | |||
int bitpos = lcd->curcol * 6; | |||
int offset = 10 - (bitpos % 8); | |||
int pos = (lcd->currow * T6A04_ROWSIZE) + (bitpos / 8); | |||
uint16_t word = lcd->ram[pos] << 8; | |||
word |= lcd->ram[pos+1]; | |||
// word contains our current ram value. Let's fit val in this. | |||
word &= ~(0x003f << offset); | |||
word |= val << offset; | |||
lcd->ram[pos] = word >> 8; | |||
lcd->ram[pos+1] = word & 0xff; | |||
} | |||
_advance(lcd); | |||
} | |||
bool t6a04_pixel(T6A04 *lcd, uint8_t y, uint8_t x) | |||
{ | |||
x = (x + lcd->offset) & 0x3f; | |||
uint8_t val = lcd->ram[x * T6A04_ROWSIZE + (y / 8)]; | |||
return (val >> (7 - (y % 8))) & 1; | |||
} |
@@ -0,0 +1,41 @@ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#define T6A04_ROWSIZE (120/8) | |||
#define T6A04_RAMSIZE 64*T6A04_ROWSIZE | |||
typedef enum { | |||
T6A04_XDEC = 0, | |||
T6A04_XINC = 1, | |||
T6A04_YDEC = 2, | |||
T6A04_YINC = 3 | |||
} T6A04_INCMODE; | |||
typedef struct { | |||
// RAM is organized in 64 rows of 120 pixels (there is some offscreen | |||
// memory). Each byte holds 8 pixels. unset means no pixel, set means pixel. | |||
uint8_t ram[T6A04_RAMSIZE]; | |||
bool enabled; | |||
// Whether the 8bit mode is enabled. | |||
bool has8bitmode; | |||
// Current "increment mode" | |||
T6A04_INCMODE incmode; | |||
// current Z offset | |||
uint8_t offset; | |||
// Currently active row | |||
uint8_t currow; | |||
// Currently active col (actual meaning depends on whether we're in 8bit | |||
// mode) | |||
uint8_t curcol; | |||
// True when a movement command was just made or if the LCD was just | |||
// initialized. When this is true, a read operation on the data port will be | |||
// invalid (returns zero and doesn't autoinc). | |||
bool just_moved; | |||
} T6A04; | |||
void t6a04_init(T6A04 *lcd); | |||
uint8_t t6a04_cmd_rd(T6A04 *lcd); | |||
void t6a04_cmd_wr(T6A04 *lcd, uint8_t val); | |||
uint8_t t6a04_data_rd(T6A04 *lcd); | |||
void t6a04_data_wr(T6A04 *lcd, uint8_t val); | |||
bool t6a04_pixel(T6A04 *lcd, uint8_t y, uint8_t x); |
@@ -0,0 +1,314 @@ | |||
/* TI-84+ | |||
* | |||
* A plain TI-84 with its built-in keyboard as an input and its LCD screen | |||
* as an output. | |||
* | |||
* Uses XCB to render the screen and record keystrokes. | |||
*/ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <stdbool.h> | |||
#include <xcb/xcb.h> | |||
#include "../../emul.h" | |||
#include "t6a04.h" | |||
#include "kbd.h" | |||
#define RAMSTART 0x8000 | |||
#define KBD_PORT 0x01 | |||
#define INTERRUPT_PORT 0x03 | |||
#define LCD_CMD_PORT 0x10 | |||
#define LCD_DATA_PORT 0x11 | |||
#define MAX_ROMSIZE 0x2000 | |||
static xcb_connection_t *conn; | |||
static xcb_screen_t *screen; | |||
/* graphics contexts */ | |||
static xcb_gcontext_t fg; | |||
/* win */ | |||
static xcb_drawable_t win; | |||
// pixels to draw. We draw them in one shot. | |||
static xcb_rectangle_t rectangles[96*64]; | |||
static Machine *m; | |||
static T6A04 lcd; | |||
static bool lcd_changed; | |||
static KBD kbd; | |||
static bool on_was_pressed; | |||
static uint8_t iord_lcd_cmd() | |||
{ | |||
return t6a04_cmd_rd(&lcd); | |||
} | |||
static uint8_t iord_lcd_data() | |||
{ | |||
return t6a04_data_rd(&lcd); | |||
} | |||
static uint8_t iord_kbd() | |||
{ | |||
return kbd_rd(&kbd); | |||
} | |||
static uint8_t iord_interrupt() | |||
{ | |||
return on_was_pressed ? 1 : 0; | |||
} | |||
static void iowr_lcd_cmd(uint8_t val) | |||
{ | |||
t6a04_cmd_wr(&lcd, val); | |||
} | |||
static void iowr_lcd_data(uint8_t val) | |||
{ | |||
lcd_changed = true; | |||
t6a04_data_wr(&lcd, val); | |||
} | |||
static void iowr_kbd(uint8_t val) | |||
{ | |||
kbd_wr(&kbd, val); | |||
} | |||
static void iowr_interrupt(uint8_t val) | |||
{ | |||
if ((val & 1) == 0) { | |||
on_was_pressed = false; | |||
} | |||
} | |||
// TIL: XCB doesn't have a builtin way to translate a keycode to an ASCII char. | |||
// Using Xlib looks complicated. This will probably not work in many cases (non | |||
// query keyboards and all...), but for now, let's go with this. | |||
static uint8_t keycode_to_tikbd(xcb_keycode_t kc) | |||
{ | |||
switch (kc) { | |||
case 0x0a: return 0x41; // 1 | |||
case 0x0b: return 0x31; // 2 | |||
case 0x0c: return 0x21; // 3 | |||
case 0x0d: return 0x42; // 4 | |||
case 0x0e: return 0x32; // 5 | |||
case 0x0f: return 0x22; // 6 | |||
case 0x10: return 0x43; // 7 | |||
case 0x11: return 0x33; // 8 | |||
case 0x12: return 0x23; // 9 | |||
case 0x13: return 0x40; // 0 | |||
case 0x14: return 0x12; // - | |||
case 0x15: return 0x11; // + | |||
case 0x16: return 0x67; // DEL | |||
case 0x18: return 0x23; // Q | |||
case 0x19: return 0x12; // W | |||
case 0x1a: return 0x45; // E | |||
case 0x1b: return 0x13; // R | |||
case 0x1c: return 0x42; // T | |||
case 0x1d: return 0x41; // Y | |||
case 0x1e: return 0x32; // U | |||
case 0x1f: return 0x54; // I | |||
case 0x20: return 0x43; // O | |||
case 0x21: return 0x33; // P | |||
case 0x22: return 0x34; // ( | |||
case 0x23: return 0x24; // ) | |||
case 0x24: return 0x10; // Return | |||
case 0x25: return KBD_ALPHA; // LCTRL | |||
case 0x26: return 0x56; // A | |||
case 0x27: return 0x52; // S | |||
case 0x28: return 0x55; // D | |||
case 0x29: return 0x35; // F | |||
case 0x2a: return 0x25; // G | |||
case 0x2b: return 0x15; // H | |||
case 0x2c: return 0x44; // J | |||
case 0x2d: return 0x34; // K | |||
case 0x2e: return 0x24; // L | |||
case 0x2f: return 0x30; // : | |||
case 0x30: return 0x11; // " | |||
case 0x32: return KBD_2ND; // Lshift | |||
case 0x34: return 0x31; // Z | |||
case 0x35: return 0x51; // X | |||
case 0x36: return 0x36; // C | |||
case 0x37: return 0x22; // V | |||
case 0x38: return 0x46; // B | |||
case 0x39: return 0x53; // N | |||
case 0x3a: return 0x14; // M | |||
case 0x3b: return 0x44; // , | |||
case 0x3c: return 0x30; // . | |||
case 0x3d: return 0x20; // ? | |||
case 0x41: return 0x40; // Space | |||
default: return 0; | |||
} | |||
} | |||
void create_window() | |||
{ | |||
uint32_t mask; | |||
uint32_t values[2]; | |||
/* Create the window */ | |||
win = xcb_generate_id(conn); | |||
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; | |||
values[0] = screen->white_pixel; | |||
values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | | |||
XCB_EVENT_MASK_KEY_RELEASE; | |||
xcb_create_window( | |||
conn, | |||
screen->root_depth, | |||
win, | |||
screen->root, | |||
0, 0, | |||
150, 150, | |||
10, | |||
XCB_WINDOW_CLASS_INPUT_OUTPUT, | |||
screen->root_visual, | |||
mask, values); | |||
fg = xcb_generate_id(conn); | |||
mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES; | |||
values[0] = screen->black_pixel; | |||
values[1] = 0; | |||
xcb_create_gc(conn, fg, screen->root, mask, values); | |||
/* Map the window on the screen */ | |||
xcb_map_window(conn, win); | |||
} | |||
bool get_pixel(int x, int y) | |||
{ | |||
return t6a04_pixel(&lcd, x, y); | |||
} | |||
void draw_pixels() | |||
{ | |||
xcb_get_geometry_reply_t *geom; | |||
geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL); | |||
xcb_clear_area( | |||
conn, 0, win, 0, 0, geom->width, geom->height); | |||
// Figure out inner size to maximize a 96x64 screen (1.5 aspect ratio) | |||
int psize = geom->height / 64; | |||
if (geom->width / 96 < psize) { | |||
// width is the constraint | |||
psize = geom->width / 96; | |||
} | |||
int innerw = psize * 96; | |||
int innerh = psize * 64; | |||
int innerx = (geom->width - innerw) / 2; | |||
int innery = (geom->height - innerh) / 2; | |||
int drawcnt = 0; | |||
for (int i=0; i<96; i++) { | |||
for (int j=0; j<64; j++) { | |||
if (get_pixel(i, j)) { | |||
int x = innerx + (i*psize); | |||
int y = innery + (j*psize); | |||
rectangles[drawcnt].x = x; | |||
rectangles[drawcnt].y = y; | |||
rectangles[drawcnt].height = psize; | |||
rectangles[drawcnt].width = psize; | |||
drawcnt++; | |||
} | |||
} | |||
} | |||
if (drawcnt) { | |||
xcb_poly_fill_rectangle( | |||
conn, win, fg, drawcnt, rectangles); | |||
} | |||
lcd_changed = false; | |||
xcb_flush(conn); | |||
} | |||
void event_loop() | |||
{ | |||
if (!emul_step()) { | |||
// We're done | |||
return; | |||
} | |||
while (1) { | |||
emul_step(); | |||
if (lcd_changed) { | |||
// To avoid overdrawing, we'll let the CPU run a bit to finish its | |||
// drawing operation. | |||
emul_steps(100); | |||
draw_pixels(); | |||
} | |||
xcb_generic_event_t *e = xcb_poll_for_event(conn); | |||
if (!e) { | |||
continue; | |||
} | |||
switch (e->response_type & ~0x80) { | |||
/* ESC to exit */ | |||
case XCB_KEY_RELEASE: | |||
case XCB_KEY_PRESS: { | |||
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e; | |||
if (ev->detail == 0x09) return; | |||
if (ev->detail == 0x31 && e->response_type == XCB_KEY_PRESS) { | |||
// tilde, mapped to ON | |||
on_was_pressed = true; | |||
Z80INT(&m->cpu, 0); | |||
Z80Execute(&m->cpu); // unhalts the CPU | |||
} | |||
uint8_t key = keycode_to_tikbd(ev->detail); | |||
if (key) { | |||
kbd_setkey(&kbd, key, e->response_type == XCB_KEY_PRESS); | |||
} | |||
break; | |||
} | |||
case XCB_EXPOSE: { | |||
draw_pixels(); | |||
break; | |||
} | |||
default: { | |||
break; | |||
} | |||
} | |||
free(e); | |||
} | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
if (argc != 2) { | |||
fprintf(stderr, "Usage: ./ti84 /path/to/rom\n"); | |||
return 1; | |||
} | |||
FILE *fp = fopen(argv[1], "r"); | |||
if (fp == NULL) { | |||
fprintf(stderr, "Can't open %s\n", argv[1]); | |||
return 1; | |||
} | |||
m = emul_init(); | |||
m->ramstart = RAMSTART; | |||
int i = 0; | |||
int c; | |||
while ((c = fgetc(fp)) != EOF && i < MAX_ROMSIZE) { | |||
m->mem[i++] = c & 0xff; | |||
} | |||
pclose(fp); | |||
if (i == MAX_ROMSIZE) { | |||
fprintf(stderr, "ROM image too large.\n"); | |||
return 1; | |||
} | |||
t6a04_init(&lcd); | |||
kbd_init(&kbd); | |||
lcd_changed = false; | |||
on_was_pressed = false; | |||
m->iord[KBD_PORT] = iord_kbd; | |||
m->iord[INTERRUPT_PORT] = iord_interrupt; | |||
m->iord[LCD_CMD_PORT] = iord_lcd_cmd; | |||
m->iord[LCD_DATA_PORT] = iord_lcd_data; | |||
m->iowr[KBD_PORT] = iowr_kbd; | |||
m->iowr[INTERRUPT_PORT] = iowr_interrupt; | |||
m->iowr[LCD_CMD_PORT] = iowr_lcd_cmd; | |||
m->iowr[LCD_DATA_PORT] = iowr_lcd_data; | |||
conn = xcb_connect(NULL, NULL); | |||
screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; | |||
create_window(); | |||
draw_pixels(); | |||
event_loop(); | |||
emul_printdebug(); | |||
return 0; | |||
} |
@@ -1,4 +1,4 @@ | |||
TARGET = os.rom | |||
TARGET = os.bin | |||
BASEDIR = ../.. | |||
ZASM = $(BASEDIR)/emul/zasm/zasm | |||
KERNEL = $(BASEDIR)/kernel | |||
@@ -9,10 +9,13 @@ MKTIUPGRADE = mktiupgrade | |||
all: $(TARGET) | |||
$(TARGET): glue.asm | |||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@ | |||
os.rom: $(TARGET) | |||
cp $(TARGET) $@ | |||
truncate -s 1M $@ | |||
os.8xu: $(TARGET) | |||
$(MKTIUPGRADE) -p -k keys/0A.key -d TI-84+ $(TARGET) $@ 00 | |||
os.8xu: os.rom | |||
$(MKTIUPGRADE) -p -k keys/0A.key -d TI-84+ os.rom $@ 00 | |||
.PHONY: send | |||
send: os.8xu | |||