/* Soft64View.m - Commodore 64 Emulator for NeXT */ /* Neil Franklin, Morgenweg 8, 8404 Winterthur */ /* version (last revision) 2.5.93 */ /* Soft64View Object */ #import "Soft64View.h" #import /* NXApp */ #import @implementation Soft64View - initFrame:(const NXRect *)frameRect { [super initFrame: frameRect]; [self setClipping: NO]; [self drawInSuperview]; auto_init(); c64_init(); Running = 1; [NXApp setDelegate: self]; return (self); } - free { timer_stop(); return ([super free]); } - drawSelf:(const NXRect *)rects :(int)rectCount { return (self); } - appDidInit:sender { [[self window] makeKeyAndOrderFront:self]; [[self window] makeFirstResponder:self]; return (self); } - (BOOL) acceptsFirstResponder { return (YES); } - becomeFirstResponder { if (Running) timer_start(self); return (self); } - resignFirstResponder { timer_stop(); return (self); } - (BOOL)appAcceptsAnotherFile:sender { if (!auto_load_done) { auto_load_done = 1; return (YES); } return (NO); } - (int)app:sender openFile:(const char *)filename type:(const char *)aType { auto_load(filename); return (YES); } - keyDown:(NXEvent *)theEvent { DATA Key = (DATA) theEvent->data.key.charCode; if (theEvent->data.key.charSet == NX_SYMBOLSET) { if (Key >= 0xAC && Key <= 0xAF) { /* NeXT arrow keys */ Key = 0x11|((Key&0x02)?0x00:0x80)|((Key&0x01)?0x00:0x0D); keyb_down(Key); } } if (theEvent->data.key.charSet == NX_ASCIISET) { if (Key == 0x7F) { /* ASCII Delete to C64 Delete */ Key = 0x14; } if (Key == 0xA3) { /* NeXT Pound sign to C64 Pound */ Key = 0x5C; } if (Key >= 0x7B) { /* drop NeXT keys w no C64 eqivalent */ return (self); } if (Key >= 0x41 && Key <= 0x5A) { Key |= 0x80; } /* ASCII shifted to C64 shifted */ if (Key >= 0x61 && Key <= 0x7A) { Key &= 0xDF; } /* ASCII unshifted to C64 unshifted */ keyb_down(Key); } return (self); } - keyUp:(NXEvent *)theEvent { keyb_up(); return (self); } - openprg:sender { id Open = [OpenPanel new]; char *Types[] = { "prg", NULL }; [Open runModalForTypes: Types]; if ([Open filename]) { auto_load([Open filename]); } return (self); } - freeze64:sender { timer_stop(); Running = 1-Running; /* toggle sys_c64running */ if (Running) timer_start(self); return (self); } - reset64:sender { timer_stop(); auto_init(); c64_init(); /* reinitialise */ Running = 1; timer_start(self); return (self); } @end /* timer for executing c64_slice() */ #import /* DPSAdd/RemoveTimedEntry() */ #import /* NX_BASETHRESHOLD */ DPSTimedEntry TimedEntry; void timer_start(id Self) { TimedEntry = DPSAddTimedEntry(1.0/68.0, (DPSTimedEntryProc)timer_entry, (void *)Self, NX_BASETHRESHOLD); } void timer_stop(void) { if (TimedEntry != NULL) { DPSRemoveTimedEntry(TimedEntry); TimedEntry = NULL; } } void timer_entry(DPSTimedEntry teNumber, double now, char *userData) { id Self = (id)userData; [Self lockFocus]; auto_slice(); c64_slice(230); /* 230*64us in 1/68s */ [Self unlockFocus]; } /* autotyper - types automatically instead of user */ #include /* for str*(), rindex() */ #include /* for MAXPATHLEN */ DATA AutoKeys[MAXPATHLEN+1], *AutoKey; int AutoKeystep; void auto_init(void) { auto_type(""); } void auto_type(DATA *Keys) { strcpy(AutoKeys, Keys); AutoKey = AutoKeys; AutoKeystep = 0; } void auto_load(const DATA *FileName) { DATA LoadCommand[MAXPATHLEN+1], *Key; strcpy(LoadCommand, "load \""); strcat(LoadCommand, FileName); *rindex(LoadCommand, '.') = '\0'; /* cut off .prg */ strcat(LoadCommand, "\",8,1\r"); /* only carriage return for C64 */ for (Key = LoadCommand; *Key; Key++) { if (*Key >= 0x41 && *Key <= 0x5A) { *Key |= 0x80; } /* ASCII shifted to C64 shifted */ if (*Key >= 0x61 && *Key <= 0x7A) { *Key &= 0xDF; } } /* ASCII unshifted to C64 unshifted */ auto_type(LoadCommand); } void auto_slice(void) { if (*AutoKey) { if (AutoKeystep == 0) { keyb_down(*AutoKey++); } if (AutoKeystep == 2) { keyb_up(); } if (AutoKeystep++ == 4) { AutoKeystep = 0; } } } /* entire C64 */ #import /* NXRunAlertPanel() */ void c64_init(void) { mpu_init(); mem_init(); vic_init(); sid_init(); cia1_init(); cia2_init(); } void c64_slice(int ScreenLines) { for (; ScreenLines >= 0; ScreenLines--) { mpu_64us(vic_64us()); /* VIC determines MPU cycles/64us */ cia1_64us(); cia2_64us(); } } void c64_abort(char *ErrorMessage) { NXRunAlertPanel(NULL, "%s,\nSoft 64 will terminate", NULL, NULL, NULL, ErrorMessage); exit(1); } /* Processor MPU 6510 */ DATA A, X, Y, SP, FlagN, FlagV, FlagZ, FlagC, TempData; ADDR PC, TempAddr; int Interrupt, IntMask, TempInt; #define MEM_IMMED PC++ #define MEM_DIREC (ADDR)mem_rd(PC++) #define MEM_DIREX (ADDR)(mem_rd(PC++)+X) #define MEM_DIREY (ADDR)(mem_rd(PC++)+Y) #define MEM_ABSOL (ADDR)mem_rd(PC++)+((ADDR)mem_rd(PC++)<<8) #define MEM_ABSOX (ADDR)mem_rd(PC++)+((ADDR)mem_rd(PC++)<<8)+(ADDR)X #define MEM_ABSOY (ADDR)mem_rd(PC++)+((ADDR)mem_rd(PC++)<<8)+(ADDR)Y #define MEM_INDIX (ADDR)mem_rd((ADDR)(mem_rd(PC)+X))+ \ ((ADDR)mem_rd((ADDR)(mem_rd(PC++)+X+1))<<8) #define MEM_INDIY (ADDR)mem_rd((ADDR)mem_rd(PC))+ \ ((ADDR)mem_rd((ADDR)mem_rd(PC++)+1)<<8)+(ADDR)Y #define MEM_SPINC 0x0100+(ADDR)(++SP) #define MEM_SPDEC 0x0100+(ADDR)(SP--) void mpu_init(void) { mem_wr(0x0001, 0x3F); /* all ones on processor port */ Interrupt = IntMask = 0x1F; } #define MPU_INT_VIC 0x01 #define MPU_INT_CIA1 0x02 #define MPU_INT_CIA2 0x08 void mpu_interrupt(int Source, int Set) { if (Set) Interrupt |= Source; else Interrupt &= ~Source; } void mpu_php(void) { mem_wr(MEM_SPDEC, (FlagN&0x80)|(FlagV&0x40)| 0x20|((Interrupt&0x04)?0x10:0)| 0x00|((IntMask&0x03)?0:0x04)| (FlagZ?0:0x02)|(FlagC&0x01)); } void mpu_plp(void) { FlagC = FlagV = FlagN = mem_rd(MEM_SPINC); if (FlagN&0x08) c64_abort("decimal arithmetic not supported"); IntMask = FlagN&0x04 ? 0x1C : 0x1F; FlagZ = FlagN&0x02 ? 0 : 1; } #include void trace(void) { if (PC == 0xEAAE) { printf("A=%2X X=%2X Y=%2X SP=%2X %c%c%c%c PC=%4X next OP=%2X\n", (unsigned int)A, (unsigned int)X, (unsigned int)Y, (unsigned int)SP, FlagN&0x80 ? 0x4E : 0x6E, FlagV&0x40 ? 0x56 : 0x76, !FlagZ ? 0x5A : 0x7A, FlagC&0x01 ? 0x43 : 0x63, (unsigned int)PC, (unsigned int)mem_rd(PC)); } } void mpu_64us(int ClockCycles) { for(; ClockCycles >= 0; ClockCycles -= 3) { if (Interrupt&IntMask) { mem_wr(MEM_SPDEC,(DATA)(PC>>8)); mem_wr(MEM_SPDEC,(DATA)PC); mpu_php(); if (Interrupt&0x10) { Interrupt = 0; PC = 0xFFFC; } else { if (Interrupt&0x08) { Interrupt &= 0x17; PC = 0xFFFA; } else { Interrupt &= 0x1B; PC = 0xFFFE; } } PC = MEM_ABSOL; IntMask = 0x1C; } /*trace();*/ switch (mem_rd(PC++)) { /* NOP JMP JMP() BRK JSR RTI RTS */ case 0xEA: break; case 0x4C: PC = MEM_ABSOL; break; case 0x6C: TempAddr = MEM_ABSOL; PC = (ADDR)mem_rd(TempAddr); TempAddr = (TempAddr&0xFF00)|(ADDR)((DATA)TempAddr+1); PC += (ADDR)mem_rd(TempAddr)<<8; break; case 0x00: Interrupt |= 0x04; break; case 0x20: PC += 1; mem_wr(MEM_SPDEC, (DATA)(PC>>8)); mem_wr(MEM_SPDEC, (DATA)PC); PC -= 1; PC = MEM_ABSOL; break; case 0x40: mpu_plp(); PC = (ADDR)mem_rd(MEM_SPINC)+ ((ADDR)mem_rd(MEM_SPINC)<<8); break; case 0x60: PC = (ADDR)mem_rd(MEM_SPINC)+ ((ADDR)mem_rd(MEM_SPINC)<<8)+1; break; /* BPL BMI BVC BVS BCC BCS BNE BEQ */ case 0x10: PC += FlagN&0x80 ? 1 : (ADDR)((char)mem_rd(PC))+1; break; case 0x30: PC += FlagN&0x80 ? (ADDR)((char)mem_rd(PC))+1 : 1; break; case 0x50: PC += FlagV&0x40 ? 1 : (ADDR)((char)mem_rd(PC))+1; break; case 0x70: PC += FlagV&0x40 ? (ADDR)((char)mem_rd(PC))+1 : 1; break; case 0x90: PC += FlagC&0x01 ? 1 : (ADDR)((char)mem_rd(PC))+1; break; case 0xB0: PC += FlagC&0x01 ? (ADDR)((char)mem_rd(PC))+1 : 1; break; case 0xD0: PC += !FlagZ ? 1 : (ADDR)((char)mem_rd(PC))+1; break; case 0xF0: PC += !FlagZ ? (ADDR)((char)mem_rd(PC))+1 : 1; break; /* CLC SEC CLI SEI CLV CLD SED */ case 0x18: FlagC = 0; break; case 0x38: FlagC = 1; break; case 0x58: IntMask = 0x1F; break; /* CLI: all allowed */ case 0x78: IntMask = 0x1C; break; /* SEI: IRQ suppressed */ case 0xB8: FlagV = 0; break; case 0xD8: break; /* no decimal flag */ case 0xF8: c64_abort("decimal arithmetic not supported"); break; /* PHP PLP PHA PLA TXS TSX */ case 0x08: mpu_php(); break; case 0x28: mpu_plp(); break; case 0x48: mem_wr(MEM_SPDEC, A); break; case 0x68: FlagZ = FlagN = A = mem_rd(MEM_SPINC); break; case 0x9A: SP = X; break; case 0xBA: FlagZ = FlagN = X = SP; break; /* ORA */ case 0x01: FlagZ = FlagN = A |= mem_rd(MEM_INDIX); break; case 0x05: FlagZ = FlagN = A |= mem_rd(MEM_DIREC); break; case 0x09: FlagZ = FlagN = A |= mem_rd(MEM_IMMED); break; case 0x0D: FlagZ = FlagN = A |= mem_rd(MEM_ABSOL); break; case 0x11: FlagZ = FlagN = A |= mem_rd(MEM_INDIY); break; case 0x15: FlagZ = FlagN = A |= mem_rd(MEM_DIREX); break; case 0x19: FlagZ = FlagN = A |= mem_rd(MEM_ABSOY); break; case 0x1D: FlagZ = FlagN = A |= mem_rd(MEM_ABSOX); break; /* AND */ case 0x21: FlagZ = FlagN = A &= mem_rd(MEM_INDIX); break; case 0x25: FlagZ = FlagN = A &= mem_rd(MEM_DIREC); break; case 0x29: FlagZ = FlagN = A &= mem_rd(MEM_IMMED); break; case 0x2D: FlagZ = FlagN = A &= mem_rd(MEM_ABSOL); break; case 0x31: FlagZ = FlagN = A &= mem_rd(MEM_INDIY); break; case 0x35: FlagZ = FlagN = A &= mem_rd(MEM_DIREX); break; case 0x39: FlagZ = FlagN = A &= mem_rd(MEM_ABSOY); break; case 0x3D: FlagZ = FlagN = A &= mem_rd(MEM_ABSOX); break; /* EOR */ case 0x41: FlagZ = FlagN = A ^= mem_rd(MEM_INDIX); break; case 0x45: FlagZ = FlagN = A ^= mem_rd(MEM_DIREC); break; case 0x49: FlagZ = FlagN = A ^= mem_rd(MEM_IMMED); break; case 0x4D: FlagZ = FlagN = A ^= mem_rd(MEM_ABSOL); break; case 0x51: FlagZ = FlagN = A ^= mem_rd(MEM_INDIY); break; case 0x55: FlagZ = FlagN = A ^= mem_rd(MEM_DIREX); break; case 0x59: FlagZ = FlagN = A ^= mem_rd(MEM_ABSOY); break; case 0x5D: FlagZ = FlagN = A ^= mem_rd(MEM_ABSOX); break; /* ADC */ case 0x61: TempInt = (int)A+(int)mem_rd(MEM_INDIX)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; case 0x65: TempInt = (int)A+(int)mem_rd(MEM_DIREC)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; case 0x69: TempInt = (int)A+(int)mem_rd(MEM_IMMED)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; case 0x6D: TempInt = (int)A+(int)mem_rd(MEM_ABSOL)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; case 0x71: TempInt = (int)A+(int)mem_rd(MEM_INDIY)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; case 0x75: TempInt = (int)A+(int)mem_rd(MEM_DIREX)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; case 0x79: TempInt = (int)A+(int)mem_rd(MEM_ABSOY)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; case 0x7D: TempInt = (int)A+(int)mem_rd(MEM_ABSOX)+(int)(FlagC&0x01); FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8); break; /* STA */ case 0x81: mem_wr(MEM_INDIX, A); break; case 0x85: mem_wr(MEM_DIREC, A); break; case 0x89: mem_wr(MEM_IMMED, A); break; case 0x8D: mem_wr(MEM_ABSOL, A); break; case 0x91: mem_wr(MEM_INDIY, A); break; case 0x95: mem_wr(MEM_DIREX, A); break; case 0x99: mem_wr(MEM_ABSOY, A); break; case 0x9D: mem_wr(MEM_ABSOX, A); break; /* LDA */ case 0xA1: FlagZ = FlagN = A = mem_rd(MEM_INDIX); break; case 0xA5: FlagZ = FlagN = A = mem_rd(MEM_DIREC); break; case 0xA9: FlagZ = FlagN = A = mem_rd(MEM_IMMED); break; case 0xAD: FlagZ = FlagN = A = mem_rd(MEM_ABSOL); break; case 0xB1: FlagZ = FlagN = A = mem_rd(MEM_INDIY); break; case 0xB5: FlagZ = FlagN = A = mem_rd(MEM_DIREX); break; case 0xB9: FlagZ = FlagN = A = mem_rd(MEM_ABSOY); break; case 0xBD: FlagZ = FlagN = A = mem_rd(MEM_ABSOX); break; /* CMP */ case 0xC1: TempInt = (int)A-(int)mem_rd(MEM_INDIX); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xC5: TempInt = (int)A-(int)mem_rd(MEM_DIREC); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xC9: TempInt = (int)A-(int)mem_rd(MEM_IMMED); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xCD: TempInt = (int)A-(int)mem_rd(MEM_ABSOL); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xD1: TempInt = (int)A-(int)mem_rd(MEM_INDIY); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xD5: TempInt = (int)A-(int)mem_rd(MEM_DIREX); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xD9: TempInt = (int)A-(int)mem_rd(MEM_ABSOY); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xDD: TempInt = (int)A-(int)mem_rd(MEM_ABSOX); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; /* SBC */ case 0xE1: TempInt =(int)A-(int)mem_rd(MEM_INDIX)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xE5: TempInt =(int)A-(int)mem_rd(MEM_DIREC)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xE9: TempInt =(int)A-(int)mem_rd(MEM_IMMED)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xED: TempInt =(int)A-(int)mem_rd(MEM_ABSOL)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xF1: TempInt =(int)A-(int)mem_rd(MEM_INDIY)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xF5: TempInt =(int)A-(int)mem_rd(MEM_DIREX)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xF9: TempInt =(int)A-(int)mem_rd(MEM_ABSOY)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xFD: TempInt =(int)A-(int)mem_rd(MEM_ABSOX)+(int)(FlagC&0x01)-1; FlagV = (A^(DATA)TempInt)>>1; FlagZ = FlagN = A = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; /* BIT */ case 0x24: FlagV = FlagN = mem_rd(MEM_DIREC); FlagZ = FlagN&A; break; case 0x2C: FlagV = FlagN = mem_rd(MEM_ABSOL); FlagZ = FlagN&A; break; /* ASL */ case 0x06: TempAddr = MEM_DIREC;FlagC = (TempData = mem_rd(TempAddr))>>7; mem_wr(TempAddr, FlagZ = FlagN = TempData<<1); break; case 0x0A: FlagC = A>>7; A = FlagZ = FlagN = A<<1; break; case 0x0E: TempAddr = MEM_ABSOL;FlagC = (TempData = mem_rd(TempAddr))>>7; mem_wr(TempAddr, FlagZ = FlagN = TempData<<1); break; case 0x16: TempAddr = MEM_DIREX;FlagC = (TempData = mem_rd(TempAddr))>>7; mem_wr(TempAddr, FlagZ = FlagN = TempData<<1); break; case 0x1E: TempAddr = MEM_ABSOX;FlagC = (TempData = mem_rd(TempAddr))>>7; mem_wr(TempAddr, FlagZ = FlagN = TempData<<1); break; /* ROL */ case 0x26: TempAddr = MEM_DIREC; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagZ = FlagN = (TempData<<1)|(FlagC&0x01)); FlagC = TempData>>7; break; case 0x2A: TempData = A; A = FlagZ = FlagN = (TempData<<1)|(FlagC&0x01); FlagC = TempData>>7; break; case 0x2E: TempAddr = MEM_ABSOL; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagZ = FlagN = (TempData<<1)|(FlagC&0x01)); FlagC = TempData>>7; break; case 0x36: TempAddr = MEM_DIREX; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagZ = FlagN = (TempData<<1)|(FlagC&0x01)); FlagC = TempData>>7; break; case 0x3E: TempAddr = MEM_ABSOX; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagZ = FlagN = (TempData<<1)|(FlagC&0x01)); FlagC = TempData>>7; break; /* LSR */ case 0x46: TempAddr = MEM_DIREC; FlagC = TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = TempData>>1); break; case 0x4A: FlagC = A; A = FlagN = FlagZ = A>>1; break; case 0x4E: TempAddr = MEM_ABSOL; FlagC = TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = TempData>>1); break; case 0x56: TempAddr = MEM_DIREX; FlagC = TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = TempData>>1); break; case 0x5E: TempAddr = MEM_ABSOX; FlagC = TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = TempData>>1); break; /* ROR */ case 0x66: TempAddr = MEM_DIREC; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = (TempData>>1)|(FlagC<<7)); FlagC = TempData; break; case 0x6A: TempData = A; A = FlagN = FlagZ = (TempData>>1)|(FlagC<<7); FlagC = TempData; break; case 0x6E: TempAddr = MEM_ABSOL; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = (TempData>>1)|(FlagC<<7)); FlagC = TempData; break; case 0x76: TempAddr = MEM_DIREX; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = (TempData>>1)|(FlagC<<7)); FlagC = TempData; break; case 0x7E: TempAddr = MEM_ABSOX; TempData = mem_rd(TempAddr); mem_wr(TempAddr, FlagN = FlagZ = (TempData>>1)|(FlagC<<7)); FlagC = TempData; break; /* DEC */ case 0xC6: TempAddr = MEM_DIREC; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)-1); break; case 0xCE: TempAddr = MEM_ABSOL; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)-1); break; case 0xD6: TempAddr = MEM_DIREX; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)-1); break; case 0xDE: TempAddr = MEM_ABSOX; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)-1); break; /* INC */ case 0xE6: TempAddr = MEM_DIREC; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)+1); break; case 0xEE: TempAddr = MEM_ABSOL; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)+1); break; case 0xF6: TempAddr = MEM_DIREX; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)+1); break; case 0xFE: TempAddr = MEM_ABSOX; mem_wr(TempAddr, FlagZ = FlagN = mem_rd(TempAddr)+1); break; /* STX TXA */ case 0x86: mem_wr(MEM_DIREC, X); break; case 0x8A: FlagZ = FlagN = A = X; break; case 0x8E: mem_wr(MEM_ABSOL, X); break; case 0x96: mem_wr(MEM_DIREY, X); break; /* LDX TAX */ case 0xA2: FlagZ = FlagN = X = mem_rd(MEM_IMMED); break; case 0xA6: FlagZ = FlagN = X = mem_rd(MEM_DIREC); break; case 0xAA: FlagZ = FlagN = X = A; break; case 0xAE: FlagZ = FlagN = X = mem_rd(MEM_ABSOL); break; case 0xB6: FlagZ = FlagN = X = mem_rd(MEM_DIREY); break; case 0xBE: FlagZ = FlagN = X = mem_rd(MEM_ABSOY); break; /* CPX */ case 0xE0: TempInt = (int)X-(int)mem_rd(MEM_IMMED); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xE4: TempInt = (int)X-(int)mem_rd(MEM_DIREC); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xEC: TempInt = (int)X-(int)mem_rd(MEM_ABSOL); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; /* DEX INX */ case 0xCA: FlagZ = FlagN = X -= 1; break; case 0xE8: FlagZ = FlagN = X += 1; break; /* STY TYA */ case 0x84: mem_wr(MEM_DIREC, Y); break; case 0x98: FlagZ = FlagN = A = Y; break; case 0x8C: mem_wr(MEM_ABSOL, Y); break; case 0x94: mem_wr(MEM_DIREX, Y); break; /* LDY TAY */ case 0xA0: FlagZ = FlagN = Y = mem_rd(MEM_IMMED); break; case 0xA4: FlagZ = FlagN = Y = mem_rd(MEM_DIREC); break; case 0xA8: FlagZ = FlagN = Y = A; break; case 0xAC: FlagZ = FlagN = Y = mem_rd(MEM_ABSOL); break; case 0xB4: FlagZ = FlagN = Y = mem_rd(MEM_DIREX); break; case 0xBC: FlagZ = FlagN = Y = mem_rd(MEM_ABSOX); break; /* CPY */ case 0xC0: TempInt = (int)Y-(int)mem_rd(MEM_IMMED); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xC4: TempInt = (int)Y-(int)mem_rd(MEM_DIREC); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; case 0xCC: TempInt = (int)Y-(int)mem_rd(MEM_ABSOL); FlagZ = FlagN = (DATA)TempInt; FlagC = (DATA)(TempInt>>8)+1; break; /* DEY INY */ case 0x88: FlagZ = FlagN = Y -= 1; break; case 0xC8: FlagZ = FlagN = Y += 1; break; /* illegal opcodes */ default: { c64_abort("undocumented opcodes not supported"); } } } } /* Memory and Address Manager */ #import #include /* for MAXPATHLEN */ #include DATA Ram[0x10000], Cram[0x0400]; /* Main and Character RAMs */ DATA Brom[0x2000], Crom[0x1000], Krom[0x2000];/* Basic, Char and Kernal ROMs */ void mem_init(void) { mem_loadrom("basic", Brom, 0x2000); mem_loadrom("char", Crom, 0x1000); mem_loadrom("kernal", Krom, 0x2000); } void mem_loadrom(char *Name, DATA *Mem, int Length) { NXBundle *bundle; char FileName[MAXPATHLEN+1]; FILE *File; bundle = [NXBundle mainBundle]; [bundle getPath:FileName forResource:Name ofType:".rom"]; File = fopen(FileName, "rb"); fread(Mem, sizeof(DATA), Length, File); fclose(File); } DATA mem_rd(ADDR Addr) { switch (Addr>>12) { case 0xA: case 0xB: { if (Ram[0x0001]&0x03 == 0x03) { /* Address Manager control pins */ return (Brom[Addr&0x1FFF]); } } case 0xD: { if (Ram[0x0001]&0x03) { if (Ram[0x0001]&0x04) { /* charen pin */ switch ((Addr>>10)&0x0003) { /* first 74LS139 */ case 0: { return (vic_rd(Addr&0x003F)); } case 1: { return (sid_rd(Addr&0x001F)); } case 2: { return (Cram[Addr&0x03FF]|0xF0); } /* 0xF0 for open coll. bus */ case 3: { switch((Addr>>8)&0x0003) {/* second 74LS139 */ case 0: { return (cia1_rd(Addr&0x000F)); } case 1: { return (cia2_rd(Addr&0x000F)); } default: { return (0xFF); } } } } } /* 0xFF for open collector bus */ else { return (Crom[Addr&0x0FFF]); } } } case 0xE: case 0xF: { if (Ram[0x0001]&0x02) { return (Krom[Addr&0x1FFF]); } } } return (Ram[Addr]); } void mem_wr(ADDR Addr, DATA Data) { if (Addr>>12 == 0xD && Ram[0x0001]&0x07 >= 0x05) { switch ((Addr>>10)&0x0003) { case 0: { vic_wr(Addr&0x003F, Data); return; } case 1: { sid_wr(Addr&0x001F, Data); return; } case 2: { Cram[Addr&0x03FF] = Data&0x0F; return; } /* &0x0F to speed up VIC */ case 3: { switch ((Addr>>8)&0x0003) { case 0: { cia1_wr(Addr&0x000F, Data); return; } case 1: { cia2_wr(Addr&0x000F, Data); return; } default: { return; } } } } } Ram[Addr] = Data; } /* VIC */ DATA Vic[64]; /* 64 Bytes to prevent errors */ int VicVideoOn, VicLine, VicIrqLine; ADDR VicChargenAddr, VicVideoAddr; DATA *VicChargen; void vic_init(void) { Krom[0x1CEF] = 0xEA; /* blank out Kernal ROM bug */ Krom[0x1CF0] = 0xEA; /* else 38 char mode will be set */ Krom[0x1CF1] = 0xEA; /* leading to c64_abort */ vic_draw_init(); } DATA vic_rd(ADDR Addr) { switch(Addr) { case 17: { /* cut up scan line information */ return ((Vic[17]&0x7F)|((VicLine&0x0100)>>1)); } case 18: { return ((DATA)VicLine); } case 19: case 20: { c64_abort("lightpen not supported"); } case 30: case 31: { c64_abort("sprite collision detection not supported"); } default: { return (Vic[Addr]); } } } void vic_wr(ADDR Addr, DATA Data) { switch(Addr) { case 17: { /* assemble scan line information */ if (Data & 0x40) c64_abort("extended color mode not supported"); if (Data & 0x20) c64_abort("bitmap graphic modes not supported"); if (!(Data & 0x08)) c64_abort("24 lines and vertical scroll not supported"); VicVideoOn = Data&0x10; VicIrqLine = (VicIrqLine&0xFF)|(Data&0x80 ? 0 : 256); break; } case 18: { VicIrqLine = (VicIrqLine&0x100)|(int)Data; break; } case 21: { if (Data) c64_abort("sprites not supported"); break; } case 22: { if (Data & 0x10) c64_abort("multicolor modes not supported"); if (!(Data & 0x08)) c64_abort("38 columns and horizontal scroll not supported"); break; } case 24: { ADDR BaseAddr = 0xC000-((ADDR)cia2_rd(0) << 14); VicChargenAddr = BaseAddr+(((ADDR)Data << 10) & 0x3800); VicVideoAddr = BaseAddr+(((ADDR)Data << 6) & 0x3C00); if ((VicChargenAddr & 0x7000) == 0x1000) { VicChargen = Crom+(VicChargenAddr&0x0FFF); } else { VicChargen = Ram+VicChargenAddr; } break; } case 25: case 26: { Vic[Addr] = Data; mpu_interrupt(MPU_INT_VIC, Vic[25]&Vic[26]); } } if (Addr&0x0020) Data = Data&0x0F; /* color registers */ Vic[Addr] = Data; } int vic_64us(void) { vic_draw(VicLine); if (VicLine == VicIrqLine) { Vic[25] |= 0x81; mpu_interrupt(MPU_INT_VIC, Vic[25]&Vic[26]); } if ((VicLine += 1) == 312) { VicLine = 0; } return ((VicLine < 200 && VicVideoOn) ? 24 : 63); } /* MPU cycles/64us */ #import /* NXDrawBitmap() */ #import /* PS*() */ DATA VicDPat[8000], VicDColor[8000], VicDBack[200], VicDExt[312]; unsigned int VicDrawC[16] = { 0x000000FF, 0xFFFFFFFF, 0xFF0000FF, 0x00FFFFFF, 0xFF00FFFF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0xFF7F00FF, 0x7F0000FF, 0xFF7F7FFF, 0x3F3F3FFF, 0x7F7F7FFF, 0x7FFF7FFF, 0x7F7FFFFF, 0xCFCFCFFF }; float VicDrawCR[16] = { 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.5, 1.0, 0.25, 0.5, 0.5, 0.5, 0.75 }; float VicDrawCG[16] = { 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.5, 0.0, 0.5, 0.25, 0.5, 1.0, 0.5, 0.75 }; float VicDrawCB[16] = { 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5, 0.25, 0.5, 0.5, 1.0, 0.75 }; void vic_draw_init(void) { int Y; for (Y = 0; Y < 200; Y++) { /* guaranteed wrong color value */ VicDBack[Y] = 0x10; } /* will lead to complete redraw */ for (Y = 0; Y < 312; Y++) { VicDExt[Y] = 0x10; } } void vic_draw(int Y) { if (Y < 200 && VicVideoOn) { static int NextPart; int X, FirstX, LastX, VidLine, ColLine; DATA *Chargen; if (Y&3) { /* reduce processor load */ return; } if (Y == 0) { if ((NextPart += 50) == 200) { NextPart = 0; } } Y = (Y>>2)+NextPart; /* advance at lower speed */ VidLine = (int)VicVideoAddr+40*(Y>>3); ColLine = 40*(Y>>3); Chargen = VicChargen+(Y&7); FirstX = 40; LastX = 0; /* nothing to be drawn */ for (X = 0; X < 40; X++) { if (*(Chargen+8*Ram[VidLine+X]) != VicDPat[40*Y+X] || Cram[ColLine+X] != VicDColor[40*Y+X]) { VicDPat[40*Y+X] = *(Chargen+8*Ram[VidLine+X]); VicDColor[40*Y+X] = Cram[ColLine+X]; if (FirstX == 40) FirstX = X; /* range to be drawn */ LastX = X+1; } } if (Vic[33] != VicDBack[Y]) { VicDBack[Y] = Vic[33]; FirstX = 0; LastX = 40; } /* everything to be drawn */ if (FirstX < 40) { NXRect Rect; unsigned int Pattern[2*40][16]; unsigned char *Data[5]; Rect.origin.x = (float)(50+16*FirstX); Rect.origin.y = (float)(448-2*Y); Rect.size.width = (float)((LastX-FirstX)*16); Rect.size.height = 2.0; for (X = FirstX; X < LastX; X++) { DATA CharPattern = VicDPat[40*Y+X]; int Pixel; for (Pixel = 0; Pixel < 16; Pixel += 2) { Pattern[X-FirstX ][Pixel] = Pattern[X-FirstX ][Pixel+1] = Pattern[X-FirstX+LastX-FirstX][Pixel] = Pattern[X-FirstX+LastX-FirstX][Pixel+1] = VicDrawC[(CharPattern&0x80) ? VicDColor[40*Y+X] : Vic[33]]; CharPattern = CharPattern<<1; } } Data[0] = (unsigned char *)Pattern; NXDrawBitmap(&Rect, (LastX-FirstX)*16, 2, 8, 4, 4, (LastX-FirstX)*64, 0, 1, NX_RGBColorSpace, Data); } if (Vic[32] != VicDExt[Y]) { VicDExt[Y] = Vic[32]; PSsetrgbcolor(VicDrawCR[Vic[32]],VicDrawCG[Vic[32]],VicDrawCB[Vic[32]]); PSrectfill(0.0, (float)(448-2*Y), 50.0, 2.0); PSrectfill(690.0, (float)(448-2*Y), 50.0, 2.0); } } else { if (Vic[32] != VicDExt[Y]) { VicDExt[Y] = Vic[32]; if (Y >= 287) Y -= 312; if (Y >= 225) return; PSsetrgbcolor(VicDrawCR[Vic[32]],VicDrawCG[Vic[32]],VicDrawCB[Vic[32]]); PSrectfill(0.0, (float)(448-2*Y), 740.0, 2.0); } } } /* SID */ #include /* for rand() */ void sid_init(void) { } DATA sid_rd(ADDR Addr) { switch (Addr) { case 25: case 26: { c64_abort("paddles not supported"); } case 27: { return ((DATA)rand()); } case 28: { return ((DATA)rand()); } default: { return (0xFF); } } } void sid_wr(ADDR Addr, DATA Data) { } /* SID does nothing */ /* CIA1 */ DATA Cia1Pra, Cia1Prb, Cia1Ddra, Cia1Ddrb; int Cia1Tima, Cia1Timast, Cia1Timb, Cia1Timbst; DATA Cia1Icrd, Cia1Icrm, Cia1Cra, Cia1Crb; void cia1_init(void) { keyb_init(); } DATA cia1_rd(ADDR Addr) { switch(Addr) { case 0: { return (0xFF); } /* joystick not supported */ case 1: { return (keyb_rd(Cia1Pra)); } /* joystick not supported */ case 2: { return (Cia1Ddra); } case 3: { return (Cia1Ddrb); } case 4: { return ((DATA)Cia1Tima); } case 5: { return ((DATA)(Cia1Tima>>8)); } case 6: { return ((DATA)Cia1Timb); } case 7: { return ((DATA)(Cia1Timb>>8)); } case 8: case 9: case 10: case 11: { c64_abort("real time clock not supported"); } case 12: { c64_abort("SDR not supported"); } case 13: { DATA Temp = Cia1Icrd; Cia1Icrd = 0; mpu_interrupt(MPU_INT_CIA1, 0); if (Temp) return (Temp|0x80); else return (0x00); } case 14: { return (Cia1Cra); } case 15: { return (Cia1Crb); } } return (0xFF); } /* for the compilers sake */ void cia1_wr(ADDR Addr, DATA Data) { switch(Addr) { case 0: { Cia1Pra = Data; return; } case 1: { Cia1Prb = Data; return; } case 2: { Cia1Ddra = Data; return; } case 3: { Cia1Ddrb = Data; return; } case 4: { /* timers run in increments of 64 */ Cia1Timast = (Cia1Timast&0xFF00)|(int)(Data&0xC0); return; } case 5: { Cia1Timast = (Cia1Timast&0x00FF)|(int)(Data<<8); return; } case 6: { Cia1Timbst = (Cia1Timbst&0xFF00)|(int)(Data&0xC0); return; } case 7: { Cia1Timbst = (Cia1Timbst&0x00FF)|(int)(Data<<8); return; } case 8: case 9: case 10: case 11: { c64_abort("real time clock not supported"); } case 12: { c64_abort("SDR not supported"); } case 13: { if (Data&0x80) Cia1Icrm |= Data; else Cia1Icrm &= ~Data; mpu_interrupt(MPU_INT_CIA1, Cia1Icrd&Cia1Icrm); return; } case 14: { if (Data&0x10) Cia1Tima = Cia1Timast; Cia1Cra = Data; return; } case 15: { if (Data&0x20) c64_abort("timer b counts timer a not supported"); if (Data&0x10) Cia1Timb = Cia1Timbst; Cia1Crb = Data; return; } } } void cia1_64us(void) { if (Cia1Cra&1) { if ((Cia1Tima -= 64) == 0) { Cia1Icrd |= 0x01; mpu_interrupt(MPU_INT_CIA1, Cia1Icrd&Cia1Icrm); Cia1Tima = Cia1Timast; if (Cia1Cra&0x08) { Cia1Cra &= 0xFE; } } } if (Cia1Crb&1) { if ((Cia1Timb -= 64) == 0) { Cia1Icrd |= 0x02; mpu_interrupt(MPU_INT_CIA1, Cia1Icrd&Cia1Icrm); Cia1Timb = Cia1Timbst; if (Cia1Crb&0x08) { Cia1Crb &= 0xFE; } } } } /* Keyboard - convert CBM-ASCII into scan codes */ DATA KeybCode[256], KeybRow1, KeybCol1, KeybRow2, KeybCol2; void keyb_init(void) { int I,Table; ADDR PAddr,TAddr; for (I = 0; I < 256; I++) { /* for every ASCII code */ KeybCode[I] = 0x34; } /* empty Shift key, null effect */ for (Table = 3; Table >= 0; Table--) { /* for every key code */ PAddr = 0xEB79+2*Table; /* reserve it's ASCII code */ TAddr = (ADDR)mem_rd(PAddr)+((ADDR)mem_rd(PAddr+1)<<8); /* prefer low */ for (I = 63; I >= 0; I--) { KeybCode[mem_rd(TAddr+I)] = (DATA)((Table<<6)+I); } } } DATA keyb_rd(DATA Row) { return (~(((~Row)&KeybRow1 ? KeybCol1 : 0) | ((~Row)&KeybRow2 ? KeybCol2 : 0))); } void keyb_down(DATA Key) { switch(KeybCode[Key]&0x38) { case 0x00: KeybRow1 = 0x01; break; case 0x08: KeybRow1 = 0x02; break; case 0x10: KeybRow1 = 0x04; break; case 0x18: KeybRow1 = 0x08; break; case 0x20: KeybRow1 = 0x10; break; case 0x28: KeybRow1 = 0x20; break; case 0x30: KeybRow1 = 0x40; break; case 0x38: KeybRow1 = 0x80; } switch(KeybCode[Key]&0x07) { case 0x00: KeybCol1 = 0x01; break; case 0x01: KeybCol1 = 0x02; break; case 0x02: KeybCol1 = 0x04; break; case 0x03: KeybCol1 = 0x08; break; case 0x04: KeybCol1 = 0x10; break; case 0x05: KeybCol1 = 0x20; break; case 0x06: KeybCol1 = 0x40; break; case 0x07: KeybCol1 = 0x80; } switch(KeybCode[Key]&0xC0) { /* normal, Shift right, C=, Ctrl */ case 0x00: KeybRow2 = 0x00; KeybCol2 = 0x00; break; case 0x40: KeybRow2 = 0x40; KeybCol2 = 0x10; break; case 0x80: KeybRow2 = 0x80; KeybCol2 = 0x20; break; case 0xC0: KeybRow2 = 0x80; KeybCol2 = 0x04; } } void keyb_up(void) { KeybRow1 = 0; KeybCol1 = 0; KeybRow2 = 0; KeybCol2 = 0; } /* CIA2 */ DATA Cia2Pra, Cia2Prb, Cia2Ddra, Cia2Ddrb; int Cia2Tima, Cia2Timast, Cia2Timb, Cia2Timbst; DATA Cia2Icrd, Cia2Icrm, Cia2Cra, Cia2Crb; void cia2_init(void) { Krom[0x0E13] = 0xAD; /* IEC Bus Hack: LDA $DD0C */ Krom[0x0E14] = 0x0C; Krom[0x0E15] = 0xDD; Krom[0x0E16] = 0x4C; /* JMP $EE82 */ Krom[0x0E17] = 0x82; Krom[0x0E18] = 0xEE; Krom[0x0D40] = 0x8D; /* STA $DD0C */ Krom[0x0D41] = 0x0C; Krom[0x0D42] = 0xDD; Krom[0x0D43] = 0x4C; /* JMP $EDAB */ Krom[0x0D44] = 0xAB; Krom[0x0D45] = 0xED; flo_init(); } DATA cia2_rd(ADDR Addr) { switch(Addr) { case 0: { return (Cia2Pra); } case 1: { return (Cia2Prb); } case 2: { return (Cia2Ddra); } case 3: { return (Cia2Ddrb); } case 4: { return ((DATA)Cia2Tima); } case 5: { return ((DATA)(Cia2Tima>>8)); } case 6: { return ((DATA)Cia2Timb); } case 7: { return ((DATA)(Cia2Timb>>8)); } case 8: case 9: case 10: case 11: { c64_abort("real time clock not supported"); } case 12: { /* IEC bus Hack, read Byte from IEC */ return (Ram[0x00A4] = flo_rd()); } case 13: { DATA Temp = Cia2Icrd; Cia2Icrd = 0; mpu_interrupt(MPU_INT_CIA2, 0); if (Temp) return (Temp|0x80); else return (0x00); } case 14: { return (Cia2Cra); } case 15: { return (Cia2Crb); } } return (0xFF); } /* for the compilers sake */ void cia2_wr(ADDR Addr, DATA Data) { switch(Addr) { case 0: { DATA Old = Cia2Pra; /* recalculate video addresses */ Cia2Pra = Data; if (Data&0x03 != Old&0x03) vic_wr(24, vic_rd(24)); return; } case 1: { Cia2Prb = Data; return; } case 2: { Cia2Ddra = Data; return; } case 3: { Cia2Ddrb = Data; return; } case 4: { /* timers run in increments of 64 */ Cia2Timast = (Cia2Timast&0xFF00)|(int)(Data&0xC0); return; } case 5: { Cia2Timast = (Cia2Timast&0x00FF)|(int)(Data<<8); return; } case 6: { Cia2Timbst = (Cia2Timbst&0xFF00)|(int)(Data&0xC0); return; } case 7: { Cia2Timbst = (Cia2Timbst&0x00FF)|(int)(Data<<8); return; } case 8: case 9: case 10: case 11: { c64_abort("real time clock not supported"); } case 12: { /* IEC bus Hack, write Byte to IEC */ if (Cia2Pra&0x08) { /* if write with ATN */ flo_atn(Ram[0x0095]); return; } flo_wr(Ram[0x0095]); return; } case 13: { if (Data&0x80) Cia2Icrm |= Data; else Cia2Icrm &= ~Data; mpu_interrupt(MPU_INT_CIA2, Cia2Icrd&Cia2Icrm); return; } case 14: { if (Data&0x10) Cia2Tima = Cia2Timast; Cia2Cra = Data; return; } case 15: { if (Data&0x20) c64_abort("timer b counts timer a not supported"); if (Data&0x10) Cia2Timb = Cia2Timbst; Cia2Crb = Data; return; } } } void cia2_64us(void) { if (Cia2Cra&1) { if ((Cia2Tima -= 64) == 0) { Cia2Icrd |= 0x01; mpu_interrupt(MPU_INT_CIA2, Cia2Icrd&Cia2Icrm); Cia2Tima = Cia2Timast; if (Cia2Cra&0x08) { Cia2Cra &= 0xFE; } } } if (Cia2Crb&1) { if ((Cia2Timb -= 64) == 0) { Cia2Icrd |= 0x02; mpu_interrupt(MPU_INT_CIA2, Cia2Icrd&Cia2Icrm); Cia2Timb = Cia2Timbst; if (Cia2Crb&0x08) { Cia2Crb &= 0xFE; } } } } #define CIA2_IEC_STAT_RD_TIMEOUT 0x02 #define CIA2_IEC_STAT_WR_TIMEOUT 0x03 #define CIA2_IEC_STAT_EOI 0x40 #define CIA2_IEC_STAT_DEVNOTPRES 0x80 void cia2_iec_stat(DATA Status) { /* set IEC device status bits */ Ram[0x0090] |= Status; } /* Floppy 1541 at address 8 */ #include /* for MAXPATHLEN */ #include #include /* str*(), index() */ #define FLO_ADDR 8 enum { idle, ws_listen, ws_talk, l_open, l_data, l_close, t_data } FloState; int FloSec; DATA FloFileName[MAXPATHLEN+1]; FILE *FloFile[16]; void flo_init(void) { FloState = idle; for (FloSec = 0; FloSec < 16; FloSec++) { FloFile[FloSec] = NULL; } } DATA flo_rd(void) { switch (FloState) { case t_data: { if (FloFile[FloSec] != NULL) { DATA Data = (DATA)fgetc(FloFile[FloSec]); if (feof(FloFile[FloSec])) { cia2_iec_stat(CIA2_IEC_STAT_EOI); } return (Data); } } default: { /* for the compilers sake */ break; } } cia2_iec_stat(CIA2_IEC_STAT_RD_TIMEOUT); return (0); } void flo_wr(DATA Data) { switch (FloState) { case l_open: { strncat(FloFileName, &Data, 1); return; } case l_data: { if (FloFile[FloSec] != NULL) { fputc((int)Data, FloFile[FloSec]); return; } } default: { break; } } cia2_iec_stat(CIA2_IEC_STAT_WR_TIMEOUT); } void flo_atn(DATA Data) { switch (FloState) { case idle: { if (Data == 0x20+FLO_ADDR) { FloState = ws_listen; return; } if (Data == 0x40+FLO_ADDR) { FloState = ws_talk; return; } break; } case ws_listen: { if ((Data&0xF0) == 0xF0) { FloSec = Data&0x0F; strcpy(FloFileName,""); FloState = l_open; return; } if ((Data&0xE0) == 0x60) { FloSec = Data&0x0F; if (FloFile[FloSec] == NULL) { FloState = idle; return; } FloState = l_data; return; } if ((Data&0xF0) == 0xE0) { FloSec = Data&0x0F; FloState = l_close; return; } break; } case ws_talk: { if ((Data&0xE0) == 0x60) { FloSec = Data&0x0F; if (FloFile[FloSec] == NULL) { FloState = idle; return; } FloState = t_data; return; } break; } case l_open: { if (Data == 0x3F) { /* end of filename, open */ flo_fopen(FloSec,FloFileName); FloState = idle; return; } break; } case l_data: { if (Data == 0x3F) { /* end write, not end file! */ FloState = idle; return; } break; } case l_close: { if (Data == 0x3F) { /* now its end of file */ fclose(FloFile[FloSec]); FloFile[FloSec] = NULL; FloState = idle; return; } break; } case t_data: { if (Data == 0x5F) { FloState = idle; return; } } } cia2_iec_stat(CIA2_IEC_STAT_DEVNOTPRES); } void flo_fopen(int FloSec, DATA *FileName) { DATA *Char, FileType, FileMode; for (Char = FileName; *Char; Char++) { if (*Char >= 0x41 && *Char <= 0x5A) { *Char |= 0x20; } /* C64 unshifted to ASCII unshifted */ if (*Char >= 0xC1 && *Char <= 0xDA) { *Char &= 0x7F; } } /* C64 shifted to ASCII shifted */ if (FloSec == 15) { c64_abort("floppy command/status channel 15 not supported"); } if (*FileName == '$') { c64_abort("loading directory with $ not supported"); } if (index(FileName, ':')) { /* get rid of 0:, @: or @0: */ FileName = index(FileName, ':')+1; } FileType = 'p'; FileMode = 'r'; if (FloSec == 1) { FileMode = 'w'; } if (FloSec >= 2) { char *End = FileName+strlen(FileName); if (*(End-2) != ',' || *(End-4) != ',') { return; } FileType = *(End-3); FileMode = *(End-1); *(End-4) = '\0'; } switch (FileType) { case 'p': { strcat(FileName,".prg"); break; } case 's': { strcat(FileName,".seq"); break; } case 'u': { strcat(FileName,".usr"); break; } case 'l': { c64_abort("relative files not supported"); } default: { return; } } switch (FileMode) { case 'r': { FloFile[FloSec] = fopen(FileName,"rb"); break; } case 'w': { FloFile[FloSec] = fopen(FileName,"wb"); break; } case 'a': { FloFile[FloSec] = fopen(FileName,"ab"); } } }