From e24040944991f4f7d8662fdfcfc2000fd8f25ba8 Mon Sep 17 00:00:00 2001 From: Zane Kaminski Date: Fri, 22 May 2020 18:25:08 -0400 Subject: [PATCH] Added VBL-based waiting --- ram2e.c | 204 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 85 deletions(-) diff --git a/ram2e.c b/ram2e.c index aa458c5..f4a238b 100644 --- a/ram2e.c +++ b/ram2e.c @@ -8,15 +8,32 @@ #define true 1 #define false 0 + +#define VBL ((signed char*)0xC019) +const uint8_t SPIN_HALFCYCLES = 3; +const uint8_t SPIN_FRAMESPERCHAR = 4; + #define PB0 ((char*)0xC061) #define PB1 ((char*)0xC062) static char read_applekey(void) { return (*PB0 | *PB1) & 0x80; } -static void ram2e_cmd(char operation, char data) { +char _cmd; +char _arg; +/* ram2e_cmd(...) issues a coded command+argument sequence to the RAM2E */ +static void ram2e_cmd(char cmd, char arg) { // Load operation and data bytes into X and Y registers // in preparation for command sequence - __asm__("ldx %o", operation); - __asm__("ldy %o", data); + _cmd = cmd; + _arg = arg; + __asm__("ldx %v", _cmd); // X = command + __asm__("ldy %v", _arg); // Y = argument + + // First, reset command sequence just in case it, + // for some reason, has not timed out. (e.g. crazy fast accelerator?) + // Write 0 twice because command and argument steps always advance seq. + __asm__("lda #0"); + __asm__("sta $C073"); + __asm__("sta $C073"); // Command sequence __asm__("lda #$FF"); @@ -31,44 +48,44 @@ static void ram2e_cmd(char operation, char data) { __asm__("sta $C073"); __asm__("lda #$AD"); __asm__("sta $C073"); - // Operation + // Command __asm__("stx $C073"); - // Data + // Argument __asm__("sty $C073"); + + // Reset RAMWorks bank register just in case + __asm__("lda #0"); + __asm__("sta $C073"); } + +/* set_mask_temp(...) sends the "Set RAMWorks Capacity Mask" to the RAM2E */ static void set_mask_temp(char mask) { ram2e_cmd(0xE0, mask); } + +/* ufm_bitbang(...) sends the "Set UFM Bitbang Outputs" to the RAM2E */ static void ufm_bitbang(char bitbang) { ram2e_cmd(0xEA, bitbang); } +/* ufm_program(...) sends the "UFM Program Once" command to the RAM2E */ +static void ufm_program() { ram2e_cmd(0xEF, 0x00); } + +/* ufm_erase(...) sends the "UFM Erase Once" command to the RAM2E */ +static void ufm_erase() { ram2e_cmd(0xEE, 0x00); } + +/* set_mask_temp(...) sends the "Set RAMWorks Capacity Mask" */ static void set_nvm(char mask) { int i; - // Shift mask into UFMD twice + // Shift mask OR'd with data register clock pulse trigger into UFMD twice for (i = 0; i < 2; i++) { - char maskpart; - - maskpart = 0x80 | ((mask >> 1) & 0x40); - ufm_bitbang(maskpart); - - maskpart = 0x80 | ((mask ) & 0x40); - ufm_bitbang(maskpart); - - maskpart = 0x80 | ((mask << 1) & 0x40); - ufm_bitbang(maskpart); - - maskpart = 0x80 | ((mask << 2) & 0x40); - ufm_bitbang(maskpart); - - maskpart = 0x80 | ((mask << 3) & 0x40); - ufm_bitbang(maskpart); - - maskpart = 0x80 | ((mask << 4) & 0x40); - ufm_bitbang(maskpart); - - maskpart = 0x80 | ((mask << 5) & 0x40); - ufm_bitbang(maskpart); - - maskpart = 0x80 | ((mask << 6) & 0x40); - ufm_bitbang(maskpart); + ufm_bitbang(0x80 | ((mask >> 1) & 0x40)); + ufm_bitbang(0x80 | ((mask >> 0) & 0x40)); + ufm_bitbang(0x80 | ((mask << 1) & 0x40)); + ufm_bitbang(0x80 | ((mask << 2) & 0x40)); + ufm_bitbang(0x80 | ((mask << 3) & 0x40)); + ufm_bitbang(0x80 | ((mask << 4) & 0x40)); + ufm_bitbang(0x80 | ((mask << 5) & 0x40)); + ufm_bitbang(0x80 | ((mask << 6) & 0x40)); } + // Program UFM + ufm_program(); } static void menu(void) @@ -103,114 +120,131 @@ static void menu(void) } static void spin(uint8_t x, uint8_t y) { - int i; - const uint8_t spin_big = 10; - const uint8_t spin_small = 150; + char i; - for (i = 0; i < spin_big; i++) { - int j; - for (j = 0; j < spin_small; j++) { - __asm__("lda $C000"); + // Sync to frame before starting + while (*VBL >= 0); + + // Wait and animate spinner. + // Spin_half + for (i = 0; i < SPIN_HALFCYCLES; i++) { + char j; + for (j = 0; j < 4; j++) { + char spinchar; + char k; + + // Assign spinner char based on j + switch (j) { + case 0: spinchar = '\\'; break; + case 1: spinchar = '|'; break; + case 2: spinchar = '/'; break; + case 3: spinchar = '-'; break; + default: spinchar = '-'; break; + } + + // Write it to screen gotoxy(x, y); - __asm__("lda $C000"); - cputs("-"); - __asm__("lda $C000"); - } - for (j = 0; j < spin_small; j++) { - __asm__("lda $C000"); - gotoxy(x, y); - __asm__("lda $C000"); - cputs("\\"); - __asm__("lda $C000"); - } - for (j = 0; j < spin_small; j++) { - __asm__("lda $C000"); - gotoxy(x, y); - __asm__("lda $C000"); - cputs("|"); - __asm__("lda $C000"); - } - for (j = 0; j < spin_small; j++) { - __asm__("lda $C000"); - gotoxy(x, y); - __asm__("lda $C000"); - cputs("/"); - __asm__("lda $C000"); + putchar(spinchar); + + // Wait specificed number of frames + for (k = 0; k < SPIN_FRAMESPERCHAR; k++) { + while (*VBL < 0); + while (*VBL >= 0); + } } } + + // Wait a frame when finished + while (*VBL < 0); + while (*VBL >= 0); } int main(void) { - char key; char mask; char nvm; + int reset_count; + // First clear screen clrscr(); // Make sure we are running on an Apple IIe if((get_ostype() & 0xF0) != APPLE_IIE) { + // If not on Apple IIe, show an error message and quit gotoxy(0, 8); cputs(" THIS PROGRAM REQUIRES AN APPLE IIE."); gotoxy(0, 10); cputs(" PRESS ANY KEY TO QUIT."); - cgetc(); - clrscr(); + cgetc(); // Wait for key + clrscr(); // Clear screen before quitting return EXIT_SUCCESS; } // Show menu menu(); - // Get user choice + // Get user choice from menu mask = 0; nvm = 0; + reset_count = 0; while (true) { - key = toupper(cgetc()); - - // Set capacity mask or quit according to keypress - if (key == 'Q') { - // Quit - clrscr(); - return EXIT_SUCCESS; + // Set capacity mask or quit according to keypress. + switch (toupper(cgetc())) { + case 'Q' : { + clrscr(); + return EXIT_SUCCESS; + } + case '1': mask = 0x00; break; + case '2': mask = 0x07; break; + case '3': mask = 0x0F; break; + case '4': mask = 0x3F; break; + case '5': mask = 0x7F; break; + case 'R': { + if (reset_count > 127) { + ufm_erase(); + reset_count = 0; + } else { reset_count++; } + } default: continue; } - else if (key == '1') { mask = 0x00; } - else if (key == '2') { mask = 0x07; } - else if (key == '3') { mask = 0x0F; } - else if (key == '4') { mask = 0x3F; } - else if (key == '5') { mask = 0x7F; } - else { continue; } - // Check if pressed with apple key + // Check if pressed with apple key. + // If so, save to nonvolatile memory. if (read_applekey()) { nvm = true; } break; } - // Set capacity in volatile memory + // Set capacity in volatile memory. set_mask_temp(mask); + // Clear screen in preparation to show saving or success message. clrscr(); - if (nvm) { // Save in NVM if requested - set_nvm(mask); + if (nvm) { // Save in NVM if requested. + // Show message about saving. gotoxy(1, 8); cputs("Saving RAM2E capacity setting."); gotoxy(1, 9); cputs("Do not turn off your Apple."); + // Save capacity in nonvolatile memory. + set_nvm(mask); + + // Wait for >= 500ms on even the fastest systems. spin(32, 8); + // Clear screen again. clrscr(); gotoxy(1, 8); cputs("RAM2E capacity saved successfully."); - } else { // Print success message + } else { // Print success message if not saving in NVM. gotoxy(1, 8); cputs("RAM2E capacity set successfully."); } - gotoxy(1, 10); cputs("Press any key to quit."); + gotoxy(1, 11); + cputs("You may also turn off your Apple."); cgetc(); // Quit