/* Micro terminal emulator, implementing all of the functionality of the ADM3A. * * It doesn’t yet do scrollback, or resize. See admu_shell.c for * hanging it off a pty. */ #define _BSD_SOURCE // to get random() in dietlibc #include #include #include #include #include "admu.h" #include "font-5x8.xpm" #define LEN(x) (sizeof(x)/sizeof((x)[0])) static void init_font(admu *t) { for (int i = 0; i != 256; i++) { for (int j = 0; j != admu_font_h; j++) { for (int k = 0; k != admu_font_w; k++) { t->font[i][j][k] = random(); } } } int y = 0; for (char **raster = font_5x8_xpm + 4; raster != font_5x8_xpm + LEN(font_5x8_xpm); raster++) { for (int x = 0; raster[0][x]; x++) { uint32_t pixel = (raster[0][x] == '+' ? 0 : -1); int glyph = 32 + y / admu_font_h * 16 + x / admu_font_w; t->font[glyph][y % admu_font_h][x % admu_font_w] = pixel; } y++; } } static inline void admu_clear(admu *t) { memset(t->screenchars, ' ', t->rows*t->cols); t->cursor_pos = 0; } void admu_init(admu *t, int cols, int rows, unsigned char *screenchars) { t->rows = rows; t->cols = cols; t->screenchars = screenchars; t->state = admu_ready; admu_clear(t); init_font(t); } void admu_generate_chars(admu *t, uint32_t *fb, int width, int stride, int height) { for (int y = 0; y < t->rows; y++) { if ((y + 1) * admu_font_h > height) break; for (int x = 0; x < t->cols; x++) { if ((x + 1) * admu_font_w > width) break; for (int i = 0; i < admu_font_h; i++) { memcpy(fb + ((y * admu_font_h) + i) * stride + x * admu_font_w, t->font[t->screenchars[y*t->cols + x]][i], sizeof(uint32_t) * admu_font_w); } } } int cursor_col = t->cursor_pos % t->cols, cursor_row = t->cursor_pos / t->cols; if ( (cursor_col+1) * admu_font_w > width || (cursor_row+1) * admu_font_h > height) return; int cursor_pix = cursor_col * admu_font_w + cursor_row * stride * admu_font_h; for (int i = 0; i < admu_font_h; i++) { for (int j = 0; j < admu_font_w; j++) { fb[cursor_pix + i * stride + j] ^= 0x55555555ul; } } } static inline int ctrl(int c) { return c & 0x1f; } /* Occasionally the ADM-3A is in an unusual state for a * multi-character sequence (specifically, CUP, cursor position), * which is handled here. */ static inline void admu_handle_seq(admu *t, unsigned char c) { switch(t->state) { case admu_esc: t->state = (c == '=') ? admu_cup : admu_ready; break; case admu_cup: t->pending_cup = c - ' '; t->state = admu_cup2; break; case admu_cup2: t->cursor_pos = t->pending_cup * t->cols + c - ' '; if (t->cursor_pos < 0) t->cursor_pos = 0; if (t->cursor_pos >= t->rows*t->cols) t->cursor_pos = t->rows*t->cols - 1; t->state = admu_ready; break; default: t->state = admu_ready; break; } } static void scroll_up_one_line(admu *t) { memmove(t->screenchars, t->screenchars + t->cols, (t->rows-1) * t->cols); memset(t->screenchars + (t->rows-1) * t->cols, ' ', t->cols); t->cursor_pos -= t->cols; } void admu_handle_char(admu *t, unsigned char c) { if (t->state != admu_ready) { admu_handle_seq(t, c); return; } if (c == ctrl('H')) { // backspace t->cursor_pos--; } else if (c == ctrl('L')) { // right t->cursor_pos++; } else if (c == ctrl('K')) { // up t->cursor_pos -= t->cols; } else if (c == ctrl('J')) { // LF, down, "\n", newline t->cursor_pos += t->cols; } else if (c == '\r') { // cr, ctrl('M') t->cursor_pos -= t->cursor_pos % t->cols; } else if (c == ctrl('^')) { // ^^ is home t->cursor_pos = 0; } else if (c == '\032') { // ^Z clears screen admu_clear(t); } else if (c == '\033') { // ESC to start cup (cursor position) sequence t->state = admu_esc; } else { t->screenchars[t->cursor_pos++] = c; } if (t->cursor_pos < 0) t->cursor_pos = 0; while (t->cursor_pos >= t->rows * t->cols) scroll_up_one_line(t); }