// // Author: Jeremy Rand // Date: July 20, 2012 // // This is the implementation for the Curta emulator UI. // #include #include #include #include #include #include #include #include "curtaModel.h" #include "curtaUI.h" // Extern symbols for joystick and graphics drivers extern char a2e_stdjoy; extern char a2e_hi; typedef int8_t tAction; #define ACTION_OPERAND_SHIFT_LEFT 0 #define ACTION_OPERAND_SHIFT_RIGHT 1 #define ACTION_OPERAND_INC 2 #define ACTION_OPERAND_DEC 3 #define ACTION_RESULT_SHIFT_LEFT 4 #define ACTION_RESULT_SHIFT_RIGHT 5 #define ACTION_ADD 6 #define ACTION_SUBTRACT 7 #define ACTION_CLEAR 8 #define ACTION_NULL 9 typedef int8_t tJoyPos; #define JOY_POS_CENTER 0 #define JOY_POS_DOWN 1 #define JOY_POS_DOWN_LEFT 2 #define JOY_POS_LEFT 3 #define JOY_POS_UP_LEFT 4 #define JOY_POS_UP 5 #define JOY_POS_UP_RIGHT 6 #define JOY_POS_RIGHT 7 #define JOY_POS_DOWN_RIGHT 8 #define OPERAND_COLOR COLOR_WHITE #define OPERAND_OFFSET 4 #define SLIDER_BAR_COLOR COLOR_BLUE #define SELECTED_SLIDER_BAR_COLOR COLOR_VIOLET #define SLIDER_COLOR COLOR_ORANGE #define SELECTED_SLIDER_COLOR COLOR_BLACK #define SLIDER_X_BORDER 8 #define SLIDER_Y_BORDER 15 #define SLIDER_BAR_WIDTH 12 #define SLIDER_BAR_HEIGHT 130 #define SLIDER_BAR_SPACING 20 #define SLIDER_X_OFFSET 2 #define SLIDER_Y_OFFSET 1 #define SLIDER_WIDTH 8 #define SLIDER_HEIGHT 11 #define SLIDER_Y_SPACING (SLIDER_HEIGHT + (2 * SLIDER_Y_OFFSET)) static char displayBuffer[] = "\n\n" "Counter: 0 0 0 0 0 0 0 0\n" " Result: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" " ^"; #define COUNTER_OFFSET 25 #define RESULT_OFFSET 50 #define BASE_OFFSET 103 static void playSound(int freq, int duration) { while (duration > 0) { asm ("STA %w", 0xc030); while (freq > 0) { freq--; } duration--; } } static void updateCounter(void) { tDigitPos pos; char *ptr = &(displayBuffer[COUNTER_OFFSET]); for(pos = NUM_COUNTER_DIGITS - 1; pos >= 0; pos--) { *ptr = GET_COUNTER_DIGIT(pos) + '0'; ptr+=2; } } static void updateResult(void) { tDigitPos pos; char *ptr = &(displayBuffer[RESULT_OFFSET]); for(pos = NUM_RESULT_DIGITS - 1; pos >= 0; pos--) { *ptr = GET_RESULT_DIGIT(pos) + '0'; ptr+=2; } ptr = &(displayBuffer[BASE_OFFSET]); for(pos = BASE_POS_MAX - 1; pos >= BASE_POS_MIN; pos--) { if (pos == basePos) { *ptr = '^'; } else { *ptr = ' '; } ptr+=2; } } static void printState(void) { updateCounter(); updateResult(); puts(displayBuffer); } static void drawSlider(char xPos, tDigit digit, char color) { tgi_setcolor(color); tgi_bar(xPos + SLIDER_X_OFFSET, SLIDER_Y_BORDER + SLIDER_Y_OFFSET + (SLIDER_Y_SPACING * digit), xPos + SLIDER_X_OFFSET + SLIDER_WIDTH, SLIDER_Y_BORDER + SLIDER_Y_OFFSET + (SLIDER_Y_SPACING * digit) + SLIDER_HEIGHT); } static void drawBar(char xPos, tDigit digit, bool isSelected) { char barColor = SLIDER_BAR_COLOR; char sliderColor = SLIDER_COLOR; if (isSelected) { barColor = SELECTED_SLIDER_BAR_COLOR; sliderColor = SELECTED_SLIDER_COLOR; } // Draw slider bar tgi_setcolor(barColor); tgi_bar(xPos, SLIDER_Y_BORDER, xPos + SLIDER_BAR_WIDTH, SLIDER_Y_BORDER + SLIDER_BAR_HEIGHT); // Draw slider drawSlider(xPos, digit, sliderColor); } static void drawText(char xPos, tDigit digit) { char buffer[2]; // Clear old text tgi_setcolor(COLOR_BLACK); tgi_bar(xPos, 0, xPos + SLIDER_BAR_WIDTH, SLIDER_Y_BORDER - 1); // Draw text label buffer[0] = digit + '0'; buffer[1] = '\0'; tgi_setcolor(OPERAND_COLOR); tgi_outtextxy(xPos + OPERAND_OFFSET, 0, buffer); } static void changeOperand(tDigitPos pos, tDigit oldValue, tDigit newValue) { char xPos; char barColor = SELECTED_SLIDER_BAR_COLOR; char sliderColor = SELECTED_SLIDER_COLOR; if (!IS_VALID_OPERAND_POS(pos)) return; xPos = SLIDER_X_BORDER + (SLIDER_BAR_SPACING * (NUM_OPERAND_DIGITS - pos - 1)); drawText(xPos, newValue); drawSlider(xPos, oldValue, SELECTED_SLIDER_BAR_COLOR); drawSlider(xPos, newValue, SELECTED_SLIDER_COLOR); } static void changeSelectedOperand(tDigitPos pos) { char xPos; tDigit digit; if (!IS_VALID_OPERAND_POS(pos)) return; digit = GET_OPERAND_DIGIT(pos); xPos = SLIDER_X_BORDER + (SLIDER_BAR_SPACING * (NUM_OPERAND_DIGITS - pos - 1)); drawBar(xPos, digit, IS_SELECTED_OPERAND(pos)); } static void drawOperand(tDigitPos pos) { char xPos; tDigit digit; if (!IS_VALID_OPERAND_POS(pos)) return; digit = GET_OPERAND_DIGIT(pos); xPos = SLIDER_X_BORDER + (SLIDER_BAR_SPACING * (NUM_OPERAND_DIGITS - pos - 1)); drawText(xPos, digit); drawBar(xPos, digit, IS_SELECTED_OPERAND(pos)); } static tJoyPos getJoyPos(char joyState) { if (JOY_BTN_UP(joyState)) { if (JOY_BTN_LEFT(joyState)) { return JOY_POS_UP_LEFT; } else if (JOY_BTN_RIGHT(joyState)) { return JOY_POS_UP_RIGHT; } else { return JOY_POS_UP; } } else if (JOY_BTN_DOWN(joyState)) { if (JOY_BTN_LEFT(joyState)) { return JOY_POS_DOWN_LEFT; } else if (JOY_BTN_RIGHT(joyState)) { return JOY_POS_DOWN_RIGHT; } else { return JOY_POS_DOWN; } } else { if (JOY_BTN_LEFT(joyState)) { return JOY_POS_LEFT; } else if (JOY_BTN_RIGHT(joyState)) { return JOY_POS_RIGHT; } else { return JOY_POS_CENTER; } } } static tAction getNextAction(void) { static bool firstCall = true; static char oldJoyState; static tJoyPos oldJoyPos; static unsigned int possibleActions = 0xffff; char joyState; tJoyPos joyPos; tAction result = ACTION_NULL; if (firstCall) { oldJoyState = joy_read(JOY_1); oldJoyPos = getJoyPos(oldJoyState); firstCall = false; return result; } joyState = joy_read(JOY_1); joyPos = getJoyPos(joyState); if (joyPos == oldJoyPos) { oldJoyState = joyState; return result; } else if (oldJoyPos == JOY_POS_CENTER) { if (joyPos == JOY_POS_LEFT) { if (JOY_BTN_FIRE(joyState)) { possibleActions = (1 << ACTION_RESULT_SHIFT_LEFT); } else { possibleActions = (1 << ACTION_OPERAND_SHIFT_LEFT); } } else if (joyPos == JOY_POS_RIGHT) { if (JOY_BTN_FIRE(joyState)) { possibleActions = (1 << ACTION_RESULT_SHIFT_RIGHT); } else { possibleActions = (1 << ACTION_OPERAND_SHIFT_RIGHT); } } else if (joyPos == JOY_POS_UP) { possibleActions = (1 << ACTION_OPERAND_DEC); } else if (joyPos == JOY_POS_DOWN) { possibleActions = (1 << ACTION_OPERAND_INC); if (JOY_BTN_FIRE(joyState)) { possibleActions |= (1 << ACTION_SUBTRACT); } else if (JOY_BTN_FIRE2(joyState)) { possibleActions |= (1 << ACTION_CLEAR); } else { possibleActions |= (1 << ACTION_ADD); } } } else { if (possibleActions & (1 << ACTION_OPERAND_SHIFT_LEFT)) { if (joyPos == JOY_POS_CENTER) { result = ACTION_OPERAND_SHIFT_LEFT; } else { possibleActions &= (~(1 << ACTION_OPERAND_SHIFT_LEFT)); } } if (possibleActions & (1 << ACTION_OPERAND_SHIFT_RIGHT)) { if (joyPos == JOY_POS_CENTER) { result = ACTION_OPERAND_SHIFT_RIGHT; } else { possibleActions &= (~(1 << ACTION_OPERAND_SHIFT_RIGHT)); } } if (possibleActions & (1 << ACTION_OPERAND_INC)) { if (joyPos == JOY_POS_CENTER) { result = ACTION_OPERAND_INC; } else { possibleActions &= (~(1 << ACTION_OPERAND_INC)); } } if (possibleActions & (1 << ACTION_OPERAND_DEC)) { if (joyPos == JOY_POS_CENTER) { result = ACTION_OPERAND_DEC; } else { possibleActions &= (~(1 << ACTION_OPERAND_DEC)); } } if (possibleActions & (1 << ACTION_RESULT_SHIFT_LEFT)) { if (joyPos == JOY_POS_CENTER) { result = ACTION_RESULT_SHIFT_LEFT; } else { possibleActions &= (~(1 << ACTION_RESULT_SHIFT_LEFT)); } } if (possibleActions & (1 << ACTION_RESULT_SHIFT_RIGHT)) { if (joyPos == JOY_POS_CENTER) { result = ACTION_RESULT_SHIFT_RIGHT; } else { possibleActions &= (~(1 << ACTION_RESULT_SHIFT_RIGHT)); } } if (possibleActions & (1 << ACTION_ADD)) { if ((joyPos == JOY_POS_DOWN) && (oldJoyPos == JOY_POS_DOWN_RIGHT)) { result = ACTION_ADD; possibleActions = 0; } else if (joyPos != oldJoyPos + 1) { possibleActions &= (~(1 << ACTION_ADD)); } } else if (possibleActions & (1 << ACTION_SUBTRACT)) { if ((joyPos == JOY_POS_DOWN) && (oldJoyPos == JOY_POS_DOWN_RIGHT)) { result = ACTION_SUBTRACT; possibleActions = 0; } else if (joyPos != oldJoyPos + 1) { possibleActions &= (~(1 << ACTION_SUBTRACT)); } } else if (possibleActions & (1 << ACTION_CLEAR)) { if ((joyPos == JOY_POS_DOWN) && (oldJoyPos == JOY_POS_DOWN_RIGHT)) { result = ACTION_CLEAR; possibleActions = 0; } else if (joyPos != oldJoyPos + 1) { possibleActions &= (~(1 << ACTION_CLEAR)); } } if (joyPos == JOY_POS_CENTER) { possibleActions = -1; } } oldJoyPos = joyPos; oldJoyState = joyState; return result; } void initUI(void) { // Install drivers tDigitPos pos; initDevice(changeOperand, changeSelectedOperand); joy_install(&a2e_stdjoy); tgi_install(&a2e_hi); tgi_init(); tgi_clear(); for (pos = 0; pos < NUM_OPERAND_DIGITS; pos++) { drawOperand(pos); } // Mixed text and graphics mode asm ("STA %w", 0xc053); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printState(); } bool processNextEvent(void) { bool timeToQuit = false; // Exit on ESC if ((kbhit()) && (cgetc() == 27)) { timeToQuit = true; } switch (getNextAction()) { case ACTION_NULL: break; case ACTION_OPERAND_SHIFT_LEFT: shiftOperandPos(true); break; case ACTION_OPERAND_SHIFT_RIGHT: shiftOperandPos(false); break; case ACTION_OPERAND_INC: playSound(100, 100); incOperandPos(selectedOperand); break; case ACTION_OPERAND_DEC: playSound(100, 100); decOperandPos(selectedOperand); break; case ACTION_RESULT_SHIFT_LEFT: shiftResultPos(true); printState(); break; case ACTION_RESULT_SHIFT_RIGHT: shiftResultPos(false); printState(); break; case ACTION_ADD: crank(false); printState(); break; case ACTION_SUBTRACT: crank(true); printState(); break; case ACTION_CLEAR: clearDevice(); printState(); break; } return timeToQuit; } void shutdownUI(void) { // Uninstall drivers tgi_uninstall(); joy_uninstall(); }