editor: add example
This adds a simple text editor with basic visual line-editing capabilities.
This commit is contained in:
parent
3b82199d08
commit
c86fcbd6af
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
|||||||
*.out
|
*.out
|
||||||
res/mai.data
|
res/mai.data
|
||||||
tools/ttf-convert
|
tools/ttf-convert
|
||||||
|
tools/ttf-bitmap
|
||||||
common/keyboard.cpp
|
common/keyboard.cpp
|
||||||
common/keyboard.hpp
|
common/keyboard.hpp
|
||||||
wordle/word_list.hpp
|
wordle/word_list.hpp
|
7
Makefile
7
Makefile
@ -91,6 +91,13 @@ scsp/sound_cpu__slot.elf: scsp/sound_cpu__slot.o m68k/slot.bin.o
|
|||||||
|
|
||||||
scsp/sound_cpu__interrupt.elf: scsp/sound_cpu__interrupt.o m68k/interrupt.bin.o sh/lib1funcs.o res/sperrypc.font.bin.o common/draw_font.o common/palette.o
|
scsp/sound_cpu__interrupt.elf: scsp/sound_cpu__interrupt.o m68k/interrupt.bin.o sh/lib1funcs.o res/sperrypc.font.bin.o common/draw_font.o common/palette.o
|
||||||
|
|
||||||
|
res/sperrypc.bitmap.bin: tools/ttf-bitmap
|
||||||
|
./tools/ttf-bitmap 20 7f res/Bm437_SperryPC_CGA.otb $@
|
||||||
|
|
||||||
|
editor/main_saturn.o: common/keyboard.hpp editor/editor.hpp
|
||||||
|
|
||||||
|
editor/main_saturn.elf: editor/main_saturn.o res/sperrypc.bitmap.bin.o sh/lib1funcs.o common/keyboard.o saturn/start.o
|
||||||
|
|
||||||
# clean
|
# clean
|
||||||
clean: clean-sh
|
clean: clean-sh
|
||||||
clean-sh:
|
clean-sh:
|
||||||
|
@ -1,19 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void copy(T * dst, const T * src, int32_t n) noexcept
|
inline void copy(T * dst, const T * src, int32_t n) noexcept
|
||||||
{
|
{
|
||||||
|
n = n / (sizeof (T));
|
||||||
while (n > 0) {
|
while (n > 0) {
|
||||||
*dst++ = *src++;
|
*dst++ = *src++;
|
||||||
n -= (sizeof (T));
|
n--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void fill(T * dst, const T src, int32_t n) noexcept
|
inline void fill(T * dst, const T src, int32_t n) noexcept
|
||||||
{
|
{
|
||||||
|
n = n / (sizeof (T));
|
||||||
while (n > 0) {
|
while (n > 0) {
|
||||||
*dst++ = src;
|
*dst++ = src;
|
||||||
n -= (sizeof (T));
|
n--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void move(T * dst, const T * src, int32_t n) noexcept
|
||||||
|
{
|
||||||
|
n = n / (sizeof (T));
|
||||||
|
if (dst < src) {
|
||||||
|
// d s
|
||||||
|
// 0123456789
|
||||||
|
while (n > 0) {
|
||||||
|
*dst++ = *src++;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// s d
|
||||||
|
// 0123456789
|
||||||
|
while (n) {
|
||||||
|
n--;
|
||||||
|
dst[n] = src[n];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
common/intback.hpp
Normal file
106
common/intback.hpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
namespace intback {
|
||||||
|
enum intback_fsm {
|
||||||
|
PORT_STATUS = 0,
|
||||||
|
PERIPHERAL_ID,
|
||||||
|
DATA1,
|
||||||
|
DATA2,
|
||||||
|
DATA3,
|
||||||
|
DATA4,
|
||||||
|
FSM_NEXT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct intback_state {
|
||||||
|
uint8_t fsm;
|
||||||
|
uint8_t controller_ix;
|
||||||
|
uint8_t port_ix;
|
||||||
|
uint8_t oreg_ix;
|
||||||
|
|
||||||
|
uint8_t port_connected;
|
||||||
|
uint8_t data_size;
|
||||||
|
uint8_t peripheral_type;
|
||||||
|
uint8_t kbd_bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*keyboard_func_ptr)(const enum keysym k, uint8_t kbd_bits);
|
||||||
|
|
||||||
|
static intback_state state;
|
||||||
|
|
||||||
|
inline void keyboard_fsm(keyboard_func_ptr callback)
|
||||||
|
{
|
||||||
|
if ((smpc.reg.SR & SR__PDL) != 0) {
|
||||||
|
// PDL == 1; 1st peripheral data
|
||||||
|
state.oreg_ix = 0;
|
||||||
|
state.controller_ix = 0;
|
||||||
|
state.port_ix = 0;
|
||||||
|
state.fsm = PORT_STATUS;
|
||||||
|
|
||||||
|
state.port_connected = 0;
|
||||||
|
state.data_size = 0;
|
||||||
|
state.peripheral_type = 0;
|
||||||
|
state.kbd_bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This intback handling is oversimplified:
|
||||||
|
|
||||||
|
- up to 2 controllers may be (directly) connected
|
||||||
|
- multitaps are not parsed correctly
|
||||||
|
*/
|
||||||
|
while (state.oreg_ix < 32) {
|
||||||
|
reg8 const& oreg = smpc.reg.oreg[state.oreg_ix++];
|
||||||
|
switch (state.fsm) {
|
||||||
|
case PORT_STATUS:
|
||||||
|
state.port_connected = (PORT_STATUS__CONNECTORS(oreg) == 1);
|
||||||
|
if (state.port_connected) {
|
||||||
|
if (PORT_STATUS__MULTITAP_ID(oreg) != 0xf) {
|
||||||
|
// this port is not directly connected; abort parse:
|
||||||
|
goto abort;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.fsm = FSM_NEXT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PERIPHERAL_ID:
|
||||||
|
state.peripheral_type = PERIPHERAL_ID__TYPE(oreg);
|
||||||
|
state.data_size = PERIPHERAL_ID__DATA_SIZE(oreg);
|
||||||
|
break;
|
||||||
|
case DATA1:
|
||||||
|
break;
|
||||||
|
case DATA2:
|
||||||
|
break;
|
||||||
|
case DATA3:
|
||||||
|
state.kbd_bits = oreg & 0b1111;
|
||||||
|
break;
|
||||||
|
case DATA4:
|
||||||
|
{
|
||||||
|
uint32_t keysym = oreg;
|
||||||
|
enum keysym k = scancode_to_keysym(keysym);
|
||||||
|
callback(k, state.kbd_bits);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((state.fsm >= PERIPHERAL_ID && state.data_size <= 0) || state.fsm == FSM_NEXT) {
|
||||||
|
if (state.port_ix == 1)
|
||||||
|
break;
|
||||||
|
else {
|
||||||
|
state.port_ix++;
|
||||||
|
state.controller_ix++;
|
||||||
|
state.fsm = PORT_STATUS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.fsm++;
|
||||||
|
state.data_size--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((smpc.reg.SR & SR__NPE) != 0) {
|
||||||
|
smpc.reg.ireg[0] = INTBACK__IREG0__CONTINUE;
|
||||||
|
} else {
|
||||||
|
abort:
|
||||||
|
smpc.reg.ireg[0] = INTBACK__IREG0__BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,8 @@ def parse_printable():
|
|||||||
for line in f:
|
for line in f:
|
||||||
if not line.strip():
|
if not line.strip():
|
||||||
continue
|
continue
|
||||||
yield line.strip().split()
|
k, v1, v2 = line.strip().split()
|
||||||
|
yield k, (v1, v2)
|
||||||
|
|
||||||
|
|
||||||
scancodes = set()
|
scancodes = set()
|
||||||
@ -46,7 +47,15 @@ keymap = build_keymap()
|
|||||||
|
|
||||||
|
|
||||||
printable = dict(parse_printable())
|
printable = dict(parse_printable())
|
||||||
printable["SPACE"] = ' '
|
printable["SPACE"] = (' ', ' ')
|
||||||
|
|
||||||
|
def e(s):
|
||||||
|
if s == '\\':
|
||||||
|
return '\\\\'
|
||||||
|
elif s == '\'':
|
||||||
|
return '\\\''
|
||||||
|
else:
|
||||||
|
return s
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.argv[1] == "header":
|
if sys.argv[1] == "header":
|
||||||
@ -62,7 +71,8 @@ if sys.argv[1] == "header":
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
print("enum keysym scancode_to_keysym(uint32_t scancode);")
|
print("enum keysym scancode_to_keysym(uint32_t scancode);")
|
||||||
print("char16_t keysym_to_char16(enum keysym k);")
|
print("int32_t keysym_to_char(enum keysym k, bool shift);")
|
||||||
|
|
||||||
|
|
||||||
if sys.argv[1] == "definition":
|
if sys.argv[1] == "definition":
|
||||||
print("#include <stdint.h>")
|
print("#include <stdint.h>")
|
||||||
@ -81,15 +91,38 @@ if sys.argv[1] == "definition":
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
print("char16_t keysym_to_char16(enum keysym k)")
|
print("static constexpr inline int32_t unshift_char(enum keysym k)")
|
||||||
print("{")
|
print("{")
|
||||||
print(" switch(k) {")
|
print(" switch(k) {")
|
||||||
for keysym, _ in keymap:
|
for keysym, _ in keymap:
|
||||||
if keysym in printable:
|
if keysym in printable:
|
||||||
value = printable[keysym]
|
value = printable[keysym]
|
||||||
if value == '\\':
|
print(f" case keysym::{keysym}: return '{e(value[0])}';")
|
||||||
value = '\\\\';
|
|
||||||
print(f" case keysym::{keysym}: return '{value}';")
|
|
||||||
print(" default: return -1;")
|
print(" default: return -1;")
|
||||||
print(" }")
|
print(" }")
|
||||||
print("}")
|
print("}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("static constexpr inline int32_t shift_char(enum keysym k)")
|
||||||
|
print("{")
|
||||||
|
print(" switch(k) {")
|
||||||
|
for keysym, _ in keymap:
|
||||||
|
if keysym in printable:
|
||||||
|
value = printable[keysym]
|
||||||
|
print(f" case keysym::{keysym}: return '{e(value[1])}';")
|
||||||
|
print(" default: return -1;")
|
||||||
|
print(" }")
|
||||||
|
print("}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("""int32_t keysym_to_char(enum keysym k, bool shift)
|
||||||
|
{
|
||||||
|
if (shift) {
|
||||||
|
return shift_char(k);
|
||||||
|
} else {
|
||||||
|
return unshift_char(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
@ -93,3 +93,8 @@ PAGE_UP 8b
|
|||||||
DELETE 85
|
DELETE 85
|
||||||
END 88
|
END 88
|
||||||
PAGE_DOWN 8c
|
PAGE_DOWN 8c
|
||||||
|
|
||||||
|
ARROW_LEFT 86
|
||||||
|
ARROW_UP 89
|
||||||
|
ARROW_DOWN 8A
|
||||||
|
ARROW_RIGHT 8d
|
||||||
|
13
common/minmax.hpp
Normal file
13
common/minmax.hpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline constexpr T min(T a, T b)
|
||||||
|
{
|
||||||
|
return a > b ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline constexpr T max(T a, T b)
|
||||||
|
{
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
@ -1,52 +1,52 @@
|
|||||||
K1 1
|
K1 1 !
|
||||||
K2 2
|
K2 2 "
|
||||||
K3 3
|
K3 3 #
|
||||||
K4 4
|
K4 4 $
|
||||||
K5 5
|
K5 5 %
|
||||||
K6 6
|
K6 6 &
|
||||||
K7 7
|
K7 7 '
|
||||||
K8 8
|
K8 8 (
|
||||||
K9 9
|
K9 9 )
|
||||||
K0 0
|
K0 0 ~
|
||||||
MINUS -
|
MINUS - =
|
||||||
CAROT ^
|
CAROT ^ ~
|
||||||
JPY $
|
JPY $ |
|
||||||
|
|
||||||
Q q
|
Q q Q
|
||||||
W w
|
W w W
|
||||||
E e
|
E e E
|
||||||
R r
|
R r R
|
||||||
T t
|
T t T
|
||||||
Y y
|
Y y Y
|
||||||
U u
|
U u U
|
||||||
I i
|
I i I
|
||||||
O o
|
O o O
|
||||||
P p
|
P p P
|
||||||
AT @
|
AT @ `
|
||||||
LEFT_SQUARE [
|
LEFT_SQUARE [ {
|
||||||
|
|
||||||
|
|
||||||
A a
|
A a A
|
||||||
S s
|
S s S
|
||||||
D d
|
D d D
|
||||||
F f
|
F f F
|
||||||
G g
|
G g G
|
||||||
H h
|
H h H
|
||||||
J j
|
J j J
|
||||||
K k
|
K k K
|
||||||
L l
|
L l L
|
||||||
SEMICOLON ;
|
SEMICOLON ; +
|
||||||
COLON :
|
COLON : *
|
||||||
RIGHT_SQUARE ]
|
RIGHT_SQUARE ] }
|
||||||
|
|
||||||
Z z
|
Z z Z
|
||||||
X x
|
X x X
|
||||||
C c
|
C c C
|
||||||
V v
|
V v V
|
||||||
B b
|
B b B
|
||||||
N n
|
N n N
|
||||||
M m
|
M m M
|
||||||
COMMA ,
|
COMMA , <
|
||||||
PERIOD .
|
PERIOD . >
|
||||||
FORWARD_SLASH /
|
FORWARD_SLASH / ?
|
||||||
BACK_SLASH \
|
BACK_SLASH \ _
|
||||||
|
383
editor/editor.hpp
Normal file
383
editor/editor.hpp
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../common/copy.hpp"
|
||||||
|
#include "../common/minmax.hpp"
|
||||||
|
|
||||||
|
namespace editor {
|
||||||
|
|
||||||
|
template <int C>
|
||||||
|
struct line {
|
||||||
|
uint8_t buf[C];
|
||||||
|
int32_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cursor {
|
||||||
|
int32_t row;
|
||||||
|
int32_t col;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
struct buffer {
|
||||||
|
line<C> row[R];
|
||||||
|
line<C> * lines[R];
|
||||||
|
int32_t length;
|
||||||
|
int32_t alloc_ix;
|
||||||
|
struct {
|
||||||
|
int32_t cell_width;
|
||||||
|
int32_t cell_height;
|
||||||
|
int32_t top;
|
||||||
|
int32_t left;
|
||||||
|
} window;
|
||||||
|
struct cursor cursor;
|
||||||
|
|
||||||
|
typedef line<C> line_type;
|
||||||
|
|
||||||
|
inline static constexpr int32_t rows_max_length(){return R;}
|
||||||
|
inline static constexpr int32_t cols_max_length(){return C;}
|
||||||
|
|
||||||
|
inline constexpr buffer(int32_t width, int32_t height);
|
||||||
|
inline constexpr line<C> * allocate();
|
||||||
|
inline constexpr void deallocate(line<C> *& l);
|
||||||
|
inline constexpr bool put(uint8_t c);
|
||||||
|
inline constexpr bool backspace();
|
||||||
|
inline constexpr bool cursor_left();
|
||||||
|
inline constexpr bool cursor_right();
|
||||||
|
inline constexpr bool cursor_up();
|
||||||
|
inline constexpr bool cursor_down();
|
||||||
|
inline constexpr bool cursor_home();
|
||||||
|
inline constexpr bool cursor_end();
|
||||||
|
inline constexpr bool enter();
|
||||||
|
private:
|
||||||
|
inline constexpr void scroll_left();
|
||||||
|
inline constexpr void scroll_right();
|
||||||
|
inline constexpr void scroll_up();
|
||||||
|
inline constexpr void scroll_down();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr buffer<C, R>::buffer(int32_t width, int32_t height)
|
||||||
|
{
|
||||||
|
this->length = 1;
|
||||||
|
this->alloc_ix = 0;
|
||||||
|
this->window.top = 0;
|
||||||
|
this->window.left = 0;
|
||||||
|
this->window.cell_height = height;
|
||||||
|
this->window.cell_width = width;
|
||||||
|
this->cursor.row = 0;
|
||||||
|
this->cursor.col = 0;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < R; i++) {
|
||||||
|
this->row[i].length = -1;
|
||||||
|
this->lines[i] = nullptr;
|
||||||
|
|
||||||
|
for (int32_t j = 0; j < C; j++) {
|
||||||
|
this->row[i].buf[j] = 0x7f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr line<C> * buffer<C, R>::allocate()
|
||||||
|
{
|
||||||
|
line<C> * l;
|
||||||
|
while ((l = &this->row[this->alloc_ix])->length != -1) {
|
||||||
|
// R must be a power of 2
|
||||||
|
static_assert((R & (R - 1)) == 0);
|
||||||
|
this->alloc_ix = (this->alloc_ix + 1) & (R - 1);
|
||||||
|
}
|
||||||
|
l->length = 0;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::deallocate(line<C> *& l)
|
||||||
|
{
|
||||||
|
// does not touch alloc_ix
|
||||||
|
fill<uint8_t>(l->buf, 0x7f, l->length);
|
||||||
|
l->length = -1;
|
||||||
|
l = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::put(const uint8_t c)
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
line<C> *& l = this->lines[cur.row];
|
||||||
|
if (l == nullptr) {
|
||||||
|
l = this->allocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// v
|
||||||
|
// 0123
|
||||||
|
if (l->length >= C || cur.col >= C || cur.col < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (l->length > cur.col) {
|
||||||
|
// c (5)
|
||||||
|
// 01234
|
||||||
|
// 012a34
|
||||||
|
// 4, 3, (5 - 3)
|
||||||
|
move(&l->buf[cur.col+1], &l->buf[cur.col], l->length - cur.col);
|
||||||
|
}
|
||||||
|
l->buf[cur.col] = c;
|
||||||
|
l->length++;
|
||||||
|
cur.col++;
|
||||||
|
scroll_right();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::backspace()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
if (cur.col < 0 || cur.col > C)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
line<C> *& l = this->lines[cur.row];
|
||||||
|
if (l == nullptr) {
|
||||||
|
// is it possible to get in this state?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (l->length < 0) {
|
||||||
|
// is it possible to get in this state?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur.col == 0) {
|
||||||
|
if (cur.row == 0) return false;
|
||||||
|
// combine this line with the previous line
|
||||||
|
line<C> *& lp = this->lines[cur.row-1];
|
||||||
|
if (lp == nullptr) lp = allocate();
|
||||||
|
// blindly truncate overflowing lines
|
||||||
|
auto length = min(l->length - cur.col, C - lp->length);
|
||||||
|
if (length > 0) move(&lp->buf[lp->length], &l->buf[cur.col], length);
|
||||||
|
|
||||||
|
cur.col = lp->length;
|
||||||
|
scroll_right();
|
||||||
|
lp->length += length;
|
||||||
|
deallocate(l);
|
||||||
|
// 0 a
|
||||||
|
// 1
|
||||||
|
// 2 _ (cur.row, deleted)
|
||||||
|
// 3 b
|
||||||
|
int32_t n_lines = this->length - (cur.row + 1);
|
||||||
|
move(&this->lines[cur.row], &this->lines[cur.row+1], (sizeof (line<C>*)) * n_lines);
|
||||||
|
this->length--;
|
||||||
|
this->lines[this->length] = nullptr;
|
||||||
|
cur.row--;
|
||||||
|
scroll_up();
|
||||||
|
} else {
|
||||||
|
// c
|
||||||
|
// 01234
|
||||||
|
auto length = l->length - cur.col;
|
||||||
|
if (length > 0) move(&l->buf[cur.col-1], &l->buf[cur.col], length);
|
||||||
|
|
||||||
|
cur.col--;
|
||||||
|
scroll_left();
|
||||||
|
l->length--;
|
||||||
|
l->buf[l->length] = 0x7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::cursor_left()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
if (cur.col <= 0) {
|
||||||
|
if (cur.row <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cur.row--;
|
||||||
|
scroll_up();
|
||||||
|
|
||||||
|
line<C> const * const l = this->lines[cur.row];
|
||||||
|
int32_t length = (l == nullptr) ? 0 : l->length;
|
||||||
|
cur.col = length;
|
||||||
|
scroll_right();
|
||||||
|
} else {
|
||||||
|
cur.col--;
|
||||||
|
scroll_left();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::cursor_right()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
line<C> const * const l = this->lines[cur.row];
|
||||||
|
int32_t length = (l == nullptr) ? 0 : l->length;
|
||||||
|
if (cur.col >= length) {
|
||||||
|
if (cur.row >= this->length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cur.row++;
|
||||||
|
scroll_down();
|
||||||
|
|
||||||
|
cur.col = 0;
|
||||||
|
scroll_left();
|
||||||
|
} else {
|
||||||
|
cur.col++;
|
||||||
|
scroll_right();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::cursor_up()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
if (cur.row <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cur.row--;
|
||||||
|
scroll_up();
|
||||||
|
|
||||||
|
line<C> const * const l = this->lines[cur.row];
|
||||||
|
int32_t length = (l == nullptr) ? 0 : l->length;
|
||||||
|
cur.col = min(cur.col, length);
|
||||||
|
scroll_left();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::cursor_down()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
if (cur.row >= this->length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cur.row++;
|
||||||
|
scroll_down();
|
||||||
|
|
||||||
|
line<C> const * const l = this->lines[cur.row];
|
||||||
|
int32_t length = (l == nullptr) ? 0 : l->length;
|
||||||
|
cur.col = min(cur.col, length);
|
||||||
|
scroll_left();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::cursor_home()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
line<C> const * const l = this->lines[cur.row];
|
||||||
|
if (l == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cur.col = 0;
|
||||||
|
scroll_left();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::cursor_end()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
line<C> const * const l = this->lines[cur.row];
|
||||||
|
if (l == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cur.col = l->length;
|
||||||
|
scroll_right();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::enter()
|
||||||
|
{
|
||||||
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
|
if (cur.row >= R || cur.row < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this->length > cur.row) {
|
||||||
|
// first, move all lines down one
|
||||||
|
int32_t n_lines = this->length - (cur.row + 1);
|
||||||
|
// don't care about nullptr here, this never dereferences
|
||||||
|
move(&this->lines[cur.row+2], &this->lines[cur.row+1], (sizeof (line<C>*)) * n_lines);
|
||||||
|
}
|
||||||
|
// column-wise copy of the cursor position to the newly-created line
|
||||||
|
line<C> * old_l = this->lines[cur.row];
|
||||||
|
line<C> * new_l = allocate();
|
||||||
|
// v
|
||||||
|
// 01234 (5)
|
||||||
|
if (old_l != nullptr) {
|
||||||
|
new_l->length = old_l->length - cur.col;
|
||||||
|
if (new_l->length > 0) {
|
||||||
|
old_l->length -= new_l->length;
|
||||||
|
copy(&new_l->buf[0], &old_l->buf[cur.col], new_l->length);
|
||||||
|
fill<uint8_t>(&old_l->buf[cur.col], 0x7f, new_l->length);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// nothing to do, new_l->length is already 0
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.row++;
|
||||||
|
scroll_down();
|
||||||
|
this->lines[cur.row] = new_l;
|
||||||
|
cur.col = 0;
|
||||||
|
scroll_left();
|
||||||
|
// because copy() is used instead of put(), length needs to be
|
||||||
|
// incremented here.
|
||||||
|
this->length++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::scroll_up()
|
||||||
|
{
|
||||||
|
if (this->cursor.row < this->window.top)
|
||||||
|
this->window.top = this->cursor.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::scroll_down()
|
||||||
|
{
|
||||||
|
// 0: a -
|
||||||
|
// 1: bv -
|
||||||
|
|
||||||
|
// 0: a
|
||||||
|
// 1: b -
|
||||||
|
// 2: cv -
|
||||||
|
|
||||||
|
if (this->cursor.row >= this->window.top + this->window.cell_height)
|
||||||
|
this->window.top = (this->cursor.row - (this->window.cell_height - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::scroll_left()
|
||||||
|
{
|
||||||
|
if (this->cursor.col < this->window.left)
|
||||||
|
this->window.left = this->cursor.col;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::scroll_right()
|
||||||
|
{
|
||||||
|
// 0: a -
|
||||||
|
// 1: bv -
|
||||||
|
|
||||||
|
// 0: a
|
||||||
|
// 1: b -
|
||||||
|
// 2: cv -
|
||||||
|
|
||||||
|
if (this->cursor.col >= this->window.left + this->window.cell_width)
|
||||||
|
this->window.left = (this->cursor.col - (this->window.cell_width - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
257
editor/main_saturn.cpp
Normal file
257
editor/main_saturn.cpp
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "vdp2.h"
|
||||||
|
#include "smpc.h"
|
||||||
|
#include "scu.h"
|
||||||
|
#include "sh2.h"
|
||||||
|
|
||||||
|
#include "../common/copy.hpp"
|
||||||
|
#include "../common/vdp2_func.hpp"
|
||||||
|
#include "../common/keyboard.hpp"
|
||||||
|
#include "../common/intback.hpp"
|
||||||
|
|
||||||
|
#include "editor.hpp"
|
||||||
|
|
||||||
|
extern void * _sperrypc_bitmap_start __asm("_binary_res_sperrypc_bitmap_bin_start");
|
||||||
|
|
||||||
|
using buffer_type = editor::buffer<64, 64>;
|
||||||
|
constexpr int32_t viewport_max_col = 320 / 8;
|
||||||
|
constexpr int32_t viewport_max_row = 240 / 8;
|
||||||
|
|
||||||
|
static buffer_type buffer {viewport_max_col, viewport_max_row};
|
||||||
|
|
||||||
|
void palette_data()
|
||||||
|
{
|
||||||
|
vdp2.cram.u16[1 + 0 ] = 0;
|
||||||
|
vdp2.cram.u16[2 + 0 ] = 0x7fff;
|
||||||
|
|
||||||
|
vdp2.cram.u16[1 + 16] = 0x7fff;
|
||||||
|
vdp2.cram.u16[2 + 16] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pix_fmt_4bpp
|
||||||
|
{
|
||||||
|
constexpr inline uint32_t
|
||||||
|
bit(uint8_t n, int32_t i)
|
||||||
|
{
|
||||||
|
i &= 7;
|
||||||
|
auto b = (n >> (7 - i)) & 1;
|
||||||
|
return ((b + 1) << ((7 - i) * 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uint32_t
|
||||||
|
bits(uint8_t n)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
bit(n, 0) | bit(n, 1) | bit(n, 2) | bit(n, 3)
|
||||||
|
| bit(n, 4) | bit(n, 5) | bit(n, 6) | bit(n, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(bits(0b1100'1110) == 0x2211'2221);
|
||||||
|
static_assert(bits(0b1010'0101) == 0x2121'1212);
|
||||||
|
static_assert(bits(0b1000'0000) == 0x2111'1111);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cell_data()
|
||||||
|
{
|
||||||
|
const uint8_t * buf = reinterpret_cast<uint8_t*>(&_sperrypc_bitmap_start);
|
||||||
|
|
||||||
|
for (int ix = 0; ix <= (0x7f - 0x20); ix++) {
|
||||||
|
for (int y = 0; y < 8; y++) {
|
||||||
|
uint8_t row = buf[ix * 8 + y];
|
||||||
|
vdp2.vram.u32[(ix * 8) + y] = pix_fmt_4bpp::bits(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct modifiers {
|
||||||
|
uint8_t left_shift;
|
||||||
|
uint8_t right_shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
static modifiers modifier_state = { 0 };
|
||||||
|
|
||||||
|
void keyboard_callback(const enum keysym k, uint8_t kbd_bits)
|
||||||
|
{
|
||||||
|
int32_t shift = modifier_state.left_shift || modifier_state.right_shift;
|
||||||
|
int32_t c = keysym_to_char(k, shift);
|
||||||
|
if (KEYBOARD__MAKE(kbd_bits)) {
|
||||||
|
if (k == keysym::UNKNOWN)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (c != -1) {
|
||||||
|
buffer.put(c);
|
||||||
|
} else {
|
||||||
|
switch (k) {
|
||||||
|
case keysym::BACKSPACE : buffer.backspace(); break;
|
||||||
|
case keysym::ARROW_LEFT : buffer.cursor_left(); break;
|
||||||
|
case keysym::ARROW_RIGHT: buffer.cursor_right(); break;
|
||||||
|
case keysym::ARROW_UP : buffer.cursor_up(); break;
|
||||||
|
case keysym::ARROW_DOWN : buffer.cursor_down(); break;
|
||||||
|
case keysym::HOME : buffer.cursor_home(); break;
|
||||||
|
case keysym::END : buffer.cursor_end(); break;
|
||||||
|
case keysym::ENTER : buffer.enter(); break;
|
||||||
|
case keysym::LEFT_SHIFT : modifier_state.left_shift = 1; break;
|
||||||
|
case keysym::RIGHT_SHIFT: modifier_state.right_shift = 1; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (KEYBOARD__BREAK(kbd_bits)) {
|
||||||
|
switch (k) {
|
||||||
|
case keysym::LEFT_SHIFT : modifier_state.left_shift = 0; break;
|
||||||
|
case keysym::RIGHT_SHIFT: modifier_state.right_shift = 0; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void smpc_int(void) __attribute__ ((interrupt_handler));
|
||||||
|
void smpc_int(void)
|
||||||
|
{
|
||||||
|
scu.reg.IST &= ~(IST__SMPC);
|
||||||
|
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
|
||||||
|
|
||||||
|
intback::keyboard_fsm(keyboard_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int32_t plane_a = 2;
|
||||||
|
constexpr inline int32_t plane_offset(int32_t n) { return n * 0x2000; }
|
||||||
|
|
||||||
|
constexpr int32_t page_size = 64 * 64 * 2; // N0PNB__1WORD (16-bit)
|
||||||
|
constexpr int32_t plane_size = page_size * 1;
|
||||||
|
|
||||||
|
constexpr int32_t cell_size = (8 * 8) / 2; // N0CHCN__16_COLOR (4-bit)
|
||||||
|
constexpr int32_t character_size = cell_size * (1 * 1); // N0CHSZ__1x1_CELL
|
||||||
|
constexpr int32_t page_width = 64;
|
||||||
|
|
||||||
|
static int plane_ix = 0;
|
||||||
|
|
||||||
|
inline void
|
||||||
|
set_char(int32_t x, int32_t y, uint8_t palette, uint8_t c)
|
||||||
|
{
|
||||||
|
const auto ix = (plane_offset(plane_a + plane_ix) / 2) + (y * page_width) + x;
|
||||||
|
vdp2.vram.u16[ix] =
|
||||||
|
PATTERN_NAME_TABLE_1WORD__PALETTE(palette)
|
||||||
|
| PATTERN_NAME_TABLE_1WORD__CHARACTER((c - 0x20));
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
for (int row = 0; row < buffer.window.cell_height; row++) {
|
||||||
|
const buffer_type::line_type * l = buffer.lines[buffer.window.top + row];
|
||||||
|
|
||||||
|
for (int col = 0; col < buffer.window.cell_width; col++) {
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
c = ' ';
|
||||||
|
if (l != nullptr && (buffer.window.left + col) < l->length)
|
||||||
|
c = l->buf[buffer.window.left + col];
|
||||||
|
|
||||||
|
set_char(col, row, 0, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor::cursor& cur = buffer.cursor;
|
||||||
|
const buffer_type::line_type * l = buffer.lines[cur.row];
|
||||||
|
uint8_t c = (l != nullptr && cur.col < l->length) ? l->buf[cur.col] : ' ';
|
||||||
|
set_char(cur.col - buffer.window.left, cur.row - buffer.window.top, 1, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void v_blank_in_int(void) __attribute__ ((interrupt_handler));
|
||||||
|
void v_blank_in_int()
|
||||||
|
{
|
||||||
|
scu.reg.IST &= ~(IST__V_BLANK_IN);
|
||||||
|
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
|
||||||
|
|
||||||
|
// flip planes;
|
||||||
|
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a + plane_ix);
|
||||||
|
plane_ix = !plane_ix;
|
||||||
|
|
||||||
|
// wait at least 300us, as specified in the SMPC manual.
|
||||||
|
// It appears reading FRC.H is mandatory and *must* occur before FRC.L on real
|
||||||
|
// hardware.
|
||||||
|
while ((sh2.reg.FTCSR & FTCSR__OVF) == 0 && sh2.reg.FRC.H == 0 && sh2.reg.FRC.L < 63);
|
||||||
|
|
||||||
|
if ((vdp2.reg.TVSTAT & TVSTAT__VBLANK) != 0) {
|
||||||
|
// on real hardware, SF contains uninitialized garbage bits other than the
|
||||||
|
// lsb.
|
||||||
|
while ((smpc.reg.SF & 1) != 0);
|
||||||
|
|
||||||
|
smpc.reg.SF = 0;
|
||||||
|
|
||||||
|
smpc.reg.ireg[0] = INTBACK__IREG0__STATUS_DISABLE;
|
||||||
|
smpc.reg.ireg[1] = ( INTBACK__IREG1__PERIPHERAL_DATA_ENABLE
|
||||||
|
| INTBACK__IREG1__PORT2_15BYTE
|
||||||
|
| INTBACK__IREG1__PORT1_15BYTE
|
||||||
|
);
|
||||||
|
smpc.reg.ireg[2] = INTBACK__IREG2__MAGIC;
|
||||||
|
|
||||||
|
smpc.reg.COMREG = COMREG__INTBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_blank_in();
|
||||||
|
|
||||||
|
// DISP: Please make sure to change this bit from 0 to 1 during V blank.
|
||||||
|
vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE
|
||||||
|
| TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320);
|
||||||
|
|
||||||
|
/* set the color mode to 5bits per channel, 1024 colors */
|
||||||
|
vdp2.reg.RAMCTL = RAMCTL__CRMD__RGB_5BIT_1024;
|
||||||
|
|
||||||
|
/* enable display of NBG0 */
|
||||||
|
vdp2.reg.BGON = BGON__N0ON;
|
||||||
|
|
||||||
|
/* set character format for NBG0 to palettized 16 color
|
||||||
|
set enable "cell format" for NBG0
|
||||||
|
set character size for NBG0 to 1x1 cell */
|
||||||
|
vdp2.reg.CHCTLA = CHCTLA__N0CHCN__16_COLOR
|
||||||
|
| CHCTLA__N0BMEN__CELL_FORMAT
|
||||||
|
| CHCTLA__N0CHSZ__1x1_CELL;
|
||||||
|
/* "Note: In color RAM modes 0 and 2, 2048-color becomes 1024-color" */
|
||||||
|
|
||||||
|
/* use 1-word (16-bit) pattern names */
|
||||||
|
vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD;
|
||||||
|
|
||||||
|
/* plane size */
|
||||||
|
vdp2.reg.PLSZ = PLSZ__N0PLSZ__1x1;
|
||||||
|
|
||||||
|
/* map plane offset
|
||||||
|
1-word: value of bit 6-0 * 0x2000
|
||||||
|
2-word: value of bit 5-0 * 0x4000
|
||||||
|
*/
|
||||||
|
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
|
||||||
|
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0
|
||||||
|
vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0
|
||||||
|
|
||||||
|
// zeroize character/cell data from 0 up to plane_a_offset
|
||||||
|
fill<uint32_t>(&vdp2.vram.u32[(0 / 4)], 0, plane_offset(plane_a));
|
||||||
|
|
||||||
|
// zeroize plane_a; `0` is the ascii 0x20 ("space") which doubles as
|
||||||
|
// "transparency" character.
|
||||||
|
fill<uint32_t>(&vdp2.vram.u32[(plane_offset(plane_a) / 4)], 0, plane_size * 2);
|
||||||
|
|
||||||
|
palette_data();
|
||||||
|
cell_data();
|
||||||
|
|
||||||
|
// free-running timer
|
||||||
|
sh2.reg.TCR = TCR__CKS__INTERNAL_DIV128;
|
||||||
|
sh2.reg.FTCSR = 0;
|
||||||
|
|
||||||
|
// initialize smpc
|
||||||
|
smpc.reg.DDR1 = 0; // INPUT
|
||||||
|
smpc.reg.DDR2 = 0; // INPUT
|
||||||
|
smpc.reg.IOSEL = 0; // SMPC control
|
||||||
|
smpc.reg.EXLE = 0; //
|
||||||
|
|
||||||
|
sh2_vec[SCU_VEC__SMPC] = (u32)(&smpc_int);
|
||||||
|
sh2_vec[SCU_VEC__V_BLANK_IN] = (u32)(&v_blank_in_int);
|
||||||
|
|
||||||
|
scu.reg.IST = 0;
|
||||||
|
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
|
||||||
|
}
|
310
editor/test_editor.cpp
Normal file
310
editor/test_editor.cpp
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "editor.hpp"
|
||||||
|
|
||||||
|
using namespace editor;
|
||||||
|
|
||||||
|
static void test_allocate()
|
||||||
|
{
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
decltype(b)::line_type * l;
|
||||||
|
|
||||||
|
assert(b.row[0].length == -1);
|
||||||
|
assert(b.row[1].length == -1);
|
||||||
|
assert(b.row[2].length == -1);
|
||||||
|
assert(b.row[3].length == -1);
|
||||||
|
|
||||||
|
l = b.allocate();
|
||||||
|
assert(b.row[0].length == 0);
|
||||||
|
assert(b.row[1].length == -1);
|
||||||
|
assert(b.row[2].length == -1);
|
||||||
|
assert(b.row[3].length == -1);
|
||||||
|
assert(l == &b.row[0]);
|
||||||
|
|
||||||
|
l = b.allocate();
|
||||||
|
assert(b.row[0].length == 0);
|
||||||
|
assert(b.row[1].length == 0);
|
||||||
|
assert(b.row[2].length == -1);
|
||||||
|
assert(b.row[3].length == -1);
|
||||||
|
assert(l == &b.row[1]);
|
||||||
|
|
||||||
|
l = b.allocate();
|
||||||
|
assert(b.row[0].length == 0);
|
||||||
|
assert(b.row[1].length == 0);
|
||||||
|
assert(b.row[2].length == 0);
|
||||||
|
assert(b.row[3].length == -1);
|
||||||
|
assert(l == &b.row[2]);
|
||||||
|
|
||||||
|
l = b.allocate();
|
||||||
|
assert(b.row[0].length == 0);
|
||||||
|
assert(b.row[1].length == 0);
|
||||||
|
assert(b.row[2].length == 0);
|
||||||
|
assert(b.row[3].length == 0);
|
||||||
|
assert(l == &b.row[3]);
|
||||||
|
|
||||||
|
l = &b.row[1];
|
||||||
|
b.deallocate(l);
|
||||||
|
assert(b.row[0].length == 0);
|
||||||
|
assert(b.row[1].length == -1);
|
||||||
|
assert(b.row[2].length == 0);
|
||||||
|
assert(b.row[3].length == 0);
|
||||||
|
|
||||||
|
l = b.allocate();
|
||||||
|
assert(b.row[0].length == 0);
|
||||||
|
assert(b.row[1].length == 0);
|
||||||
|
assert(b.row[2].length == 0);
|
||||||
|
assert(b.row[3].length == 0);
|
||||||
|
assert(l == &b.row[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_put()
|
||||||
|
{
|
||||||
|
// v
|
||||||
|
// "as" -> "abs"
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
decltype(b)::line_type * l;
|
||||||
|
|
||||||
|
assert(b.cursor.col == 0);
|
||||||
|
assert(b.cursor.row == 0);
|
||||||
|
assert(b.length == 1);
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
l = b.lines[0];
|
||||||
|
assert(l = &b.row[0]);
|
||||||
|
assert(l->length == 1);
|
||||||
|
assert(l->buf[0] == 'a');
|
||||||
|
assert(l->buf[1] == 0x7f);
|
||||||
|
assert(b.cursor.col == 1);
|
||||||
|
assert(b.cursor.row == 0);
|
||||||
|
assert(b.length == 1);
|
||||||
|
|
||||||
|
b.put('b');
|
||||||
|
l = b.lines[0];
|
||||||
|
assert(l->length == 2);
|
||||||
|
assert(l->buf[0] == 'a');
|
||||||
|
assert(l->buf[1] == 'b');
|
||||||
|
assert(l->buf[2] == 0x7f);
|
||||||
|
assert(b.cursor.col == 2);
|
||||||
|
assert(b.cursor.row == 0);
|
||||||
|
assert(b.length == 1);
|
||||||
|
|
||||||
|
b.cursor.col = 1;
|
||||||
|
b.put('c');
|
||||||
|
l = b.lines[0];
|
||||||
|
assert(l->length == 3);
|
||||||
|
assert(l->buf[0] == 'a');
|
||||||
|
assert(l->buf[1] == 'c');
|
||||||
|
assert(l->buf[2] == 'b');
|
||||||
|
assert(l->buf[3] == 0x7f);
|
||||||
|
assert(b.cursor.col == 2);
|
||||||
|
assert(b.cursor.row == 0);
|
||||||
|
assert(b.length == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_backspace()
|
||||||
|
{
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
decltype(b)::line_type * l;
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
l = b.lines[0];
|
||||||
|
assert(l->length == 1);
|
||||||
|
assert(l->buf[0] == 'a');
|
||||||
|
|
||||||
|
assert(b.backspace() == true);
|
||||||
|
assert(l->length == 0);
|
||||||
|
assert(l->buf[0] == 0x7f);
|
||||||
|
|
||||||
|
assert(b.backspace() == false);
|
||||||
|
|
||||||
|
b.put('b');
|
||||||
|
b.put('c');
|
||||||
|
b.put('d');
|
||||||
|
b.put('e');
|
||||||
|
b.cursor.col = 2;
|
||||||
|
assert(l->length == 4);
|
||||||
|
|
||||||
|
//"bcde"
|
||||||
|
assert(b.backspace() == true);
|
||||||
|
assert(l->buf[0] == 'b');
|
||||||
|
assert(l->buf[1] == 'd');
|
||||||
|
assert(l->buf[2] == 'e');
|
||||||
|
assert(l->buf[3] == 0x7f);
|
||||||
|
assert(l->length == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_enter()
|
||||||
|
{
|
||||||
|
// [0] asDf
|
||||||
|
// [1] qwer
|
||||||
|
|
||||||
|
// [0] as
|
||||||
|
// [1] Df
|
||||||
|
// [2] qwer
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
|
||||||
|
b.cursor.row = 0;
|
||||||
|
b.cursor.col = 0;
|
||||||
|
b.put('a');
|
||||||
|
b.put('s');
|
||||||
|
b.put('d');
|
||||||
|
b.put('f');
|
||||||
|
|
||||||
|
b.cursor.row = 1;
|
||||||
|
b.cursor.col = 0;
|
||||||
|
b.put('q');
|
||||||
|
b.put('w');
|
||||||
|
b.put('e');
|
||||||
|
b.put('r');
|
||||||
|
|
||||||
|
assert(b.length == 2);
|
||||||
|
assert(b.lines[0]->buf[0] == 'a');
|
||||||
|
assert(b.lines[0]->buf[3] == 'f');
|
||||||
|
assert(b.lines[1]->buf[0] == 'q');
|
||||||
|
assert(b.lines[1]->buf[3] == 'r');
|
||||||
|
|
||||||
|
b.cursor.row = 0;
|
||||||
|
b.cursor.col = 2;
|
||||||
|
b.enter();
|
||||||
|
|
||||||
|
assert(b.length == 3);
|
||||||
|
assert(b.lines[0]->length == 2);
|
||||||
|
assert(b.lines[1]->length == 2);
|
||||||
|
assert(b.lines[2]->length == 4);
|
||||||
|
assert(b.lines[0]->buf[0] == 'a');
|
||||||
|
assert(b.lines[0]->buf[1] == 's');
|
||||||
|
|
||||||
|
assert(b.lines[1]->buf[0] == 'd');
|
||||||
|
assert(b.lines[1]->buf[1] == 'f');
|
||||||
|
|
||||||
|
assert(b.lines[2]->buf[0] == 'q');
|
||||||
|
assert(b.lines[2]->buf[1] == 'w');
|
||||||
|
assert(b.lines[2]->buf[2] == 'e');
|
||||||
|
assert(b.lines[2]->buf[3] == 'r');
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_enter_backspace1()
|
||||||
|
{
|
||||||
|
// abcd
|
||||||
|
|
||||||
|
// ab
|
||||||
|
//
|
||||||
|
// cd
|
||||||
|
|
||||||
|
// abcd
|
||||||
|
|
||||||
|
// ab
|
||||||
|
// cd
|
||||||
|
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.put('b');
|
||||||
|
b.put('c');
|
||||||
|
b.put('d');
|
||||||
|
b.cursor_left();
|
||||||
|
b.cursor_left();
|
||||||
|
b.enter();
|
||||||
|
b.enter();
|
||||||
|
assert(b.length == 3);
|
||||||
|
b.backspace();
|
||||||
|
b.backspace();
|
||||||
|
assert(b.length == 1);
|
||||||
|
b.enter();
|
||||||
|
assert(b.length == 2);
|
||||||
|
|
||||||
|
assert(b.lines[0]->buf[0] == 'a');
|
||||||
|
assert(b.lines[0]->buf[1] == 'b');
|
||||||
|
assert(b.lines[1]->buf[0] == 'c');
|
||||||
|
assert(b.lines[1]->buf[1] == 'd');
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_enter_backspace2()
|
||||||
|
{
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.enter();
|
||||||
|
assert(b.cursor.row == 1);
|
||||||
|
assert(b.cursor.col == 0);
|
||||||
|
assert(b.length == 2);
|
||||||
|
b.backspace();
|
||||||
|
assert(b.cursor.row == 0);
|
||||||
|
assert(b.cursor.col == 1);
|
||||||
|
assert(b.length == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_enter_scroll()
|
||||||
|
{
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
|
||||||
|
assert(b.window.top == 0);
|
||||||
|
b.put('a');
|
||||||
|
assert(b.window.top == 0);
|
||||||
|
b.enter();
|
||||||
|
assert(b.window.top == 0);
|
||||||
|
b.put('b');
|
||||||
|
assert(b.window.top == 0);
|
||||||
|
b.enter();
|
||||||
|
assert(b.cursor.row == 2);
|
||||||
|
assert(b.window.top == 1);
|
||||||
|
b.put('c');
|
||||||
|
assert(b.window.top == 1);
|
||||||
|
b.enter();
|
||||||
|
assert(b.window.top == 2);
|
||||||
|
b.put('d');
|
||||||
|
assert(b.window.top == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_first_enter()
|
||||||
|
{
|
||||||
|
buffer<8, 4> b {4, 2};
|
||||||
|
|
||||||
|
b.enter();
|
||||||
|
assert(b.length == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_enter_backspace3()
|
||||||
|
{
|
||||||
|
// a
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// b
|
||||||
|
|
||||||
|
buffer<8, 8> b {4, 2};
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.enter();
|
||||||
|
b.enter();
|
||||||
|
b.enter();
|
||||||
|
b.put('b');
|
||||||
|
b.cursor_up();
|
||||||
|
assert(b.lines[0]->buf[0] == 'a');
|
||||||
|
assert(b.lines[1]->length == 0);
|
||||||
|
assert(b.lines[2]->length == 0);
|
||||||
|
assert(b.lines[3]->buf[0] == 'b');
|
||||||
|
assert(b.lines[4] == nullptr);
|
||||||
|
assert(b.length == 4);
|
||||||
|
|
||||||
|
b.backspace();
|
||||||
|
assert(b.length == 3);
|
||||||
|
assert(b.lines[0]->buf[0] == 'a');
|
||||||
|
assert(b.lines[1]->length == 0);
|
||||||
|
assert(b.lines[2]->buf[0] == 'b');
|
||||||
|
assert(b.lines[3] == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test_allocate();
|
||||||
|
test_put();
|
||||||
|
test_backspace();
|
||||||
|
test_enter_backspace1();
|
||||||
|
test_enter_backspace2();
|
||||||
|
test_enter_backspace3();
|
||||||
|
test_enter_scroll();
|
||||||
|
test_first_enter();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -243,7 +243,7 @@ void smpc_int(void) {
|
|||||||
print_hex(str_num, 2, keysym);
|
print_hex(str_num, 2, keysym);
|
||||||
|
|
||||||
enum keysym k = scancode_to_keysym(keysym);
|
enum keysym k = scancode_to_keysym(keysym);
|
||||||
char16_t c = keysym_to_char16(k);
|
int32_t c = keysym_to_char(k, false);
|
||||||
if (k != keysym::UNKNOWN && c != -1) {
|
if (k != keysym::UNKNOWN && c != -1) {
|
||||||
text[0] = c;
|
text[0] = c;
|
||||||
x += draw_font::horizontal_string(font_state,
|
x += draw_font::horizontal_string(font_state,
|
||||||
|
@ -2,7 +2,7 @@ CFLAGS = -Og -Wall -Wextra -Werror -ggdb -Wno-error=unused-parameter -Wno-error=
|
|||||||
CFLAGS += $(shell pkg-config --cflags freetype2)
|
CFLAGS += $(shell pkg-config --cflags freetype2)
|
||||||
LDFLAGS = $(shell pkg-config --libs freetype2)
|
LDFLAGS = $(shell pkg-config --libs freetype2)
|
||||||
|
|
||||||
all: ttf-convert
|
all: ttf-convert ttf-bitmap
|
||||||
|
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
$(CXX) $(CFLAGS) -c $< -o $@
|
$(CXX) $(CFLAGS) -c $< -o $@
|
||||||
@ -11,7 +11,7 @@ all: ttf-convert
|
|||||||
$(CXX) $(LDFLAGS) $< -o $@
|
$(CXX) $(LDFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o ttf-convert
|
rm -f *.o ttf-convert ttf-bitmap
|
||||||
|
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
.INTERMEDIATE:
|
.INTERMEDIATE:
|
||||||
|
130
tools/ttf-bitmap.cpp
Normal file
130
tools/ttf-bitmap.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
load_bitmap_char(FT_Face face,
|
||||||
|
FT_ULong char_code,
|
||||||
|
uint8_t * buf)
|
||||||
|
{
|
||||||
|
FT_Error error;
|
||||||
|
FT_UInt glyph_index = FT_Get_Char_Index(face, char_code);
|
||||||
|
|
||||||
|
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Load_Glyph " << FT_Error_String(error) << '\n';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(face->glyph->format == FT_GLYPH_FORMAT_BITMAP);
|
||||||
|
//printf("num_grays %d\n", face->glyph->bitmap.num_grays);
|
||||||
|
//printf("pitch %d\n", face->glyph->bitmap.pitch);
|
||||||
|
//printf("width %d\n", face->glyph->bitmap.width);
|
||||||
|
assert(face->glyph->bitmap.width == 8);
|
||||||
|
//printf("char_code %lx rows %d\n", char_code, face->glyph->bitmap.rows);
|
||||||
|
assert((face->glyph->bitmap.rows % 8) == 0);
|
||||||
|
assert(face->glyph->bitmap.width / face->glyph->bitmap.pitch == 8);
|
||||||
|
|
||||||
|
for (int y = 0; y < (int)face->glyph->bitmap.rows; y++) {
|
||||||
|
uint8_t * row = &face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch];
|
||||||
|
uint8_t row_out = 0;
|
||||||
|
for (unsigned int x = 0; x < face->glyph->bitmap.width; x++) {
|
||||||
|
int bit;
|
||||||
|
if (x < face->glyph->bitmap.width) {
|
||||||
|
bit = (row[x / 8] >> (7 - (x % 8))) & 1;
|
||||||
|
} else {
|
||||||
|
bit = 0;
|
||||||
|
}
|
||||||
|
//std::cerr << (bit ? "█" : " ");
|
||||||
|
row_out |= (bit << (7 - (x % 8)));
|
||||||
|
}
|
||||||
|
//std::cerr << '\n';
|
||||||
|
buf[y] = row_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'pitch' is bytes; 'width' is pixels
|
||||||
|
return face->glyph->bitmap.rows * face->glyph->bitmap.pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline T
|
||||||
|
parse_num(decltype(std::hex)& format, const char * s)
|
||||||
|
{
|
||||||
|
T n;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << format << s;
|
||||||
|
ss >> n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
FT_Library library;
|
||||||
|
FT_Face face;
|
||||||
|
FT_Error error;
|
||||||
|
|
||||||
|
if (argc != 5) {
|
||||||
|
std::cerr << "usage: " << argv[0] << " [start-hex] [end-hex] [font-file-path] [output-file-path]\n\n";
|
||||||
|
std::cerr << " ex: " << argv[0] << " 3000 30ff ipagp.ttf font.bin\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto start = parse_num<uint32_t>(std::hex, argv[1]);
|
||||||
|
auto end = parse_num<uint32_t>(std::hex, argv[2]);
|
||||||
|
auto font_file_path = argv[3];
|
||||||
|
auto output_file_path = argv[4];
|
||||||
|
|
||||||
|
error = FT_Init_FreeType(&library);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Init_FreeType\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = FT_New_Face(library, font_file_path, 0, &face);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_New_Face\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = FT_Select_Size(face, 0);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Select_Size: " << FT_Error_String(error) << ' ' << error << '\n';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(end > start);
|
||||||
|
|
||||||
|
uint32_t pixels_per_glyph = (face->size->metrics.height * face->size->metrics.max_advance);
|
||||||
|
assert(pixels_per_glyph % 8 == 0);
|
||||||
|
uint32_t bytes_per_glyph = pixels_per_glyph / 8;
|
||||||
|
uint32_t num_glyphs = (end - start) + 1;
|
||||||
|
uint8_t buf[bytes_per_glyph * num_glyphs];
|
||||||
|
|
||||||
|
uint32_t bitmap_offset = 0;
|
||||||
|
for (uint32_t char_code = start; char_code <= end; char_code++) {
|
||||||
|
int32_t bitmap_size = load_bitmap_char(face,
|
||||||
|
char_code,
|
||||||
|
&buf[bitmap_offset]);
|
||||||
|
if (bitmap_size < 0) {
|
||||||
|
std::cerr << "load_bitmap_char error\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_offset += bitmap_size;
|
||||||
|
assert(bitmap_offset < (sizeof (buf)));
|
||||||
|
}
|
||||||
|
std::cerr << "bitmap_offset: 0x" << std::dec << bitmap_offset << '\n';
|
||||||
|
|
||||||
|
FILE * out = fopen(output_file_path, "w");
|
||||||
|
if (out == NULL) {
|
||||||
|
perror("fopen(w)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fwrite(reinterpret_cast<void*>(&buf), bitmap_offset, 1, out);
|
||||||
|
fclose(out);
|
||||||
|
}
|
@ -11,47 +11,6 @@
|
|||||||
|
|
||||||
#include "../common/font.hpp"
|
#include "../common/font.hpp"
|
||||||
|
|
||||||
/*
|
|
||||||
int load_bitmap_char(FT_Face face, FT_ULong char_code, uint8_t * buf)
|
|
||||||
{
|
|
||||||
FT_Error error;
|
|
||||||
FT_UInt glyph_index = FT_Get_Char_Index(face, char_code);
|
|
||||||
|
|
||||||
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
|
|
||||||
if (error) {
|
|
||||||
printf("FT_Load_Glyph %s\n", FT_Error_String(error));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(face->glyph->format == FT_GLYPH_FORMAT_BITMAP);
|
|
||||||
printf("num_grays %d\n", face->glyph->bitmap.num_grays);
|
|
||||||
printf("pitch %d\n", face->glyph->bitmap.pitch);
|
|
||||||
printf("width %d\n", face->glyph->bitmap.width);
|
|
||||||
assert(face->glyph->bitmap.width == 16);
|
|
||||||
printf("char_code %lx rows %d\n", char_code, face->glyph->bitmap.rows);
|
|
||||||
assert(face->glyph->bitmap.rows == 8 || (face->glyph->bitmap.rows % 8) == 0);
|
|
||||||
|
|
||||||
for (int y = 0; y < (int)face->glyph->bitmap.rows; y++) {
|
|
||||||
uint8_t * row = &face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch];
|
|
||||||
uint8_t row_out = 0;
|
|
||||||
for (int x = 0; x < face->glyph->bitmap.width; x++) {
|
|
||||||
int bit;
|
|
||||||
if (x < (int)face->glyph->bitmap.width) {
|
|
||||||
bit = (row[x / 8] >> (7 - (x % 8))) & 1;
|
|
||||||
} else {
|
|
||||||
bit = 0;
|
|
||||||
}
|
|
||||||
fprintf(stderr, bit ? "█" : " ");
|
|
||||||
row_out |= (bit << x);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
//buf[y] = row_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
return face->glyph->bitmap.rows * face->glyph->bitmap.pitch;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
int32_t
|
int32_t
|
||||||
load_outline_char(const FT_Face face,
|
load_outline_char(const FT_Face face,
|
||||||
const FT_ULong char_code,
|
const FT_ULong char_code,
|
||||||
@ -180,21 +139,13 @@ int main(int argc, char *argv[])
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
error = FT_Select_Size(face, 0);
|
|
||||||
if (error) {
|
|
||||||
fprintf(stderr, "FT_Select_Size: %s %d\n", FT_Error_String(error), error);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
std::stringstream ss3;
|
std::stringstream ss3;
|
||||||
int font_size;
|
int font_size;
|
||||||
ss3 << std::dec << argv[3];
|
ss3 << std::dec << argv[3];
|
||||||
ss3 >> font_size;
|
ss3 >> font_size;
|
||||||
std::cerr << "font_size: " << font_size << '\n';
|
std::cerr << "font_size: " << font_size << '\n';
|
||||||
|
|
||||||
error = FT_Set_Pixel_Sizes (face, 0, font_size);
|
error = FT_Set_Pixel_Sizes(face, 0, font_size);
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cerr << "FT_Set_Pixel_Sizes: " << FT_Error_String(error) << error << '\n';
|
std::cerr << "FT_Set_Pixel_Sizes: " << FT_Error_String(error) << error << '\n';
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "../common/draw_font.hpp"
|
#include "../common/draw_font.hpp"
|
||||||
#include "../common/palette.hpp"
|
#include "../common/palette.hpp"
|
||||||
#include "../common/vdp2_func.hpp"
|
#include "../common/vdp2_func.hpp"
|
||||||
|
#include "../common/intback.hpp"
|
||||||
|
|
||||||
#include "wordle.hpp"
|
#include "wordle.hpp"
|
||||||
#include "draw.hpp"
|
#include "draw.hpp"
|
||||||
@ -25,28 +26,6 @@ static struct draw_state draw_state;
|
|||||||
|
|
||||||
static struct wordle::screen wordle_state;
|
static struct wordle::screen wordle_state;
|
||||||
|
|
||||||
#define assert(n) if ((n) == 0) while (1);
|
|
||||||
|
|
||||||
enum intback_fsm {
|
|
||||||
PORT_STATUS = 0,
|
|
||||||
PERIPHERAL_ID,
|
|
||||||
DATA1,
|
|
||||||
DATA2,
|
|
||||||
DATA3,
|
|
||||||
DATA4,
|
|
||||||
FSM_NEXT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct intback_state {
|
|
||||||
int fsm;
|
|
||||||
int controller_ix;
|
|
||||||
int port_ix;
|
|
||||||
};
|
|
||||||
|
|
||||||
static intback_state intback;
|
|
||||||
static int oreg_ix;
|
|
||||||
|
|
||||||
|
|
||||||
struct xorshift32_state {
|
struct xorshift32_state {
|
||||||
uint32_t a;
|
uint32_t a;
|
||||||
};
|
};
|
||||||
@ -63,65 +42,15 @@ uint32_t xorshift32(struct xorshift32_state *state)
|
|||||||
static xorshift32_state random_state = { 0x12345678 };
|
static xorshift32_state random_state = { 0x12345678 };
|
||||||
static uint32_t frame_count = 0;
|
static uint32_t frame_count = 0;
|
||||||
|
|
||||||
void smpc_int(void) __attribute__ ((interrupt_handler));
|
void keyboard_callback(const enum keysym k, uint8_t kbd_bits)
|
||||||
void smpc_int(void) {
|
{
|
||||||
scu.reg.IST &= ~(IST__SMPC);
|
if (!KEYBOARD__MAKE(kbd_bits))
|
||||||
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
|
return;
|
||||||
|
|
||||||
if ((smpc.reg.SR & SR__PDL) != 0) {
|
int32_t c = keysym_to_char(k, false);
|
||||||
// PDL == 1; 1st peripheral data
|
if (k == keysym::UNKNOWN)
|
||||||
oreg_ix = 0;
|
return;
|
||||||
intback.controller_ix = 0;
|
|
||||||
intback.port_ix = 0;
|
|
||||||
intback.fsm = PORT_STATUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int port_connected = 0;
|
|
||||||
int data_size = 0;
|
|
||||||
int peripheral_type = 0;
|
|
||||||
(void)peripheral_type;
|
|
||||||
int kbd_bits = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
This intback handling is oversimplified:
|
|
||||||
|
|
||||||
- up to 2 controllers may be connected
|
|
||||||
- multitaps are not parsed correctly
|
|
||||||
*/
|
|
||||||
|
|
||||||
while (oreg_ix < 32) {
|
|
||||||
reg8 const& oreg = smpc.reg.oreg[oreg_ix++];
|
|
||||||
switch (intback.fsm) {
|
|
||||||
case PORT_STATUS:
|
|
||||||
port_connected = (PORT_STATUS__CONNECTORS(oreg) == 1);
|
|
||||||
if (port_connected) {
|
|
||||||
assert(PORT_STATUS__MULTITAP_ID(oreg) == 0xf);
|
|
||||||
} else {
|
|
||||||
intback.fsm = FSM_NEXT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PERIPHERAL_ID:
|
|
||||||
peripheral_type = PERIPHERAL_ID__TYPE(oreg);
|
|
||||||
data_size = PERIPHERAL_ID__DATA_SIZE(oreg);
|
|
||||||
break;
|
|
||||||
case DATA1:
|
|
||||||
{
|
|
||||||
//controller_state& c = intback.controller[intback.controller_ix];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DATA2:
|
|
||||||
break;
|
|
||||||
case DATA3:
|
|
||||||
kbd_bits = oreg & 0b1111;
|
|
||||||
break;
|
|
||||||
case DATA4:
|
|
||||||
{
|
|
||||||
uint32_t keysym = oreg;
|
|
||||||
|
|
||||||
if (kbd_bits & 0b1000) { // Make
|
|
||||||
enum keysym k = scancode_to_keysym(keysym);
|
|
||||||
char16_t c = keysym_to_char16(k);
|
|
||||||
if (k != keysym::UNKNOWN) {
|
|
||||||
if (c >= 'a' && c <= 'z') {
|
if (c >= 'a' && c <= 'z') {
|
||||||
// uppercase
|
// uppercase
|
||||||
wordle::type_letter(wordle_state, c);
|
wordle::type_letter(wordle_state, c);
|
||||||
@ -134,36 +63,15 @@ void smpc_int(void) {
|
|||||||
uint32_t rand = xorshift32(&random_state);
|
uint32_t rand = xorshift32(&random_state);
|
||||||
wordle::init_screen(wordle_state, rand);
|
wordle::init_screen(wordle_state, rand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (kbd_bits & 0b0001) { // Break
|
void smpc_int(void) __attribute__ ((interrupt_handler));
|
||||||
|
void smpc_int(void)
|
||||||
|
{
|
||||||
|
scu.reg.IST &= ~(IST__SMPC);
|
||||||
|
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
|
||||||
|
|
||||||
}
|
intback::keyboard_fsm(keyboard_callback);
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((intback.fsm >= PERIPHERAL_ID && data_size <= 0) || intback.fsm == FSM_NEXT) {
|
|
||||||
if (intback.port_ix == 1)
|
|
||||||
break;
|
|
||||||
else {
|
|
||||||
intback.port_ix++;
|
|
||||||
intback.controller_ix++;
|
|
||||||
intback.fsm = PORT_STATUS;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
intback.fsm++;
|
|
||||||
data_size--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((smpc.reg.SR & SR__NPE) != 0) {
|
|
||||||
smpc.reg.ireg[0] = INTBACK__IREG0__CONTINUE;
|
|
||||||
} else {
|
|
||||||
smpc.reg.ireg[0] = INTBACK__IREG0__BREAK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
|
Loading…
x
Reference in New Issue
Block a user