diff --git a/Makefile b/Makefile index 3952ec4..430ca4e 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ all: RAM2Eutil.po RAM2Eutil.dbg.po -.PHONY: clean - obj: @mkdir obj bin: @mkdir bin -ram2e.c: +obj/main.o: obj main.c + cc65 main.c -O --cpu 6502 -t apple2enh -o obj/main.s + ca65 obj/main.s -o obj/main.o obj/ram2e.o: obj ram2e.c cc65 ram2e.c -O --cpu 6502 -t apple2enh -o obj/ram2e.s @@ -18,19 +18,35 @@ obj/ram2e.dbg.o: obj ram2e.c cc65 ram2e.c -O --cpu 6502 -t apple2enh -o obj/ram2e.dbg.s -DSKIP_RAM2E_DETECT ca65 obj/ram2e.dbg.s -o obj/ram2e.dbg.o -bin/ram2e.sys: bin obj/ram2e.o - ld65 -o bin/ram2e.sys obj/ram2e.o -C apple2enh-system.cfg --lib apple2enh.lib -D __EXEHDR__=0 +obj/ram2gs_asm.o: obj ram2gs_asm.s + ca65 ram2gs_asm.s -o obj/ram2gs_asm.o -bin/ram2e.dbg.sys: bin obj/ram2e.dbg.o - ld65 -o bin/ram2e.dbg.sys obj/ram2e.dbg.o -C apple2enh-system.cfg --lib apple2enh.lib -D __EXEHDR__=0 +obj/ram2gs.o: obj ram2gs.c + cc65 ram2gs.c -O --cpu 6502 -t apple2enh -o obj/ram2gs.s + ca65 obj/ram2gs.s -o obj/ram2gs.o -RAM2Eutil.po: bin/ram2e.sys +obj/ram2gs.dbg.o: obj ram2gs.c + cc65 ram2gs.c -O --cpu 6502 -t apple2enh -o obj/ram2gs.dbg.s -DSKIP_RAM2GS_DETECT + ca65 obj/ram2gs.dbg.s -o obj/ram2gs.dbg.o + +obj/util.o: obj util.c + cc65 util.c -O --cpu 6502 -t apple2enh -o obj/util.s + ca65 obj/util.s -o obj/util.o + +bin/main.sys: bin obj/main.o obj/ram2e.o obj/ram2gs.o obj/ram2gs_asm.o obj/util.o + ld65 -o bin/main.sys obj/main.o obj/ram2gs.o obj/ram2e.o obj/ram2gs_asm.o obj/util.o -C apple2enh-system.cfg --lib apple2enh.lib -D __EXEHDR__=0 + +bin/main.dbg.sys: bin obj/main.o obj/ram2e.dbg.o obj/ram2gs.dbg.o obj/ram2gs_asm.o obj/util.o + ld65 -o bin/main.dbg.sys obj/main.o obj/ram2gs.dbg.o obj/ram2e.dbg.o obj/ram2gs_asm.o obj/util.o -C apple2enh-system.cfg --lib apple2enh.lib -D __EXEHDR__=0 + +RAM2Eutil.po: bin/main.sys cp prodos140.po bin/RAM2Eutil.po - cat bin/ram2e.sys | java -jar ./AppleCommander-ac-1.6.0.jar -p bin/RAM2Eutil.po ram2e.system sys 0x2000 + cat bin/main.sys | java -jar ./AppleCommander-ac-1.6.0.jar -p bin/RAM2Eutil.po gwram.system sys 0x2000 -RAM2Eutil.dbg.po: bin/ram2e.dbg.sys +RAM2Eutil.dbg.po: bin/main.dbg.sys cp prodos140.po bin/RAM2Eutil.dbg.po - cat bin/ram2e.dbg.sys | java -jar ./AppleCommander-ac-1.6.0.jar -p bin/RAM2Eutil.dbg.po ram2e.system sys 0x2000 + cat bin/main.dbg.sys | java -jar ./AppleCommander-ac-1.6.0.jar -p bin/RAM2Eutil.dbg.po gwram.system sys 0x2000 +.PHONY: clean clean: rm -fr bin obj diff --git a/bin/RAM2Eutil.dbg.po b/bin/RAM2Eutil.dbg.po index 2f2c9cb..4500fbb 100644 Binary files a/bin/RAM2Eutil.dbg.po and b/bin/RAM2Eutil.dbg.po differ diff --git a/bin/RAM2Eutil.po b/bin/RAM2Eutil.po index d86b29d..7d74161 100644 Binary files a/bin/RAM2Eutil.po and b/bin/RAM2Eutil.po differ diff --git a/main.c b/main.c new file mode 100644 index 0000000..aca4bfe --- /dev/null +++ b/main.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "ram2e.h" +#include "ram2gs.h" + +int main(void) +{ + // First clear screen + clrscr(); + + // Check machine type + switch ((get_ostype() & 0xF0)) { + case APPLE_IIE: + ram2e_main(); + break; + case APPLE_IIGS: + ram2gs_main(); + break; + default: + // If not on IIe or IIgs, show an error message and quit + gotoxy(0, 8); + cputs(" THIS PROGRAM REQUIRES APPLE IIE OR IIGS"); + gotoxy(0, 10); + cputs(" PRESS ANY KEY TO QUIT."); + cgetc(); // Wait for key + clrscr(); // Clear screen before quitting + return EXIT_SUCCESS; + break; + } +} diff --git a/ram2e.c b/ram2e.c index 5268dc7..efefeb6 100644 --- a/ram2e.c +++ b/ram2e.c @@ -5,20 +5,10 @@ #include #include -#define true 1 -#define false 0 +#include "util.h" - -#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; } - -char _cmd; -char _arg; +static char _cmd; +static 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 @@ -100,7 +90,7 @@ static char auxram_detect() { } /* ram2e_detect() returns true if a RAM2E II has been detected */ -uint8_t _detect; +static uint8_t _detect; static char ram2e_detect() { #ifdef SKIP_RAM2E_DETECT return true; @@ -148,8 +138,8 @@ static char ram2e_detect() { } /* ramworks_getsize() returns the number of banks of RAM2E aux memory */ -uint8_t _rwsize; -uint8_t _rwnot16mb; +static uint8_t _rwsize; +static uint8_t _rwnot16mb; static uint16_t ramworks_getsize() { _rwnot16mb = 1; // Set "not 16 mb" flag @@ -169,11 +159,11 @@ static uint16_t ramworks_getsize() { CountLoop: __asm__("sty $C073"); // Set bank __asm__("cpy $00"); // Is bank num stored at address 0? - __asm__("bne %g", NotMem); // If not, skip increment + __asm__("bne %g", AfterInc); // If not, skip increment __asm__("inx"); // If so, increment bank count - __asm__("bne %g", NotMem); // Skip next if x!=0 + __asm__("bne %g", AfterInc); // Skip next if x!=0 __asm__("stx %v", _rwnot16mb); // Othwerwise rolled over so clear rwnot16mb - NotMem: + AfterInc: __asm__("iny"); // Move to next bank __asm__("bne %g", CountLoop); // Repeat if not on bank 0 @@ -250,67 +240,12 @@ static void menu(void) cputs("Press [Q] to quit without saving."); } -static void spin(uint8_t x, uint8_t y) { - char i; - - // 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); - 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) +int ram2e_main(void) { 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(); // Wait for key - clrscr(); // Clear screen before quitting - return EXIT_SUCCESS; - } - // Check for RAM2E if(!auxram_detect() || !ram2e_detect()) { // If no RAM2E, show an error message and quit @@ -331,7 +266,7 @@ int main(void) reset_count = 0; while (true) { // Set capacity mask or quit according to keypress. - switch (toupper(cgetc())) { + switch (toupper(cgetc() & 0x7F)) { case 'Q' : { clrscr(); return EXIT_SUCCESS; @@ -366,8 +301,7 @@ int main(void) } default: continue; } - // Check if pressed with apple key. - // If so, save to nonvolatile memory. + // Check if pressed with apple key. If so, save to nonvolatile memory. if (read_applekey()) { nvm = true; } break; } diff --git a/ram2e.h b/ram2e.h new file mode 100644 index 0000000..a8da674 --- /dev/null +++ b/ram2e.h @@ -0,0 +1,6 @@ +#ifndef RAM2E_H +#define RAM2E_H + +int ram2e_main(void); + +#endif /* RAM2E_H */ diff --git a/ram2gs.c b/ram2gs.c new file mode 100644 index 0000000..065f244 --- /dev/null +++ b/ram2gs.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "ram2gs_asm.h" + +static void ram2gs_erase() { ram2gs_cmd(0x28); } +static void ram2gs_program() { ram2gs_cmd(0x24); } +static void ram2gs_set4mb() { ram2gs_cmd(0x10); } +static void ram2gs_set8mb() { ram2gs_cmd(0x11); } +static void ram2gs_setnvm(char en8meg) { + char i; + // Clock in 0 to enable this setting entry + ram2gs_cmd(0x20); + ram2gs_cmd(0x22); + + if (en8meg) { + // Clock in 1 to enable 8mb + ram2gs_cmd(0x21); + ram2gs_cmd(0x23); + } else { + // Clock in 0 to disable 8mb + ram2gs_cmd(0x20); + ram2gs_cmd(0x22); + } + + // Clock in 14 dummy "1"s + for (i = 0; i < 14; i++) { + ram2gs_cmd(0x21); + ram2gs_cmd(0x23); + } + + ram2gs_program(); +} + +static void menu(void) +{ + uint8_t bankcount = ram2gs_getsize(); + gotoxy(5, 1); + cputs("-- RAM2GS Capacity Settings --"); + gotoxy(4, 3); + printf("Current RAM2GS capacity: %d kB", bankcount * 64); + + gotoxy(1, 6); + cputs("Select desired memory capacity:"); + + gotoxy(4, 8); + cputs("1. 4 megabytes"); + gotoxy(4, 10); + cputs("2. 8 megabytes"); + + gotoxy(1, 18); + cputs("Capacity will be saved until power-off."); + + gotoxy(1, 20); + cputs("To remember capacity setting in"); + gotoxy(1, 21); + cputs("nonvolatile memory, press Apple+number."); + + gotoxy(1, 23); + cputs("Press [Q] to quit without saving."); +} + +int ram2gs_main(void) +{ + char en8meg; + char nvm; + int reset_count; + + // Check for RAM2GS + #ifndef SKIP_RAM2GS_DETECT + if(!ram2gs_detect()) { + // If no RAM2GS, show an error message and quit + gotoxy(0, 8); + cputs(" No RAM2GS II detected."); + gotoxy(0, 10); + cputs(" Press any key to quit."); + cgetc(); // Wait for key + clrscr(); // Clear screen before quitting + return EXIT_SUCCESS; + } + #endif + + menu(); // Print menu + + // Get user choice from menu + en8meg = 0; + nvm = 0; + reset_count = 0; + while (true) { + // Set capacity or quit according to keypress. + switch (toupper(cgetc() & 0x7F)) { + case 'Q' : { + clrscr(); + return EXIT_SUCCESS; + } + case '!': nvm = true; + case '1': en8meg = 0; ram2gs_set4mb(); break; + case '@': nvm = true; + case '2': en8meg = 1; ram2gs_set8mb(); break; + case 'R': { + reset_count++; + if (reset_count >= 100) { + // Show message about saving. + clrscr(); // Clear screen + gotoxy(1, 8); + cputs("Resetting RAM2GS settings."); + gotoxy(1, 9); + cputs("Do not turn off your Apple."); + + ram2gs_erase(); // Erase RAM2GS settings memory + ram2gs_set8mb(); // Enable 8 megabytes now + + // Wait for >= 500ms on even the fastest systems. + spin(32, 8); + + // Show success message and quit + clrscr(); // Clear screen + gotoxy(1, 8); + cputs("RAM2GS settings reset successfully."); + goto end; + } + } default: continue; + } + + // Check if pressed with apple key. If so, save to nonvolatile memory. + if (read_applekey()) { nvm = true; } + break; + } + + // Clear screen in preparation to show saving or success message. + clrscr(); + + if (nvm) { // Save in NVM if requested. + // Show message about saving. + gotoxy(1, 8); + cputs("Saving RAM2GS capacity setting."); + gotoxy(1, 9); + cputs("Do not turn off your Apple."); + // Save capacity in nonvolatile memory. + ram2gs_setnvm(en8meg); + // Wait for >= 500ms on even the fastest systems. + spin(33, 8); + // Print success message + clrscr(); // Clear screen + gotoxy(1, 8); + cputs("RAM2GS capacity saved successfully."); + } else { // Print success message if not saving in NVM. + gotoxy(1, 8); + cputs("RAM2GS capacity set successfully."); + } + + end: + if (nvm) { // Show end message for nonvolatile save + gotoxy(1, 10); + cputs("You may now turn off your Apple."); + gotoxy(1, 12); + cputs("You may also reset your Apple for"); + gotoxy(1, 13); + cputs("the setting change to take effect."); + } else { // Show end message for volatile save + gotoxy(1, 10); + cputs("Please reset your Apple for"); + gotoxy(1, 11); + cputs("the setting change to take effect."); + } + // Don't quit. Instead leave prompt asking user to reset. + while(1) { cgetc(); } +} diff --git a/ram2gs.h b/ram2gs.h new file mode 100644 index 0000000..fd1221e --- /dev/null +++ b/ram2gs.h @@ -0,0 +1,6 @@ +#ifndef RAM2GS_H +#define RAM2GS_H + +int ram2gs_main(void); + +#endif /* RAM2GS_H */ diff --git a/ram2gs_asm.h b/ram2gs_asm.h new file mode 100644 index 0000000..fa3e6ec --- /dev/null +++ b/ram2gs_asm.h @@ -0,0 +1,8 @@ +#ifndef RAM2GS_ASM_H +#define RAM2GS_ASM_H + +uint8_t __fastcall__ ram2gs_getsize(void); +uint8_t __fastcall__ ram2gs_detect(void); +uint8_t __fastcall__ ram2gs_cmd(char cmd); + +#endif /* RAM2GS_ASM_H */ diff --git a/ram2gs_asm.s b/ram2gs_asm.s new file mode 100644 index 0000000..45f95f1 --- /dev/null +++ b/ram2gs_asm.s @@ -0,0 +1,306 @@ +.setcpu "65816" +.autoimport on +.importzp sp + +.export _ram2gs_getsize +.export _ram2gs_detect +.export _ram2gs_cmd + +.macro A8 + sep #$20 ; put the 65C816 in 8-bit accumulator mode + .A8 +.endmacro + +.macro I8 + sep #$10 ; put the 65C816 in 8-bit index register mode + .I8 +.endmacro + +.macro AI8 + sep #$30 ; put the 65C816 in 8-bit accumulator and index register mode + .A8 + .I8 +.endmacro + +.macro A16 + rep #$20 ; put the 65C816 in 8-bit accumulator mode + .A16 +.endmacro + +.macro I16 + rep #$10 ; put the 65C816 in 8-bit index register mode + .I16 +.endmacro + +.macro AI16 + rep #$30 ; put the 65C816 in 8-bit accumulator and index register mode + .A16 + .I16 +.endmacro + +.segment "CODE" + +.proc _gsram_getsize: near +.A8 +.I8 + ; Preamble + sei ; Disable interrupts + clc ; Clear carry + xce ; Clear emulation bit + php ; Push status + phb ; Push bank + AI8 + + ; Store bank number at address 0xFFFF in each bank + ldy #$7F ; Start at bank 0x7F + BankSetLoop: + phy ; Push future bank + plb ; Pull bank + lda $8000 ; Get address 0xFFFF in this bank + pha ; Save old address 0xFFFF contents + tya ; A = Y + eor #$FF ; Flip all bits + tay ; Y = A + sty $8000 ; Overwrite address 0xFFFF with bank number + eor #$FF ; Flip all bits back + tay ; Y = A + dey ; Decrement bank number + cpy #$FF ; Have we wrapped around? + bne BankSetLoop ; If not, repeat + + ; Count banks with matching bank number + ldy #$00 ; Y is bank + ldx #$00 ; X is count + CountLoop: + phy ; Push future bank + plb ; Pull bank + tya ; A = Y + eor #$FF ; Flip all bits + tay ; Y = A + cpy $8000 ; Is bank num stored at address 0xFFFF? + bne AfterInc ; If not, skip increment + inx ; If so, increment bank count + AfterInc: + eor #$FF ; Flip all bits back + tay ; Y = A + pla ; Get contents to restore + sta $8000 ; Restore address 0xFFFF in this bank + iny ; Move to next bank + cpy #$80 ; Are we at bank 0x80 yet? + bne CountLoop ; If not, repeat + + ; Postamble + plb ; Restore bank + plp ; Restore status + xce ; Restore emulation bit + cli ; Enable interrupts + txa ; Transfer bank count to A + rts +.endproc + +.proc _ram2gs_getsize: near +.A8 +.I8 + ; Preamble + sei ; Disable interrupts + clc ; Clear carry + xce ; Clear emulation bit + php ; Push status + phb ; Push bank + AI8 + + ; Go to bank 3F + ldy #$3F + phy + plb + ; Save 3F/3456 + ldx $3456 + + ; Go to bank 7F + ldy #$7F + phy + plb + ; Invert 7F/3456 + lda $3456 + eor #$FF + sta $3456 + + ; Go to bank 3F + ldy #$3F + phy + plb + ; Has 3F/3456 changed? + cpx $3456 + php + + ; Go to bank 7F + ldy #$7F + phy + plb + ; Restore 7F/3456 + eor #$FF + sta $3456 + + ; Check result + ldx #$80 + plp + beq _ram2gs_getsize_return + ldx #$40 + + ; Postamble + _ram2gs_getsize_return: + plb ; Restore bank + plp ; Restore status + xce ; Restore emulation bit + cli ; Enable interrupts + txa ; Transfer bank count to A + rts +.endproc + + +.proc _unswap: near +.A8 +.I8 + ; Save current bank and accumulator + phb + pha + ; Switch to bank 0xFB + lda #$FB + pha + plb + ; Submit C1AD + lda #$C1 + sta $FFFE + lda #$AD + sta $FFFF + ; Pull and submit command + lda #$00 + sta $FFFD + ; Restore accumulator and bank and return + pla + plb + rts +.endproc + +.proc _swap: near +.A8 +.I8 + ; Save current bank and accumulator + phb + pha + ; Switch to bank 0xFB + lda #$FB + pha + plb + ; Submit C1AD + lda #$C1 + sta $FFFE + lda #$AD + sta $FFFF + ; Pull and submit command + lda #$01 + sta $FFFD + ; Restore accumulator and bank and return + pla + plb + rts +.endproc + +.proc _ram2gs_detect: near +.A8 +.I8 + ; Preamble + sei ; Disable interrupts + clc ; Clear carry + xce ; Clear emulation bit + php ; Push status + phb ; Push bank + AI8 + + ; Switch to bank 0x3F + lda #$3F + pha + plb + + ; Unswap + jsr _unswap + ; Save unswapped 3F/8000 + lda $8000 + pha + ; Swap + jsr _swap + ; Save swapped 3F/8000 + lda $8000 + pha + + ; Store 0xFF in swapped + lda #$FF + sta $8000 + ; Verify 0xFF stored + lda $8000 + cmp #$FF + bne _ram2gs_detect_fail + + ; Unswap + jsr _unswap + ; Store 0x00 in unswapped + lda #$00 + sta $8000 + ; Verify 0x00 stored + lda $8000 + cmp #$00 + bne _ram2gs_detect_fail + + ; Swap + jsr _swap + ; Verify 0xFF stored + lda $8000 + cmp #$FF + bne _ram2gs_detect_fail + + ; Get success return value and jump to postamble + ldx #$01 ; Get success falue + bne _ram2gs_detect_return ; Jump to postamble + + ; Fail + _ram2gs_detect_fail: + ldx #$00 ; Get fail value + + ; Postamble + _ram2gs_detect_return: + jsr _swap ; Swap + pla ; Get value to restore to swapped bank 3F + sta $8000 ; Restore + jsr _unswap ; Unswap + pla ; Get value to restore to unswapped bank 3F + sta $8000 ; Restore + txa ; Put return value in accumulator + plb ; Restore bank + plp ; Restore status + xce ; Restore emulation bit + cli ; Enable interrupts + rts +.endproc + +.proc _ram2gs_cmd: near +.A8 +.I8 + ; Save current bank and command in accumulator + phb + pha + ; Switch to bank 0xFB + lda #$FB + pha + plb + ; Submit C1AD + lda #$C1 + sta $FFFE + lda #$AD + sta $FFFF + ; Pull and submit command + pla + sta $FFFD + ; Restore bank and return + plb + rts +.endproc diff --git a/util.c b/util.c new file mode 100644 index 0000000..a597700 --- /dev/null +++ b/util.c @@ -0,0 +1,52 @@ +#include "util.h" + +#include +#include +#include + +#define PB0 ((char*)0xC061) +#define PB1 ((char*)0xC062) +char read_applekey(void) { return ((*PB0) | (*PB1)) & 0x80; } + +#define VBL ((signed char*)0xC019) +#define SPIN_HALFCYCLES 3 +#define SPIN_FRAMESPERCHAR 4 +void spin(uint8_t x, uint8_t y) { + char i; + + // 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); + 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); +} \ No newline at end of file diff --git a/util.h b/util.h new file mode 100644 index 0000000..a591b63 --- /dev/null +++ b/util.h @@ -0,0 +1,12 @@ +#include + +#ifndef UTIL_H +#define UTIL_H + +#define true 1 +#define false 0 + +char read_applekey(void); +void spin(uint8_t x, uint8_t y); + +#endif /* UTIL_H */ \ No newline at end of file