Calculator
Source: src/calculator.c · 527 lines · 34 functions.
Screen captures
Views of this part running on the Mini Arcade.

Module entry points
These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.
How to read these diagrams
Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.
These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.
Functions in this file
isDigitKey() · source line 62
static bool isDigitKey(char key)
- Exit this function
return (key >= '0' && key <= '9') || (key >= 'A' && key <= 'F');
Read original function source
static bool isDigitKey(char key)
{
return (key >= '0' && key <= '9') || (key >= 'A' && key <= 'F');
}digitValue() · source line 67
static uint8_t digitValue(char key)
- Exit this function
return key >= 'A' ? (uint8_t)(key - 'A' + 10) : (uint8_t)(key - '0');
Read original function source
static uint8_t digitValue(char key)
{
return key >= 'A' ? (uint8_t)(key - 'A' + 10) : (uint8_t)(key - '0');
}inputBase() · source line 72
static uint8_t inputBase(void)
- Exit this function
return ascii_mode ? 16U : base;
Read original function source
static uint8_t inputBase(void)
{
return ascii_mode ? 16U : base;
}keyIsAvailable() · source line 77
static bool keyIsAvailable(const calculator_key_t *key)
Calls into this documentation:
- Decision
key->type == KEY_NONETrue- Exit this function
return false;
FalseContinue after this decision.
- Exit this function
return key->type != KEY_DIGIT || digitValue(key->value) < inputBase();
Read original function source
static bool keyIsAvailable(const calculator_key_t *key)
{
if (key->type == KEY_NONE) return false;
return key->type != KEY_DIGIT || digitValue(key->value) < inputBase();
}logicLabel() · source line 83
static const char *logicLabel(void)
- Decision
logic_operator == '&'True- Exit this function
return "AND";
FalseContinue after this decision.
- Exit this function
return logic_operator == '|' ? "OR" : "XOR";
Read original function source
static const char *logicLabel(void)
{
if (logic_operator == '&') return "AND";
return logic_operator == '|' ? "OR" : "XOR";
}operatorLabel() · source line 89
static const char *operatorLabel(char operation)
- Select a case
operationEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case '+': - Exit this function
return "ADD"; - Case / label
case '-': - Exit this function
return "SUB"; - Case / label
case '*': - Exit this function
return "MUL"; - Case / label
case '/': - Exit this function
return "DIV"; - Case / label
case '%': - Exit this function
return "MOD"; - Case / label
case 'P': - Exit this function
return "POWER"; - Case / label
case '&': - Exit this function
return "AND"; - Case / label
case '|': - Exit this function
return "OR"; - Case / label
case '^': - Exit this function
return "XOR"; - Case / label
default: - Exit this function
return "";
Read original function source
static const char *operatorLabel(char operation)
{
switch (operation) {
case '+': return "ADD";
case '-': return "SUB";
case '*': return "MUL";
case '/': return "DIV";
case '%': return "MOD";
case 'P': return "POWER";
case '&': return "AND";
case '|': return "OR";
case '^': return "XOR";
default: return "";
}
}clearCalculator() · source line 105
static void clearCalculator(void)
- Execute
entry_active = false; - Execute
has_accumulator = false; - Execute
entry_value = 0U; - Execute
accumulator = 0U; - Execute
pending_operator = '\0'; - Execute
error_text = NULL;
Read original function source
static void clearCalculator(void)
{
entry_active = false;
has_accumulator = false;
entry_value = 0U;
accumulator = 0U;
pending_operator = '\0';
error_text = NULL;
}displayedValue() · source line 115
static uint64_t displayedValue(void)
- Exit this function
return entry_active ? entry_value : (has_accumulator ? accumulator : 0U);
Read original function source
static uint64_t displayedValue(void)
{
return entry_active ? entry_value : (has_accumulator ? accumulator : 0U);
}formatValue() · source line 120
static void formatValue(uint64_t value, char *text, size_t text_size)
Calls into this documentation:
- Execute
static const char digits[] = "0123456789ABCDEF"; - Execute
char reversed[65]; - Execute
size_t length = 0U; - DO / WHILE loop
value != 0U && length < sizeof(reversed)Run body, then test condition; repeat while true.
- Execute
reversed[length++] = digits[value % inputBase()]; - Execute
value /= inputBase();
- Decision
text_size <= lengthTrue- Exit this function
return;
FalseContinue after this decision.
- FOR loop
size_t index = 0U; index < length; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
text[index] = reversed[length - index - 1U];
- Execute
text[length] = '\0';
Read original function source
static void formatValue(uint64_t value, char *text, size_t text_size)
{
static const char digits[] = "0123456789ABCDEF";
char reversed[65];
size_t length = 0U;
do {
reversed[length++] = digits[value % inputBase()];
value /= inputBase();
} while (value != 0U && length < sizeof(reversed));
if (text_size <= length) return;
for (size_t index = 0U; index < length; ++index) {
text[index] = reversed[length - index - 1U];
}
text[length] = '\0';
}multiplyChecked() · source line 138
static bool multiplyChecked(uint64_t left, uint64_t right, uint64_t *result)
- Decision
right != 0U && left > UINT64_MAX / rightTrue- Execute
error_text = "64 BIT OVERFLOW"; - Exit this function
return false;
FalseContinue after this decision.
- Execute
*result = left * right; - Exit this function
return true;
Read original function source
static bool multiplyChecked(uint64_t left, uint64_t right, uint64_t *result)
{
if (right != 0U && left > UINT64_MAX / right) {
error_text = "64 BIT OVERFLOW";
return false;
}
*result = left * right;
return true;
}powerChecked() · source line 148
static bool powerChecked(uint64_t value, uint64_t exponent, uint64_t *result)
Calls into this documentation:
- Execute
uint64_t power = 1U; - Execute
uint64_t factor = value; - WHILE loop
exponent != 0URepeat body while the loop condition permits; then continue below.
- Decision
(exponent & 1U) != 0U && !multiplyChecked(power, factor, &power)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
exponent >>= 1U; - Decision
exponent != 0U && !multiplyChecked(factor, factor, &factor)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
*result = power; - Exit this function
return true;
Read original function source
static bool powerChecked(uint64_t value, uint64_t exponent, uint64_t *result)
{
uint64_t power = 1U;
uint64_t factor = value;
while (exponent != 0U) {
if ((exponent & 1U) != 0U && !multiplyChecked(power, factor, &power)) return false;
exponent >>= 1U;
if (exponent != 0U && !multiplyChecked(factor, factor, &factor)) return false;
}
*result = power;
return true;
}integerSquareRoot() · source line 161
static uint64_t integerSquareRoot(uint64_t value)
- Execute
uint64_t result = 0U; - Execute
uint64_t bit = UINT64_C(1) << 62U; - WHILE loop
bit > valueRepeat body while the loop condition permits; then continue below.
- Execute
bit >>= 2U;
- WHILE loop
bit != 0URepeat body while the loop condition permits; then continue below.
- Decision
value >= result + bitTrue- Execute
value -= result + bit; - Execute
result = (result >> 1U) + bit;
False- Execute
result >>= 1U;
- Execute
bit >>= 2U;
- Exit this function
return result;
Read original function source
static uint64_t integerSquareRoot(uint64_t value)
{
uint64_t result = 0U;
uint64_t bit = UINT64_C(1) << 62U;
while (bit > value) bit >>= 2U;
while (bit != 0U) {
if (value >= result + bit) {
value -= result + bit;
result = (result >> 1U) + bit;
} else {
result >>= 1U;
}
bit >>= 2U;
}
return result;
}applyOperator() · source line 178
static bool applyOperator(char operation, uint64_t left, uint64_t right, uint64_t *result)
Calls into this documentation:
- Select a case
operationEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case '+': - Decision
left > UINT64_MAX - rightTrue- Execute
error_text = "64 BIT OVERFLOW";
False- Execute
*result = left + right;
- Exit enclosing loop or switch
break; - Case / label
case '-': - Decision
left < rightTrue- Execute
error_text = "NEGATIVE RESULT";
False- Execute
*result = left - right;
- Exit enclosing loop or switch
break; - Case / label
case '*': - Exit this function
return multiplyChecked(left, right, result); - Case / label
case '/': - Decision
right == 0UTrue- Execute
error_text = "DIVIDE BY ZERO";
False- Execute
*result = left / right;
- Exit enclosing loop or switch
break; - Case / label
case '%': - Decision
right == 0UTrue- Execute
error_text = "DIVIDE BY ZERO";
False- Execute
*result = left % right;
- Exit enclosing loop or switch
break; - Case / label
case 'P': - Exit this function
return powerChecked(left, right, result); - Case / label
case '&': - Execute
*result = left & right; - Exit enclosing loop or switch
break; - Case / label
case '|': - Execute
*result = left | right; - Exit enclosing loop or switch
break; - Case / label
case '^': - Execute
*result = left ^ right; - Exit enclosing loop or switch
break; - Case / label
default: - Exit this function
return false;
- Exit this function
return error_text == NULL;
Read original function source
static bool applyOperator(char operation, uint64_t left, uint64_t right, uint64_t *result)
{
switch (operation) {
case '+':
if (left > UINT64_MAX - right) error_text = "64 BIT OVERFLOW";
else *result = left + right;
break;
case '-':
if (left < right) error_text = "NEGATIVE RESULT";
else *result = left - right;
break;
case '*': return multiplyChecked(left, right, result);
case '/':
if (right == 0U) error_text = "DIVIDE BY ZERO";
else *result = left / right;
break;
case '%':
if (right == 0U) error_text = "DIVIDE BY ZERO";
else *result = left % right;
break;
case 'P': return powerChecked(left, right, result);
case '&': *result = left & right; break;
case '|': *result = left | right; break;
case '^': *result = left ^ right; break;
default: return false;
}
return error_text == NULL;
}appendDigit() · source line 207
static void appendDigit(uint8_t digit)
Calls into this documentation:
- Decision
error_text != NULLTrue- Execute
clearCalculator();
FalseContinue after this decision.
- Decision
!entry_activeTrue- Execute
entry_value = 0U; - Execute
entry_active = true;
FalseContinue after this decision.
- Decision
entry_value > (UINT64_MAX - digit) / inputBase()True- Execute
error_text = "64 BIT OVERFLOW"; - Exit this function
return;
FalseContinue after this decision.
- Execute
entry_value = entry_value * inputBase() + digit;
Read original function source
static void appendDigit(uint8_t digit)
{
if (error_text != NULL) clearCalculator();
if (!entry_active) {
entry_value = 0U;
entry_active = true;
}
if (entry_value > (UINT64_MAX - digit) / inputBase()) {
error_text = "64 BIT OVERFLOW";
return;
}
entry_value = entry_value * inputBase() + digit;
}selectOperator() · source line 221
static void selectOperator(char operation)
Calls into this documentation:
- Decision
error_text != NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
!has_accumulatorTrue- Execute
accumulator = entry_active ? entry_value : 0U; - Execute
has_accumulator = true;
False- Decision
pending_operator != '\0' && entry_activeTrue- Execute
uint64_t result = 0U; - Decision
!applyOperator(pending_operator, accumulator, entry_value, &result)True- Exit this function
return;
FalseContinue after this decision.
- Execute
accumulator = result;
FalseContinue after this decision.
- Execute
pending_operator = operation; - Execute
entry_active = false;
Read original function source
static void selectOperator(char operation)
{
if (error_text != NULL) return;
if (!has_accumulator) {
accumulator = entry_active ? entry_value : 0U;
has_accumulator = true;
} else if (pending_operator != '\0' && entry_active) {
uint64_t result = 0U;
if (!applyOperator(pending_operator, accumulator, entry_value, &result)) return;
accumulator = result;
}
pending_operator = operation;
entry_active = false;
}calculateResult() · source line 236
static void calculateResult(void)
Calls into this documentation:
- Decision
error_text != NULL || pending_operator == '\0' || !entry_activeTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
uint64_t result = 0U; - Decision
!applyOperator(pending_operator, accumulator, entry_value, &result)True- Exit this function
return;
FalseContinue after this decision.
- Execute
entry_value = result; - Execute
accumulator = result; - Execute
entry_active = true; - Execute
has_accumulator = false; - Execute
pending_operator = '\0';
Read original function source
static void calculateResult(void)
{
if (error_text != NULL || pending_operator == '\0' || !entry_active) return;
uint64_t result = 0U;
if (!applyOperator(pending_operator, accumulator, entry_value, &result)) return;
entry_value = result;
accumulator = result;
entry_active = true;
has_accumulator = false;
pending_operator = '\0';
}calculateRoot() · source line 248
static void calculateRoot(void)
Calls into this documentation:
- Decision
error_text != NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
entry_value = integerSquareRoot(displayedValue()); - Execute
entry_active = true;
Read original function source
static void calculateRoot(void)
{
if (error_text != NULL) return;
entry_value = integerSquareRoot(displayedValue());
entry_active = true;
}moveCursor() · source line 255
static void moveCursor(int row_delta, int column_delta)
Calls into this documentation:
- FOR loop
unsigned tries = 0U; tries < CALCULATOR_ROWS * CALCULATOR_COLUMNS; ++triesRepeat body while the loop condition permits; then continue below.
- Execute
selected_row = (uint8_t)((selected_row + row_delta + CALCULATOR_ROWS) % CALCULATOR_ROWS); - Execute
selected_column = (uint8_t)((selected_column + column_delta + CALCULATOR_COLUMNS) % CALCULATOR_COLUMNS); - Decision
keyIsAvailable(&keypad[selected_row][selected_column])True- Exit this function
return;
FalseContinue after this decision.
Read original function source
static void moveCursor(int row_delta, int column_delta)
{
for (unsigned tries = 0U; tries < CALCULATOR_ROWS * CALCULATOR_COLUMNS; ++tries) {
selected_row = (uint8_t)((selected_row + row_delta + CALCULATOR_ROWS) %
CALCULATOR_ROWS);
selected_column = (uint8_t)((selected_column + column_delta +
CALCULATOR_COLUMNS) % CALCULATOR_COLUMNS);
if (keyIsAvailable(&keypad[selected_row][selected_column])) return;
}
}changeBase() · source line 266
static void changeBase(bool increase)
Calls into this documentation:
- Decision
ascii_modeTrue- Execute
ascii_mode = false; - Execute
base = increase ? CALCULATOR_MIN_BASE : CALCULATOR_MAX_BASE;
False- Decision
increase && base == CALCULATOR_MAX_BASETrue- Execute
ascii_mode = true;
False- Decision
!increase && base == CALCULATOR_MIN_BASETrue- Execute
ascii_mode = true;
False- Execute
base = (uint8_t)(base + (increase ? 1 : -1));
- WHILE loop
!keyIsAvailable(&keypad[selected_row][selected_column])Repeat body while the loop condition permits; then continue below.
- Execute
moveCursor(0, 1);
Read original function source
static void changeBase(bool increase)
{
if (ascii_mode) {
ascii_mode = false;
base = increase ? CALCULATOR_MIN_BASE : CALCULATOR_MAX_BASE;
} else if (increase && base == CALCULATOR_MAX_BASE) {
ascii_mode = true;
} else if (!increase && base == CALCULATOR_MIN_BASE) {
ascii_mode = true;
} else {
base = (uint8_t)(base + (increase ? 1 : -1));
}
while (!keyIsAvailable(&keypad[selected_row][selected_column])) moveCursor(0, 1);
}pressSelectedKey() · source line 281
static void pressSelectedKey(void)
Calls into this documentation:
- Execute
const calculator_key_t *key = &keypad[selected_row][selected_column]; - Decision
!keyIsAvailable(key)True- Exit this function
return;
FalseContinue after this decision.
- Decision
key->type == KEY_DIGITTrue- Execute
appendDigit(digitValue(key->value));
False- Decision
key->type == KEY_OPERATORTrue- Execute
selectOperator(key->value);
False- Decision
key->type == KEY_EQUALSTrue- Execute
calculateResult();
False- Decision
key->type == KEY_ROOTTrue- Execute
calculateRoot();
False- Decision
key->type == KEY_LOGICTrue- Execute
selectOperator(logic_operator);
False- Decision
key->type == KEY_BASE_DOWNTrue- Execute
changeBase(false);
False- Decision
key->type == KEY_BASE_UPTrue- Execute
changeBase(true);
FalseContinue after this decision.
Read original function source
static void pressSelectedKey(void)
{
const calculator_key_t *key = &keypad[selected_row][selected_column];
if (!keyIsAvailable(key)) return;
if (key->type == KEY_DIGIT) appendDigit(digitValue(key->value));
else if (key->type == KEY_OPERATOR) selectOperator(key->value);
else if (key->type == KEY_EQUALS) calculateResult();
else if (key->type == KEY_ROOT) calculateRoot();
else if (key->type == KEY_LOGIC) selectOperator(logic_operator);
else if (key->type == KEY_BASE_DOWN) changeBase(false);
else if (key->type == KEY_BASE_UP) changeBase(true);
}startCalculator() · source line 294
void startCalculator(uint32_t now_ms)
Calls into this documentation:
- Execute
running = true; - Execute
base = 10U; - Execute
ascii_mode = false; - Execute
logic_operator = '&'; - Execute
selected_row = 3U; - Execute
selected_column = 0U; - Execute
last_cursor_move = now_ms - CURSOR_REPEAT_MS; - Execute
clearCalculator();
Read original function source
void startCalculator(uint32_t now_ms)
{
running = true;
base = 10U;
ascii_mode = false;
logic_operator = '&';
selected_row = 3U;
selected_column = 0U;
last_cursor_move = now_ms - CURSOR_REPEAT_MS;
clearCalculator();
}stopCalculator() · source line 306
void stopCalculator(void)
- Execute
running = false;
Read original function source
void stopCalculator(void)
{
running = false;
}calculatorAction() · source line 311
void calculatorAction(bool select, bool backspace, bool clear, bool cycle_logic, uint32_t now_ms)
Calls into this documentation:
- Execute
(void)now_ms; - Decision
!runningTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
clearTrue- Execute
clearCalculator();
False- Decision
backspaceTrue- Decision
error_text != NULLTrue- Execute
clearCalculator();
False- Decision
entry_activeTrue- Execute
entry_value /= inputBase();
FalseContinue after this decision.
False- Decision
cycle_logic && keypad[selected_row][selected_column].type == KEY_LOGICTrue- Execute
logic_operator = logic_operator == '&' ? '|' : (logic_operator == '|' ? '^' : '&');
False- Decision
selectTrue- Execute
pressSelectedKey();
FalseContinue after this decision.
Read original function source
void calculatorAction(bool select, bool backspace, bool clear, bool cycle_logic, uint32_t now_ms)
{
(void)now_ms;
if (!running) return;
if (clear) {
clearCalculator();
} else if (backspace) {
if (error_text != NULL) clearCalculator();
else if (entry_active) entry_value /= inputBase();
} else if (cycle_logic && keypad[selected_row][selected_column].type == KEY_LOGIC) {
logic_operator = logic_operator == '&' ? '|' : (logic_operator == '|' ? '^' : '&');
} else if (select) {
pressSelectedKey();
}
}updateCalculator() · source line 327
void updateCalculator(int x_offset, int y_offset, bool joystick_valid, uint32_t now_ms)
Calls into this documentation:
- Decision
!running || !joystick_valid || now_ms - last_cursor_move < CURSOR_REPEAT_MSTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
x_offset >= JOYSTICK_ENTERTrue- Execute
moveCursor(0, 1);
False- Decision
x_offset <= -JOYSTICK_ENTERTrue- Execute
moveCursor(0, -1);
False- Decision
y_offset >= JOYSTICK_ENTERTrue- Execute
moveCursor(-1, 0);
False- Decision
y_offset <= -JOYSTICK_ENTERTrue- Execute
moveCursor(1, 0);
False- Exit this function
return;
- Execute
last_cursor_move = now_ms;
Read original function source
void updateCalculator(int x_offset, int y_offset, bool joystick_valid, uint32_t now_ms)
{
if (!running || !joystick_valid || now_ms - last_cursor_move < CURSOR_REPEAT_MS) return;
if (x_offset >= JOYSTICK_ENTER) moveCursor(0, 1);
else if (x_offset <= -JOYSTICK_ENTER) moveCursor(0, -1);
else if (y_offset >= JOYSTICK_ENTER) moveCursor(-1, 0);
else if (y_offset <= -JOYSTICK_ENTER) moveCursor(1, 0);
else return;
last_cursor_move = now_ms;
}asciiGlyphSupported() · source line 338
static bool asciiGlyphSupported(uint8_t character)
- Exit this function
return (character >= '0' && character <= '9') || (character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || character == ':' || character == '/' || character == '-' || character == '+' || character == '.' || character == '\'' || character == '?' || character == '!' || character == ' ';
Read original function source
static bool asciiGlyphSupported(uint8_t character)
{
return (character >= '0' && character <= '9') ||
(character >= 'A' && character <= 'Z') ||
(character >= 'a' && character <= 'z') || character == ':' ||
character == '/' || character == '-' || character == '+' ||
character == '.' || character == '\'' || character == '?' ||
character == '!' || character == ' ';
}drawAsciiValue() · source line 348
static void drawAsciiValue(uint16_t *frame)
Calls into this documentation:
- Execute
uint8_t bytes[8]; - Execute
uint64_t value = displayedValue(); - Execute
size_t count = 0U; - DO / WHILE loop
value != 0U && count < sizeof(bytes)Run body, then test condition; repeat while true.
- Execute
bytes[count++] = (uint8_t)value; - Execute
value >>= 8U;
- Execute
const int character_width = UI_FONT_ADVANCE * 2; - Execute
int x = (LCD_WIDTH - (int)count * character_width + 2) / 2; - FOR loop
size_t index = count; index > 0U; --indexRepeat body while the loop condition permits; then continue below.
- Execute
const uint8_t character = bytes[index - 1U]; - Decision
character < 32U || character > 126UTrue- Execute
fill_rect(frame, x, 17, 12, 16, COLOR_WHITE);
False- Execute
char text[2] = {(char)(asciiGlyphSupported(character) ? character : '?'), '\0'}; - Execute
draw_text(frame, x, 17, text, 2, COLOR_WHITE);
- Execute
x += character_width;
Read original function source
static void drawAsciiValue(uint16_t *frame)
{
uint8_t bytes[8];
uint64_t value = displayedValue();
size_t count = 0U;
do {
bytes[count++] = (uint8_t)value;
value >>= 8U;
} while (value != 0U && count < sizeof(bytes));
const int character_width = UI_FONT_ADVANCE * 2;
int x = (LCD_WIDTH - (int)count * character_width + 2) / 2;
for (size_t index = count; index > 0U; --index) {
const uint8_t character = bytes[index - 1U];
if (character < 32U || character > 126U) {
fill_rect(frame, x, 17, 12, 16, COLOR_WHITE);
} else {
char text[2] = {(char)(asciiGlyphSupported(character) ? character : '?'), '\0'};
draw_text(frame, x, 17, text, 2, COLOR_WHITE);
}
x += character_width;
}
}displayCapacity() · source line 372
static size_t displayCapacity(uint8_t radix)
- Execute
static const uint8_t capacities[] = { 0U, 0U, 64U, 41U, 32U, 28U, 25U, 23U, 22U, 21U, 20U, 19U, 18U, 18U, 17U, 17U, 16U, }; - Exit this function
return capacities[radix];
Read original function source
static size_t displayCapacity(uint8_t radix)
{
static const uint8_t capacities[] = {
0U, 0U, 64U, 41U, 32U, 28U, 25U, 23U, 22U, 21U, 20U, 19U, 18U,
18U, 17U, 17U, 16U,
};
return capacities[radix];
}drawValue() · source line 381
static void drawValue(uint16_t *frame)
Calls into this documentation:
- Execute
char value[65]; - Execute
formatValue(displayedValue(), value, sizeof(value)); - Decision
ascii_modeTrue- Execute
drawAsciiValue(frame); - Exit this function
return;
FalseContinue after this decision.
- Execute
const size_t length = strlen(value); - Execute
const size_t capacity = displayCapacity(inputBase()); - Execute
const size_t digits_per_line = capacity > 42U ? 32U : capacity; - Execute
const int scale = capacity <= 20U ? 2 : 1; - Execute
const int advance = UI_FONT_ADVANCE * scale; - Execute
const size_t ghost_count = capacity - length; - FOR loop
size_t position = 0U; position < capacity; ++positionRepeat body while the loop condition permits; then continue below.
- Execute
const size_t line = position / digits_per_line; - Execute
const size_t line_start = line * digits_per_line; - Execute
const size_t line_length = capacity - line_start < digits_per_line ? capacity - line_start : digits_per_line; - Execute
const int x = LCD_WIDTH - (int)line_length * advance + scale + (int)(position - line_start) * advance; - Execute
const int y = capacity > 42U ? 14 + (int)line * 11 : (scale == 2 ? 17 : 22); - Execute
const bool ghost = position < ghost_count; - Execute
char digit[2] = {ghost ? '0' : value[position - ghost_count], '\0'}; - Execute
draw_text(frame, x, y, digit, scale, ghost ? COLOR_DIMGREY : COLOR_WHITE);
Read original function source
static void drawValue(uint16_t *frame)
{
char value[65];
formatValue(displayedValue(), value, sizeof(value));
if (ascii_mode) {
drawAsciiValue(frame);
return;
}
const size_t length = strlen(value);
const size_t capacity = displayCapacity(inputBase());
const size_t digits_per_line = capacity > 42U ? 32U : capacity;
const int scale = capacity <= 20U ? 2 : 1;
const int advance = UI_FONT_ADVANCE * scale;
const size_t ghost_count = capacity - length;
for (size_t position = 0U; position < capacity; ++position) {
const size_t line = position / digits_per_line;
const size_t line_start = line * digits_per_line;
const size_t line_length = capacity - line_start < digits_per_line ?
capacity - line_start : digits_per_line;
const int x = LCD_WIDTH - (int)line_length * advance + scale +
(int)(position - line_start) * advance;
const int y = capacity > 42U ? 14 + (int)line * 11 : (scale == 2 ? 17 : 22);
const bool ghost = position < ghost_count;
char digit[2] = {ghost ? '0' : value[position - ghost_count], '\0'};
draw_text(frame, x, y, digit, scale, ghost ? COLOR_DIMGREY : COLOR_WHITE);
}
}keyX() · source line 411
static int keyX(uint8_t row, uint8_t column)
- Decision
row == 0UTrue- Exit this function
return 56 + (int)column * 34;
FalseContinue after this decision.
- Decision
column < 3UTrue- Exit this function
return 57 + (int)column * 45;
FalseContinue after this decision.
- Decision
column == 3UTrue- Exit this function
return 195;
FalseContinue after this decision.
- Decision
column == 4UTrue- Exit this function
return 250;
FalseContinue after this decision.
- Exit this function
return 6;
Read original function source
static int keyX(uint8_t row, uint8_t column)
{
if (row == 0U) return 56 + (int)column * 34;
if (column < 3U) return 57 + (int)column * 45;
if (column == 3U) return 195;
if (column == 4U) return 250;
return 6;
}keyY() · source line 420
static int keyY(uint8_t row)
- Exit this function
return row == 0U ? 35 : 58 + (int)(row - 1U) * 18;
Read original function source
static int keyY(uint8_t row)
{
return row == 0U ? 35 : 58 + (int)(row - 1U) * 18;
}keyWidth() · source line 425
static int keyWidth(uint8_t row, uint8_t column)
- Decision
row == 0UTrue- Exit this function
return 29;
FalseContinue after this decision.
- Decision
column < 3UTrue- Exit this function
return 40;
FalseContinue after this decision.
- Decision
column == 3UTrue- Exit this function
return 48;
FalseContinue after this decision.
- Decision
column == 4UTrue- Exit this function
return 61;
FalseContinue after this decision.
- Exit this function
return 43;
Read original function source
static int keyWidth(uint8_t row, uint8_t column)
{
if (row == 0U) return 29;
if (column < 3U) return 40;
if (column == 3U) return 48;
if (column == 4U) return 61;
return 43;
}drawBasicOperator() · source line 434
static void drawBasicOperator(uint16_t *frame, int x, int y, int width,
char operation, uint16_t colour)Calls into this documentation:
- Execute
const int centre_x = x + width / 2; - Decision
operation == '+'True- Execute
fill_rect(frame, centre_x - 1, y + 2, 3, 12, colour); - Execute
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
False- Decision
operation == '-'True- Execute
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
False- Decision
operation == '*'True- FOR loop
int thickness = -1; thickness <= 1; ++thicknessRepeat body while the loop condition permits; then continue below.
- Execute
draw_line(frame, centre_x - 6, y + 2 + thickness, centre_x + 6, y + 14 + thickness, colour); - Execute
draw_line(frame, centre_x - 6, y + 14 + thickness, centre_x + 6, y + 2 + thickness, colour);
False- Decision
operation == '/'True- Execute
fill_rect(frame, centre_x - 1, y + 1, 3, 3, colour); - Execute
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour); - Execute
fill_rect(frame, centre_x - 1, y + 12, 3, 3, colour);
False- Execute
fill_rect(frame, centre_x - 8, y + 4, 17, 3, colour); - Execute
fill_rect(frame, centre_x - 8, y + 10, 17, 3, colour);
Read original function source
static void drawBasicOperator(uint16_t *frame, int x, int y, int width,
char operation, uint16_t colour)
{
const int centre_x = x + width / 2;
if (operation == '+') {
fill_rect(frame, centre_x - 1, y + 2, 3, 12, colour);
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
} else if (operation == '-') {
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
} else if (operation == '*') {
for (int thickness = -1; thickness <= 1; ++thickness) {
draw_line(frame, centre_x - 6, y + 2 + thickness, centre_x + 6, y + 14 + thickness,
colour);
draw_line(frame, centre_x - 6, y + 14 + thickness, centre_x + 6, y + 2 + thickness,
colour);
}
} else if (operation == '/') {
fill_rect(frame, centre_x - 1, y + 1, 3, 3, colour);
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
fill_rect(frame, centre_x - 1, y + 12, 3, 3, colour);
} else {
fill_rect(frame, centre_x - 8, y + 4, 17, 3, colour);
fill_rect(frame, centre_x - 8, y + 10, 17, 3, colour);
}
}drawKey() · source line 460
static void drawKey(uint16_t *frame, uint8_t row, uint8_t column)
Calls into this documentation:
- Execute
const calculator_key_t *key = &keypad[row][column]; - Decision
key->type == KEY_NONETrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const int x = keyX(row, column); - Execute
const int y = keyY(row); - Execute
const int width = keyWidth(row, column); - Execute
const bool selected = row == selected_row && column == selected_column; - Execute
const bool digit = key->type == KEY_DIGIT; - Execute
const uint16_t colour = keyIsAvailable(key) ? COLOR_LIGHTGREY : COLOR_DARKGREY; - Decision
selectedTrue- Execute
draw_rect(frame, x, y, width, 16, COLOR_ORANGE);
FalseContinue after this decision.
- Decision
key->type == KEY_LOGICTrue- Execute
draw_text_centred(frame, x + width / 2, y, "LOGIC", 1, selected ? COLOR_ORANGE : colour); - Execute
draw_text_centred(frame, x + width / 2, y + 8, logicLabel(), 1, selected ? COLOR_ORANGE : colour); - Exit this function
return;
FalseContinue after this decision.
- Decision
(key->type == KEY_OPERATOR && (key->value == '+' || key->value == '-' || key->value == '*' || key->value == '/')) || key->type == KEY_EQUALSTrue- Execute
drawBasicOperator(frame, x, y, width, key->type == KEY_EQUALS ? '=' : key->value, selected ? COLOR_ORANGE : colour); - Exit this function
return;
FalseContinue after this decision.
- Execute
char digit_text[2] = {key->value, '\0'}; - Execute
const char *label = digit ? digit_text : (key->type == KEY_OPERATOR ? operatorLabel(key->value) : (key->type == KEY_EQUALS ? "EQ" : (key->type == KEY_ROOT ? "ROOT" : (key->type == KEY_BASE_DOWN ? "BASE-" : "BASE+")))); - Execute
draw_text_centred(frame, x + width / 2, y + (digit ? (row == 0U ? 0 : 1) : 4), label, digit ? 2 : 1, selected ? COLOR_ORANGE : colour);
Read original function source
static void drawKey(uint16_t *frame, uint8_t row, uint8_t column)
{
const calculator_key_t *key = &keypad[row][column];
if (key->type == KEY_NONE) return;
const int x = keyX(row, column);
const int y = keyY(row);
const int width = keyWidth(row, column);
const bool selected = row == selected_row && column == selected_column;
const bool digit = key->type == KEY_DIGIT;
const uint16_t colour = keyIsAvailable(key) ? COLOR_LIGHTGREY : COLOR_DARKGREY;
if (selected) draw_rect(frame, x, y, width, 16, COLOR_ORANGE);
if (key->type == KEY_LOGIC) {
draw_text_centred(frame, x + width / 2, y, "LOGIC", 1, selected ? COLOR_ORANGE : colour);
draw_text_centred(frame, x + width / 2, y + 8, logicLabel(), 1,
selected ? COLOR_ORANGE : colour);
return;
}
if ((key->type == KEY_OPERATOR && (key->value == '+' || key->value == '-' ||
key->value == '*' || key->value == '/')) ||
key->type == KEY_EQUALS) {
drawBasicOperator(frame, x, y, width, key->type == KEY_EQUALS ? '=' : key->value,
selected ? COLOR_ORANGE : colour);
return;
}
char digit_text[2] = {key->value, '\0'};
const char *label = digit ? digit_text :
(key->type == KEY_OPERATOR ? operatorLabel(key->value) :
(key->type == KEY_EQUALS ? "EQ" :
(key->type == KEY_ROOT ? "ROOT" :
(key->type == KEY_BASE_DOWN ? "BASE-" : "BASE+"))));
draw_text_centred(frame, x + width / 2, y + (digit ? (row == 0U ? 0 : 1) : 4), label,
digit ? 2 : 1, selected ? COLOR_ORANGE : colour);
}drawCalculator() · source line 497
void drawCalculator(uint16_t *frame)
Calls into this documentation:
- Decision
!running || frame == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
clear(frame, COLOR_BLACK); - Execute
drawValue(frame); - FOR loop
uint8_t row = 0U; row < CALCULATOR_ROWS; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
uint8_t column = 0U; column < CALCULATOR_COLUMNS; ++columnRepeat body while the loop condition permits; then continue below.
- Execute
drawKey(frame, row, column);
- Execute
draw_text_centred(frame, 27, 78, "BASE", 1, COLOR_CYAN); - Decision
ascii_modeTrue- Execute
draw_text_centred(frame, 27, 87, "ASCII", 1, COLOR_WHITE);
False- Execute
char base_text[4]; - Execute
snprintf(base_text, sizeof(base_text), "%u", (unsigned)base); - Execute
draw_text_centred(frame, 27, 87, base_text, 1, COLOR_WHITE);
- Decision
error_text != NULLTrue- Execute
draw_text_centred(frame, LCD_WIDTH / 2, 148, error_text, 1, COLOR_RED);
False- Decision
pending_operator != '\0'True- Execute
char operation[20]; - Execute
snprintf(operation, sizeof(operation), "OPERATOR %s", operatorLabel(pending_operator)); - Execute
draw_text_centred(frame, 85, 148, operation, 1, COLOR_YELLOW);
FalseContinue after this decision.
- Execute
draw_text_centred(frame, LCD_WIDTH / 2, 160, "A/K KEY B LOGIC C BACK D CLEAR", 1, COLOR_LIGHTGREY);
Read original function source
void drawCalculator(uint16_t *frame)
{
if (!running || frame == NULL) return;
clear(frame, COLOR_BLACK);
drawValue(frame);
for (uint8_t row = 0U; row < CALCULATOR_ROWS; ++row) {
for (uint8_t column = 0U; column < CALCULATOR_COLUMNS; ++column) {
drawKey(frame, row, column);
}
}
draw_text_centred(frame, 27, 78, "BASE", 1, COLOR_CYAN);
if (ascii_mode) {
draw_text_centred(frame, 27, 87, "ASCII", 1, COLOR_WHITE);
} else {
char base_text[4];
snprintf(base_text, sizeof(base_text), "%u", (unsigned)base);
draw_text_centred(frame, 27, 87, base_text, 1, COLOR_WHITE);
}
if (error_text != NULL) draw_text_centred(frame, LCD_WIDTH / 2, 148, error_text, 1, COLOR_RED);
else if (pending_operator != '\0') {
char operation[20];
snprintf(operation, sizeof(operation), "OPERATOR %s", operatorLabel(pending_operator));
draw_text_centred(frame, 85, 148, operation, 1, COLOR_YELLOW);
}
draw_text_centred(frame, LCD_WIDTH / 2, 160, "A/K KEY B LOGIC C BACK D CLEAR", 1,
COLOR_LIGHTGREY);
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "calculator.h"
#include "game_graphics.h"
#include "PET2001_font.h"
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define CALCULATOR_ROWS 6U
#define CALCULATOR_COLUMNS 6U
#define CALCULATOR_MIN_BASE 2U
#define CALCULATOR_MAX_BASE 16U
#define JOYSTICK_ENTER 600
#define CURSOR_REPEAT_MS 160U
typedef enum {
KEY_NONE,
KEY_DIGIT,
KEY_OPERATOR,
KEY_EQUALS,
KEY_ROOT,
KEY_LOGIC,
KEY_BASE_DOWN,
KEY_BASE_UP,
} calculator_key_type_t;
typedef struct {
calculator_key_type_t type;
char value;
} calculator_key_t;
static const calculator_key_t keypad[CALCULATOR_ROWS][CALCULATOR_COLUMNS] = {
{{KEY_DIGIT, 'A'}, {KEY_DIGIT, 'B'}, {KEY_DIGIT, 'C'},
{KEY_DIGIT, 'D'}, {KEY_DIGIT, 'E'}, {KEY_DIGIT, 'F'}},
{{KEY_DIGIT, '7'}, {KEY_DIGIT, '8'}, {KEY_DIGIT, '9'},
{KEY_OPERATOR, '+'}, {KEY_OPERATOR, '%'}, {KEY_BASE_DOWN, 0}},
{{KEY_DIGIT, '4'}, {KEY_DIGIT, '5'}, {KEY_DIGIT, '6'},
{KEY_OPERATOR, '-'}, {KEY_OPERATOR, 'P'}, {KEY_NONE, 0}},
{{KEY_DIGIT, '1'}, {KEY_DIGIT, '2'}, {KEY_DIGIT, '3'},
{KEY_OPERATOR, '*'}, {KEY_ROOT, 0}, {KEY_BASE_UP, 0}},
{{KEY_NONE, 0}, {KEY_DIGIT, '0'}, {KEY_NONE, 0},
{KEY_OPERATOR, '/'}, {KEY_LOGIC, 0}, {KEY_NONE, 0}},
{{KEY_NONE, 0}, {KEY_NONE, 0}, {KEY_NONE, 0},
{KEY_EQUALS, 0}, {KEY_NONE, 0}, {KEY_NONE, 0}},
};
static bool running;
static bool entry_active;
static bool has_accumulator;
static uint64_t entry_value;
static uint64_t accumulator;
static char pending_operator;
static char logic_operator;
static const char *error_text;
static uint8_t base;
static bool ascii_mode;
static uint8_t selected_row;
static uint8_t selected_column;
static uint32_t last_cursor_move;
static bool isDigitKey(char key)
{
return (key >= '0' && key <= '9') || (key >= 'A' && key <= 'F');
}
static uint8_t digitValue(char key)
{
return key >= 'A' ? (uint8_t)(key - 'A' + 10) : (uint8_t)(key - '0');
}
static uint8_t inputBase(void)
{
return ascii_mode ? 16U : base;
}
static bool keyIsAvailable(const calculator_key_t *key)
{
if (key->type == KEY_NONE) return false;
return key->type != KEY_DIGIT || digitValue(key->value) < inputBase();
}
static const char *logicLabel(void)
{
if (logic_operator == '&') return "AND";
return logic_operator == '|' ? "OR" : "XOR";
}
static const char *operatorLabel(char operation)
{
switch (operation) {
case '+': return "ADD";
case '-': return "SUB";
case '*': return "MUL";
case '/': return "DIV";
case '%': return "MOD";
case 'P': return "POWER";
case '&': return "AND";
case '|': return "OR";
case '^': return "XOR";
default: return "";
}
}
static void clearCalculator(void)
{
entry_active = false;
has_accumulator = false;
entry_value = 0U;
accumulator = 0U;
pending_operator = '\0';
error_text = NULL;
}
static uint64_t displayedValue(void)
{
return entry_active ? entry_value : (has_accumulator ? accumulator : 0U);
}
static void formatValue(uint64_t value, char *text, size_t text_size)
{
static const char digits[] = "0123456789ABCDEF";
char reversed[65];
size_t length = 0U;
do {
reversed[length++] = digits[value % inputBase()];
value /= inputBase();
} while (value != 0U && length < sizeof(reversed));
if (text_size <= length) return;
for (size_t index = 0U; index < length; ++index) {
text[index] = reversed[length - index - 1U];
}
text[length] = '\0';
}
static bool multiplyChecked(uint64_t left, uint64_t right, uint64_t *result)
{
if (right != 0U && left > UINT64_MAX / right) {
error_text = "64 BIT OVERFLOW";
return false;
}
*result = left * right;
return true;
}
static bool powerChecked(uint64_t value, uint64_t exponent, uint64_t *result)
{
uint64_t power = 1U;
uint64_t factor = value;
while (exponent != 0U) {
if ((exponent & 1U) != 0U && !multiplyChecked(power, factor, &power)) return false;
exponent >>= 1U;
if (exponent != 0U && !multiplyChecked(factor, factor, &factor)) return false;
}
*result = power;
return true;
}
static uint64_t integerSquareRoot(uint64_t value)
{
uint64_t result = 0U;
uint64_t bit = UINT64_C(1) << 62U;
while (bit > value) bit >>= 2U;
while (bit != 0U) {
if (value >= result + bit) {
value -= result + bit;
result = (result >> 1U) + bit;
} else {
result >>= 1U;
}
bit >>= 2U;
}
return result;
}
static bool applyOperator(char operation, uint64_t left, uint64_t right, uint64_t *result)
{
switch (operation) {
case '+':
if (left > UINT64_MAX - right) error_text = "64 BIT OVERFLOW";
else *result = left + right;
break;
case '-':
if (left < right) error_text = "NEGATIVE RESULT";
else *result = left - right;
break;
case '*': return multiplyChecked(left, right, result);
case '/':
if (right == 0U) error_text = "DIVIDE BY ZERO";
else *result = left / right;
break;
case '%':
if (right == 0U) error_text = "DIVIDE BY ZERO";
else *result = left % right;
break;
case 'P': return powerChecked(left, right, result);
case '&': *result = left & right; break;
case '|': *result = left | right; break;
case '^': *result = left ^ right; break;
default: return false;
}
return error_text == NULL;
}
static void appendDigit(uint8_t digit)
{
if (error_text != NULL) clearCalculator();
if (!entry_active) {
entry_value = 0U;
entry_active = true;
}
if (entry_value > (UINT64_MAX - digit) / inputBase()) {
error_text = "64 BIT OVERFLOW";
return;
}
entry_value = entry_value * inputBase() + digit;
}
static void selectOperator(char operation)
{
if (error_text != NULL) return;
if (!has_accumulator) {
accumulator = entry_active ? entry_value : 0U;
has_accumulator = true;
} else if (pending_operator != '\0' && entry_active) {
uint64_t result = 0U;
if (!applyOperator(pending_operator, accumulator, entry_value, &result)) return;
accumulator = result;
}
pending_operator = operation;
entry_active = false;
}
static void calculateResult(void)
{
if (error_text != NULL || pending_operator == '\0' || !entry_active) return;
uint64_t result = 0U;
if (!applyOperator(pending_operator, accumulator, entry_value, &result)) return;
entry_value = result;
accumulator = result;
entry_active = true;
has_accumulator = false;
pending_operator = '\0';
}
static void calculateRoot(void)
{
if (error_text != NULL) return;
entry_value = integerSquareRoot(displayedValue());
entry_active = true;
}
static void moveCursor(int row_delta, int column_delta)
{
for (unsigned tries = 0U; tries < CALCULATOR_ROWS * CALCULATOR_COLUMNS; ++tries) {
selected_row = (uint8_t)((selected_row + row_delta + CALCULATOR_ROWS) %
CALCULATOR_ROWS);
selected_column = (uint8_t)((selected_column + column_delta +
CALCULATOR_COLUMNS) % CALCULATOR_COLUMNS);
if (keyIsAvailable(&keypad[selected_row][selected_column])) return;
}
}
static void changeBase(bool increase)
{
if (ascii_mode) {
ascii_mode = false;
base = increase ? CALCULATOR_MIN_BASE : CALCULATOR_MAX_BASE;
} else if (increase && base == CALCULATOR_MAX_BASE) {
ascii_mode = true;
} else if (!increase && base == CALCULATOR_MIN_BASE) {
ascii_mode = true;
} else {
base = (uint8_t)(base + (increase ? 1 : -1));
}
while (!keyIsAvailable(&keypad[selected_row][selected_column])) moveCursor(0, 1);
}
static void pressSelectedKey(void)
{
const calculator_key_t *key = &keypad[selected_row][selected_column];
if (!keyIsAvailable(key)) return;
if (key->type == KEY_DIGIT) appendDigit(digitValue(key->value));
else if (key->type == KEY_OPERATOR) selectOperator(key->value);
else if (key->type == KEY_EQUALS) calculateResult();
else if (key->type == KEY_ROOT) calculateRoot();
else if (key->type == KEY_LOGIC) selectOperator(logic_operator);
else if (key->type == KEY_BASE_DOWN) changeBase(false);
else if (key->type == KEY_BASE_UP) changeBase(true);
}
void startCalculator(uint32_t now_ms)
{
running = true;
base = 10U;
ascii_mode = false;
logic_operator = '&';
selected_row = 3U;
selected_column = 0U;
last_cursor_move = now_ms - CURSOR_REPEAT_MS;
clearCalculator();
}
void stopCalculator(void)
{
running = false;
}
void calculatorAction(bool select, bool backspace, bool clear, bool cycle_logic, uint32_t now_ms)
{
(void)now_ms;
if (!running) return;
if (clear) {
clearCalculator();
} else if (backspace) {
if (error_text != NULL) clearCalculator();
else if (entry_active) entry_value /= inputBase();
} else if (cycle_logic && keypad[selected_row][selected_column].type == KEY_LOGIC) {
logic_operator = logic_operator == '&' ? '|' : (logic_operator == '|' ? '^' : '&');
} else if (select) {
pressSelectedKey();
}
}
void updateCalculator(int x_offset, int y_offset, bool joystick_valid, uint32_t now_ms)
{
if (!running || !joystick_valid || now_ms - last_cursor_move < CURSOR_REPEAT_MS) return;
if (x_offset >= JOYSTICK_ENTER) moveCursor(0, 1);
else if (x_offset <= -JOYSTICK_ENTER) moveCursor(0, -1);
else if (y_offset >= JOYSTICK_ENTER) moveCursor(-1, 0);
else if (y_offset <= -JOYSTICK_ENTER) moveCursor(1, 0);
else return;
last_cursor_move = now_ms;
}
static bool asciiGlyphSupported(uint8_t character)
{
return (character >= '0' && character <= '9') ||
(character >= 'A' && character <= 'Z') ||
(character >= 'a' && character <= 'z') || character == ':' ||
character == '/' || character == '-' || character == '+' ||
character == '.' || character == '\'' || character == '?' ||
character == '!' || character == ' ';
}
static void drawAsciiValue(uint16_t *frame)
{
uint8_t bytes[8];
uint64_t value = displayedValue();
size_t count = 0U;
do {
bytes[count++] = (uint8_t)value;
value >>= 8U;
} while (value != 0U && count < sizeof(bytes));
const int character_width = PET2001_FONT_GLYPH_WIDTH * 2;
int x = (LCD_WIDTH - (int)count * character_width) / 2;
for (size_t index = count; index > 0U; --index) {
const uint8_t character = bytes[index - 1U];
if (character < 32U || character > 126U) {
fill_rect(frame, x, 17, character_width, 16, COLOR_WHITE);
} else {
char text[2] = {(char)(asciiGlyphSupported(character) ? character : '?'), '\0'};
drawPet2001TextTransparent(frame, x, 17, text, COLOR_WHITE, 2);
}
x += character_width;
}
}
static size_t displayCapacity(uint8_t radix)
{
static const uint8_t capacities[] = {
0U, 0U, 64U, 41U, 32U, 28U, 25U, 23U, 22U, 21U, 20U, 19U, 18U,
18U, 17U, 17U, 16U,
};
return capacities[radix];
}
static void drawValue(uint16_t *frame)
{
char value[65];
formatValue(displayedValue(), value, sizeof(value));
if (ascii_mode) {
drawAsciiValue(frame);
return;
}
const size_t length = strlen(value);
const size_t capacity = displayCapacity(inputBase());
const bool wraps_to_two_lines = capacity > 40U;
const size_t digits_per_line = wraps_to_two_lines ? 32U : capacity;
const int scale = capacity <= 20U ? 2 : 1;
const int advance = PET2001_FONT_GLYPH_WIDTH * scale;
const size_t ghost_count = capacity - length;
for (size_t position = 0U; position < capacity; ++position) {
const size_t line = position / digits_per_line;
const size_t line_start = line * digits_per_line;
const size_t line_length = capacity - line_start < digits_per_line ?
capacity - line_start : digits_per_line;
const int x = LCD_WIDTH - (int)line_length * advance +
(int)(position - line_start) * advance;
const int y = wraps_to_two_lines ? 14 + (int)line * 11 : (scale == 2 ? 17 : 22);
const bool ghost = position < ghost_count;
char digit[2] = {ghost ? '0' : value[position - ghost_count], '\0'};
drawPet2001TextTransparent(frame, x, y, digit, ghost ? COLOR_DIMGREY : COLOR_WHITE, scale);
}
}
static int keyX(uint8_t row, uint8_t column)
{
if (row == 0U) return 56 + (int)column * 34;
if (column < 3U) return 57 + (int)column * 45;
if (column == 3U) return 195;
if (column == 4U) return 250;
return 6;
}
static int keyY(uint8_t row)
{
return row == 0U ? 35 : 58 + (int)(row - 1U) * 18;
}
static int keyWidth(uint8_t row, uint8_t column)
{
if (row == 0U) return 29;
if (column < 3U) return 40;
if (column == 3U) return 48;
if (column == 4U) return 61;
return 43;
}
static void drawBasicOperator(uint16_t *frame, int x, int y, int width,
char operation, uint16_t colour)
{
const int centre_x = x + width / 2;
if (operation == '+') {
fill_rect(frame, centre_x - 1, y + 2, 3, 12, colour);
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
} else if (operation == '-') {
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
} else if (operation == '*') {
for (int thickness = -1; thickness <= 1; ++thickness) {
draw_line(frame, centre_x - 6, y + 2 + thickness, centre_x + 6, y + 14 + thickness,
colour);
draw_line(frame, centre_x - 6, y + 14 + thickness, centre_x + 6, y + 2 + thickness,
colour);
}
} else if (operation == '/') {
fill_rect(frame, centre_x - 1, y + 1, 3, 3, colour);
fill_rect(frame, centre_x - 8, y + 7, 17, 3, colour);
fill_rect(frame, centre_x - 1, y + 12, 3, 3, colour);
} else {
fill_rect(frame, centre_x - 8, y + 4, 17, 3, colour);
fill_rect(frame, centre_x - 8, y + 10, 17, 3, colour);
}
}
static void drawKey(uint16_t *frame, uint8_t row, uint8_t column)
{
const calculator_key_t *key = &keypad[row][column];
if (key->type == KEY_NONE) return;
const int x = keyX(row, column);
const int y = keyY(row);
const int width = keyWidth(row, column);
const bool selected = row == selected_row && column == selected_column;
const bool digit = key->type == KEY_DIGIT;
const uint16_t colour = keyIsAvailable(key) ? COLOR_LIGHTGREY : COLOR_DARKGREY;
if (selected) draw_rect(frame, x, y, width, 16, COLOR_ORANGE);
if (key->type == KEY_LOGIC) {
drawPet2001TextCentredTransparent(frame, x + width / 2, y, "LOGIC", selected ? COLOR_ORANGE : colour, 1);
drawPet2001TextCentredTransparent(frame, x + width / 2, y + 8, logicLabel(),
selected ? COLOR_ORANGE : colour, 1);
return;
}
if ((key->type == KEY_OPERATOR && (key->value == '+' || key->value == '-' ||
key->value == '*' || key->value == '/')) ||
key->type == KEY_EQUALS) {
drawBasicOperator(frame, x, y, width, key->type == KEY_EQUALS ? '=' : key->value,
selected ? COLOR_ORANGE : colour);
return;
}
char digit_text[2] = {key->value, '\0'};
const char *label = digit ? digit_text :
(key->type == KEY_OPERATOR ? operatorLabel(key->value) :
(key->type == KEY_EQUALS ? "EQ" :
(key->type == KEY_ROOT ? "ROOT" :
(key->type == KEY_BASE_DOWN ? "BASE-" : "BASE+"))));
drawPet2001TextCentredTransparent(frame, x + width / 2, y + (digit ? (row == 0U ? 0 : 1) : 4),
label, selected ? COLOR_ORANGE : colour, digit ? 2 : 1);
}
void drawCalculator(uint16_t *frame)
{
if (!running || frame == NULL) return;
clear(frame, COLOR_BLACK);
drawValue(frame);
for (uint8_t row = 0U; row < CALCULATOR_ROWS; ++row) {
for (uint8_t column = 0U; column < CALCULATOR_COLUMNS; ++column) {
drawKey(frame, row, column);
}
}
drawPet2001TextCentredTransparent(frame, 27, 78, "BASE", COLOR_CYAN, 1);
if (ascii_mode) {
drawPet2001TextCentredTransparent(frame, 27, 87, "ASCII", COLOR_WHITE, 1);
} else {
char base_text[4];
snprintf(base_text, sizeof(base_text), "%u", (unsigned)base);
drawPet2001TextCentredTransparent(frame, 27, 87, base_text, COLOR_WHITE, 1);
}
if (error_text != NULL) drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 148, error_text, COLOR_RED, 1);
else if (pending_operator != '\0') {
char operation[20];
snprintf(operation, sizeof(operation), "OPERATOR %s", operatorLabel(pending_operator));
drawPet2001TextCentredTransparent(frame, 85, 148, operation, COLOR_YELLOW, 1);
}
drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 160,
"A/K KEY B LOGIC C BACK D CLEAR", COLOR_LIGHTGREY, 1);
}