-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathpetscii.c
More file actions
132 lines (124 loc) · 6.01 KB
/
petscii.c
File metadata and controls
132 lines (124 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
* PETSCII code point → UTF-8 for terminal display (-petscii mode).
* Faithful Unicode mapping (Kreative Korp / C64 style): £ ↑ ←, box-drawing,
* block elements, card suits ♠♥♦♣, π, and Symbols for Legacy Computing where applicable.
*/
#include "petscii.h"
/* C64 has two ROM character sets:
* - uppercase/graphics (default)
* - lowercase/uppercase (shifted)
*
* When lowercase charset is enabled we render:
* - 0x41-0x5A as 'a'-'z'
* - 0x61-0x7A as 'A'-'Z'
* Everything else uses the faithful Unicode mapping table.
*/
static int petscii_lowercase_charset = 0;
static const char *petscii_to_utf8[256] = {
/* 0x00 - 0x1F: control (output suppressed in basic.c; table fallback) */
" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "\n", " ", " ", "\r", " ", " ",
" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ",
/* 0x20 - 0x2F */
" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
/* 0x30 - 0x3F */
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
/* 0x40 - 0x5F: @ A-Z [ £ ] ↑ ← */
"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\u00A3", "]", "\u2191", "\u2190",
/* 0x60 - 0x7F: graphics (horizontal, spade, block elements, box-drawing, card suits, π, etc.) */
"\u2500", "\u2660", "\U0001FB72", "\U0001FB78", "\U0001FB77", "\U0001FB76", "\U0001FB7A", "\U0001FB71",
"\U0001FB74", "\u256E", "\u2570", "\u256F", "\U0001FB7C", "\u2572", "\u2571", "\U0001FB7D",
"\U0001FB7E", "\u2022", "\U0001FB7B", "\u2665", "\U0001FB70", "\u256D", "\u2573", "\u25CB",
"\u2663", "\U0001FB75", "\u2666", "\u253C", "\U0001FB8C", "\u2502", "\u03C0", "\u25E5",
/* 0x80 - 0x9F: control (output suppressed in basic.c) */
" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ",
" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ",
/* 0xA0 - 0xBF: block elements, box-drawing, quadrants */
"\u00A0", "\u258C", "\u2584", "\u2594", "\u2581", "\u258F", "\u2592", "\u2595",
"\U0001FB8F", "\u25E4", "\U0001FB87", "\u251C", "\u2597", "\u2514", "\u2510", "\u2582",
"\u250C", "\u2534", "\u252C", "\u2524", "\u258E", "\u258D", "\U0001FB88", "\U0001FB82",
"\U0001FB83", "\u2583", "\U0001FB7F", "\u2596", "\u259D", "\u2518", "\u2598", "\u259A",
/* 0xC0 - 0xDF: same graphics as 0x60-0x7F (C64 uppercase/graphics) */
"\u2500", "\u2660", "\U0001FB72", "\U0001FB78", "\U0001FB77", "\U0001FB76", "\U0001FB7A", "\U0001FB71",
"\U0001FB74", "\u256E", "\u2570", "\u256F", "\U0001FB7C", "\u2572", "\u2571", "\U0001FB7D",
"\U0001FB7E", "\u2022", "\U0001FB7B", "\u2665", "\U0001FB70", "\u256D", "\u2573", "\u25CB",
"\u2663", "\U0001FB75", "\u2666", "\u253C", "\U0001FB8C", "\u2502", "\u03C0", "\u25E5",
/* 0xE0 - 0xFF: same block elements as 0xA0-0xBF */
"\u00A0", "\u258C", "\u2584", "\u2594", "\u2581", "\u258F", "\u2592", "\u2595",
"\U0001FB8F", "\u25E4", "\U0001FB87", "\u251C", "\u2597", "\u2514", "\u2510", "\u2582",
"\u250C", "\u2534", "\u252C", "\u2524", "\u258E", "\u258D", "\U0001FB88", "\U0001FB82",
"\U0001FB83", "\u2583", "\U0001FB7F", "\u2596", "\u259D", "\u2518", "\u2598", "\u259A"
};
const char *petscii_code_to_utf8(unsigned char c)
{
if (petscii_lowercase_charset) {
if (c >= 0x41 && c <= 0x5A) {
static char buf[2];
buf[0] = (char)('a' + (c - 0x41));
buf[1] = '\0';
return buf;
}
if (c >= 0x61 && c <= 0x7A) {
static char buf[2];
buf[0] = (char)('A' + (c - 0x61));
buf[1] = '\0';
return buf;
}
}
return petscii_to_utf8[c];
}
void petscii_set_lowercase(int enabled)
{
petscii_lowercase_charset = enabled ? 1 : 0;
}
int petscii_get_lowercase(void)
{
return petscii_lowercase_charset;
}
/* C64 PETSCII (print/CHR$ stream) to screen code (POKE/screen RAM) conversion.
* Ref: sta.c64.org/cbm64pettoscr.html
* When charset is lowercase, letters use C64 layout: 1-26=a-z, 65-90=A-Z. */
unsigned char petscii_to_screencode(unsigned char p)
{
if (petscii_lowercase_charset) {
if (p >= 0x41 && p <= 0x5A) return (unsigned char)(p - 0x40); /* A-Z → 1-26 (a-z) */
if (p >= 0x61 && p <= 0x7A) return (unsigned char)(p - 0x20); /* a-z → 65-90 (A-Z) */
}
if (p <= 31) return (unsigned char)(p + 128);
if (p <= 63) return p;
if (p <= 95) return (unsigned char)(p - 64);
if (p <= 127) return (unsigned char)(p - 32);
if (p <= 159) return (unsigned char)(p + 64);
if (p <= 191) return (unsigned char)(p - 64);
if (p <= 223) return (unsigned char)(p - 128);
if (p <= 254) return (unsigned char)(p - 128);
return 94; /* 255 (π) → 94 */
}
/* ASCII-to-screencode mapper for DRAWTEXT and friends that should
* render what the program actually typed — a literal "Press" stays
* "Press" regardless of which charset the user swapped to via
* `#OPTION lowercase` or CHR$(14) / CHR$(142).
*
* Difference from petscii_to_screencode: ASCII uppercase always
* lands on the UPPER glyph slot, ASCII lowercase always on the
* LOWER glyph slot. In the upper/graphics ROM there's only one
* case so lowercase silently folds to upper (the only sensible
* result with that charset). In the lower/upper ROM upper maps to
* 0x41+ (A-Z positions) and lower maps to 0x01+ (a-z positions).
*
* Non-letter bytes fall through petscii_to_screencode so punctuation,
* digits, and graphic CHR$ codes still render correctly. */
unsigned char petscii_ascii_to_screencode(unsigned char c)
{
if (c >= 'A' && c <= 'Z') {
return petscii_lowercase_charset
? (unsigned char)(0x41 + (c - 'A'))
: (unsigned char)(0x01 + (c - 'A'));
}
if (c >= 'a' && c <= 'z') {
return petscii_lowercase_charset
? (unsigned char)(0x01 + (c - 'a'))
: (unsigned char)(0x01 + (c - 'a'));
}
return petscii_to_screencode(c);
}