diff --git a/apps/Makefile.k64f b/apps/Makefile.k64f index 85bb767..2a96149 100755 --- a/apps/Makefile.k64f +++ b/apps/Makefile.k64f @@ -151,6 +151,10 @@ else ifeq ($(__ZOS__),1) CPPFLAGS += -D__ZOS__ endif +ifeq ($(__TRANZPUTER__),1) + CPPFLAGS += -D__TRANZPUTER__ +endif + # Allow local overrides to the HEAPADDR for certain applications. ifeq (,$(findstring __HEAPADDR__,$(CPPFLAGS))) ifeq ($(HEAPADDR),) diff --git a/common/tranzputer.c b/common/tranzputer.c index b4cdcca..9f18341 100644 --- a/common/tranzputer.c +++ b/common/tranzputer.c @@ -106,7 +106,7 @@ static void __attribute((naked, noinline)) irqPortD(void) { // Save register we use. asm volatile ("push {r0-r6,lr}"); - + // Capture GPIO ports - this is necessary in order to make a clean capture and then decode. asm volatile ("ldr r5, =0x400ff010"); // GPIOA_PDIR asm volatile ("ldr r0, [r5, #0]"); @@ -158,6 +158,13 @@ cbr0: asm volatile ("lsrs r5, r0, #17"); // (z80Control.portA >> 17)&0x01) asm volatile ("and.w r5, r5, #1"); asm volatile ("orrs r3, r5"); + + // Ignore IO requests which arent service related. + asm volatile ("movw r5, " XSTR(IO_TZ_SVCREQ) ""); + asm volatile ("cmp r3, r5"); + asm volatile goto("bne %l0" :::: irqPortD_Exit); + + // Not memory mode so store the address. asm volatile ("str r3, %0" : "=m" (z80Control.ioAddr) :: "r0","r1","r2","r3","r4","r5","r7","r8","r9","r10","r11","r12"); // Convert data port bits into a byte and store. @@ -209,6 +216,9 @@ static void __attribute((naked, noinline)) irqPortC(void) // Save register we use. asm volatile ("push {r0-r6,lr}"); + // Short circuit interrupt when not needed. + asm volatile goto("b %l0" :::: irqPortC_Exit); + // Capture GPIO ports - this is necessary in order to make a clean capture and then decode. asm volatile ("ldr r5, =0x400ff010"); // GPIOA_PDIR asm volatile ("ldr r0, [r5, #0]"); @@ -372,7 +382,7 @@ static void setupIRQ(void) installIRQ(Z80_IORQ, IRQ_MASK_FALLING); // Setup the IRQ for Z80_MREQ. - installIRQ(Z80_MREQ, IRQ_MASK_FALLING); + //installIRQ(Z80_MREQ, IRQ_MASK_FALLING); // Setup the IRQ for Z80_RESET. installIRQ(Z80_RESET, IRQ_MASK_FALLING); @@ -500,6 +510,7 @@ printf("FirstCall\n"); } // Initialise control structure. + z80Control.svcControlAddr = getServiceAddr(); z80Control.refreshAddr = 0x00; z80Control.disableRefresh = 0; z80Control.runCtrlLatch = readCtrlLatch(); @@ -543,14 +554,13 @@ void resetZ80(void) __disable_irq(); pinOutputSet(Z80_RESET, LOW); for(volatile uint32_t pulseWidth=0; pulseWidth < 100; pulseWidth++); - //while((*ms - startTime) < 1); pinHigh(Z80_RESET); pinInput(Z80_RESET); __enable_irq(); // Wait a futher settling period before reinstating the interrupt. // - while((*ms - startTime) < 250); + while((*ms - startTime) < 400); // Restore the Z80 RESET IRQ as we have changed the pin mode. // @@ -1548,7 +1558,7 @@ FRESULT loadMZFZ80Memory(const char *src, uint32_t addr, uint8_t mainBoard, uint // Locals. FIL File; unsigned int readSize; - unsigned char buf[128]; + t_svcDirEnt mzfHeader; FRESULT fr0; // Sanity check on filenames. @@ -1561,7 +1571,7 @@ FRESULT loadMZFZ80Memory(const char *src, uint32_t addr, uint8_t mainBoard, uint // If no error occurred, read in the header. // if(!fr0) - fr0 = f_read(&File, buf, MZF_HEADER_SIZE, &readSize); + fr0 = f_read(&File, (char *)&mzfHeader, MZF_HEADER_SIZE, &readSize); // No errors, process. if(!fr0 && readSize == MZF_HEADER_SIZE) @@ -1570,13 +1580,22 @@ FRESULT loadMZFZ80Memory(const char *src, uint32_t addr, uint8_t mainBoard, uint f_close(&File); // Save the header into the CMT area for reference, some applications expect it. + // This assumes the TZFS is running and the memory bank is 64K block 0. // - copyToZ80(MZ_CMT_ADDR, (uint8_t *)buf, MZF_HEADER_SIZE, 0); - + copyToZ80(MZ_CMT_ADDR, (uint8_t *)&mzfHeader, MZF_HEADER_SIZE, 0); +printf("File:%s,attr=%02x,addr:%08lx\n", src, mzfHeader.attr, addr); // Now obtain the parameters. // if(addr == 0xFFFFFFFF) - addr = (uint32_t)(buf[MZF_LOADADDR+1] << 8) | (uint32_t)buf[MZF_LOADADDR]; + addr = mzfHeader.loadAddr; + + // Look at the attribute byte, if it is >= 0xF8 then it is a special tranZPUter binary object requiring loading into a seperate memory bank. + // The attribute & 0x07 << 16 specifies the memory bank in which to load the image. + if(mzfHeader.attr >= 0xF8) + { + addr += ((mzfHeader.attr & 0x07) << 16); + printf("CPM: Addr=%08lx\n", addr); + } // Ok, load up the file into Z80 memory. fr0 = loadZ80Memory(src, MZF_HEADER_SIZE, addr, 0, mainBoard, releaseBus); @@ -1899,8 +1918,9 @@ void loadTranZPUterDefaultROMS(void) // Locals. FRESULT result; - // Start off by clearing memory, the AS6C4008 chip holds random values at power on. - fillZ80Memory(0x000000, 0x80000, 0x00, 0); + // Start off by clearing active memory banks, the AS6C4008 chip holds random values at power on. + fillZ80Memory(0x000000, 0x10000, 0x00, 0); // TZFS and Sharp MZ80A mode. + fillZ80Memory(0x040000, 0x20000, 0x00, 0); // CPM Mode. // Now load the necessary images into memory. if((result=loadZ80Memory((const char *)MZ_ROM_SA1510_40C, 0, MZ_MROM_ADDR, 0, 0, 1)) != FR_OK) @@ -1915,7 +1935,7 @@ void loadTranZPUterDefaultROMS(void) { printf("Error: Failed to load page 2 of %s into tranZPUter memory.\n", MZ_ROM_TZFS); } - if(!result && (result=loadZ80Memory((const char *)MZ_ROM_TZFS, 0x2800, MZ_BANKRAM_ADDR+0x20000, 0x1000, 0, 0) != FR_OK)) + if(!result && (result=loadZ80Memory((const char *)MZ_ROM_TZFS, 0x2800, MZ_BANKRAM_ADDR+0x20000, 0x1000, 0, 1) != FR_OK)) { printf("Error: Failed to load page 3 of %s into tranZPUter memory.\n", MZ_ROM_TZFS); } @@ -1930,10 +1950,11 @@ void loadTranZPUterDefaultROMS(void) { // Set the memory model to BOOT so we can bootstrap TZFS. setCtrlLatch(TZMM_BOOT); - + // If autoboot flag set, force a restart to the ROM which will call User ROM startup code. if(osControl.tzAutoBoot) { + delay(100); fillZ80Memory(MZ_MROM_STACK_ADDR, MZ_MROM_STACK_SIZE, 0x00, 1); } @@ -1962,7 +1983,7 @@ uint8_t setZ80SvcStatus(uint8_t status) // Update the memory location. // - result=writeZ80Memory(TZSVC_CMD_STRUCT_ADDR+TZSVC_RESULT_OFFSET, status); + result=writeZ80Memory(z80Control.svcControlAddr+TZSVC_RESULT_OFFSET, status); // Release the Z80. writeCtrlLatch(z80Control.runCtrlLatch); @@ -2081,7 +2102,7 @@ static int matchFileWithWildcard(const char *pattern, const char *fileName, int getNextChar(&fileName); /* Retry until end of name if infinite search is specified */ - } while (infinite && nc); + } while (infinite && nc != 0x00 && nc != 0x0d); return 0; } @@ -2104,7 +2125,7 @@ uint8_t svcReadDir(uint8_t mode) static uint8_t dirSector = 0; // Virtual directory sector. FRESULT result = FR_OK; unsigned int readSize; - char fqfn[FF_SFN_BUF + 13]; // 0:\12345678\ + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ FIL File; t_svcCmpDirBlock *dirBlock = (t_svcCmpDirBlock *)&svcControl.sector; @@ -2233,13 +2254,13 @@ uint8_t svcReadDir(uint8_t mode) // It is a bit long winded as each file that matches the filename specification has to be opened and the MZF header filename // has to be checked. Cacheing would help here but wasteful in resources for number of times it would be called. // -uint8_t svcFindFile(char *file, char *searchFile, uint32_t searchNo) +uint8_t svcFindFile(char *file, char *searchFile, uint8_t searchNo) { // Locals uint8_t fileNo = 0; uint8_t found = 0; unsigned int readSize; - char fqfn[FF_SFN_BUF + 13]; // 0:\12345678\ + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ FIL File; FILINFO fno; DIR dirFp; @@ -2303,7 +2324,7 @@ uint8_t svcFindFile(char *file, char *searchFile, uint32_t searchNo) } // If we are searching on file number and the latest directory entry retrieval matches, exit and return the filename. - if(searchNo != 0xFFFFFFFF && fileNo == (uint8_t)searchNo) + if(searchNo != 0xFF && fileNo == (uint8_t)searchNo) { found = 1; } else @@ -2419,7 +2440,7 @@ uint8_t svcReadDirCache(uint8_t mode) // A method to find a file using the cached directory. If the cache is not available (ie. no memory) use the standard method. // -uint8_t svcFindFileCache(char *file, char *searchFile, uint32_t searchNo) +uint8_t svcFindFileCache(char *file, char *searchFile, uint8_t searchNo) { // Locals uint8_t fileNo = 0; @@ -2435,7 +2456,7 @@ uint8_t svcFindFileCache(char *file, char *searchFile, uint32_t searchNo) } else { // If we are searching on file number and there is no filter in place, see if it is valid and exit with data. - if(searchNo != 0xFFFFFFFF && strcmp((char *)svcControl.wildcard, TZSVC_DEFAULT_WILDCARD) == 0) + if(searchNo != 0xFF && strcmp((char *)svcControl.wildcard, TZSVC_DEFAULT_WILDCARD) == 0) { if(searchNo < osControl.dirMap.entries && osControl.dirMap.file[searchNo]) { @@ -2464,7 +2485,7 @@ uint8_t svcFindFileCache(char *file, char *searchFile, uint32_t searchNo) } // If we are searching on file number then see if it matches (after filter has been applied above). - if(searchNo != 0xFFFFFFFF && fileNo == (uint8_t)searchNo) + if(searchNo != 0xFF && fileNo == (uint8_t)searchNo) { found = 1; } else @@ -2502,7 +2523,7 @@ uint8_t svcCacheDir(const char *directory, uint8_t force) // Locals uint8_t fileNo = 0; unsigned int readSize; - char fqfn[FF_SFN_BUF + 13]; // 0:\12345678\ + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ FIL File; FILINFO fno; DIR dirFp; @@ -2618,7 +2639,7 @@ uint8_t svcReadFile(uint8_t mode) static uint8_t fileSector = 0; // Sector number being read. FRESULT result = FR_OK; unsigned int readSize; - char fqfn[FF_SFN_BUF + 13]; // 0:\12345678\ + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ // Find the required file. // Request to open? Validate that we dont already have an open file then find and open the file. @@ -2693,7 +2714,7 @@ uint8_t svcLoadFile(void) // Locals - dont use global as this is a seperate thread. // FRESULT result = FR_OK; - char fqfn[FF_SFN_BUF + 13]; // 0:\12345678\ + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ // Setup the defaults // @@ -2705,6 +2726,18 @@ uint8_t svcLoadFile(void) { // Call method to load an MZF file. result = loadMZFZ80Memory(fqfn, 0xFFFFFFFF, 0, 1); + + // Store the filename, used in reload or immediate saves. + // + osControl.lastFile = (uint8_t *)realloc(osControl.lastFile, strlen(fqfn)+1); + if(osControl.lastFile == NULL) + { + printf("Out of memory saving last file name, dependent applications (ie. CP/M) wont work!\n"); + result = FR_NOT_ENOUGH_CORE; + } else + { + strcpy((char *)osControl.lastFile, fqfn); + } } else { result = FR_NO_FILE; @@ -2722,7 +2755,7 @@ uint8_t svcSaveFile(void) // Locals - dont use global as this is a seperate thread. // FRESULT result = FR_OK; - char fqfn[FF_SFN_BUF + 13]; // 0:\12345678\ + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ char asciiFileName[MZF_FILENAME_LEN+1]; t_svcDirEnt mzfHeader; @@ -2757,7 +2790,7 @@ uint8_t svcEraseFile(void) // Locals - dont use global as this is a seperate thread. // FRESULT result = FR_OK; - char fqfn[FF_SFN_BUF + 13]; // 0:\12345678\ + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ // Setup the defaults // @@ -2779,6 +2812,198 @@ uint8_t svcEraseFile(void) return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); } +// Method to add a SD disk file as a CP/M disk drive for read/write by CP/M. +// +uint8_t svcAddCPMDrive(void) +{ + // Locals + char fqfn[FF_LFN_BUF + 13]; // 0:\12345678\ + FRESULT result = FR_OK; + // Sanity checks. + // + if(svcControl.fileNo >= CPM_MAX_DRIVES) + return(TZSVC_STATUS_FILE_ERROR); + + // Disk already allocated? May be a reboot or drive reassignment so free up the memory to reallocate. + // + if(osControl.cpmDriveMap.drive[svcControl.fileNo] != NULL) + { + if(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName != NULL) + { + free(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName); + osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName = 0; + } + free(osControl.cpmDriveMap.drive[svcControl.fileNo]); + osControl.cpmDriveMap.drive[svcControl.fileNo] = 0; + } + + // Build filename for the drive. + // + sprintf(fqfn, CPM_DRIVE_TMPL, svcControl.fileNo); + osControl.cpmDriveMap.drive[svcControl.fileNo] = (t_cpmDrive *)malloc(sizeof(t_cpmDrive)); + if(osControl.cpmDriveMap.drive[svcControl.fileNo] == NULL) + { + printf("Out of memory adding CP/M drive:%s\n", fqfn); + result = FR_NOT_ENOUGH_CORE; + } else + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName = (uint8_t *)malloc(strlen(fqfn)+1); + if(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName == NULL) + { + printf("Out of memory adding filename to CP/M drive:%s\n", fqfn); + result = FR_NOT_ENOUGH_CORE; + } else + { + strcpy((char *)osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName, fqfn); + + // Open the file to verify it exists and is valid, also to assign the file handle. + // + result = f_open(&osControl.cpmDriveMap.drive[svcControl.fileNo]->File, (char *)osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName, FA_OPEN_ALWAYS | FA_WRITE | FA_READ); + + // If no error occurred, read in the header. + // + if(!result) + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastTrack = 0; + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastSector = 0; + } else + { + // Error opening file so free up and release slot, return error. + free(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName); + osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName = 0; + free(osControl.cpmDriveMap.drive[svcControl.fileNo]); + osControl.cpmDriveMap.drive[svcControl.fileNo] = 0; + result = FR_NOT_ENOUGH_CORE; + } + } + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to read one of the opened, attached CPM drive images according to the Track and Sector provided. +// Inputs: +// svcControl.trackNo = Track to read from. +// svcControl.sectorNo = Sector to read from. +// svcControl.fileNo = CPM virtual disk number, which should have been attached with the svcAddCPMDrive method. +// Outputs: +// svcControl.sector = 512 bytes read from file. +// +uint8_t svcReadCPMDrive(void) +{ + // Locals. + FRESULT result = FR_OK; + uint32_t fileOffset; + unsigned int readSize; + + // Sanity checks. + // + if(svcControl.fileNo >= CPM_MAX_DRIVES || osControl.cpmDriveMap.drive[svcControl.fileNo] == NULL) + { + printf("svcReadCPMDrive: Illegal input values: fileNo=%d, driveMap=%08lx\n", svcControl.fileNo, (uint32_t)osControl.cpmDriveMap.drive[svcControl.fileNo]); + return(TZSVC_STATUS_FILE_ERROR); + } + + // Calculate the offset into the file. + fileOffset = ((svcControl.trackNo * CPM_SECTORS_PER_TRACK) + svcControl.sectorNo) * SECTOR_SIZE; + + // Seek to the correct location as directed by the track/sector. + result = f_lseek(&osControl.cpmDriveMap.drive[svcControl.fileNo]->File, fileOffset); + if(!result) + result = f_read(&osControl.cpmDriveMap.drive[svcControl.fileNo]->File, (char *)svcControl.sector, SECTOR_SIZE, &readSize); + + // No errors but insufficient bytes read, either the image is bad or there was an error! + if(!result && readSize != SECTOR_SIZE) + { + result = FR_DISK_ERR; + } else + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastTrack = svcControl.trackNo; + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastSector = svcControl.sectorNo; + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to write to one of the opened, attached CPM drive images according to the Track and Sector provided. +// Inputs: +// svcControl.trackNo = Track to write into. +// svcControl.sectorNo = Sector to write into. +// svcControl.fileNo = CPM virtual disk number, which should have been attached with the svcAddCPMDrive method. +// svcControl.sector = 512 bytes to write into the file. +// Outputs: +// +uint8_t svcWriteCPMDrive(void) +{ + // Locals. + FRESULT result = FR_OK; + uint32_t fileOffset; + unsigned int writeSize; + + // Sanity checks. + // + if(svcControl.fileNo >= CPM_MAX_DRIVES || osControl.cpmDriveMap.drive[svcControl.fileNo] == NULL) + { + printf("svcWriteCPMDrive: Illegal input values: fileNo=%d, driveMap=%08lx\n", svcControl.fileNo, (uint32_t)osControl.cpmDriveMap.drive[svcControl.fileNo]); + return(TZSVC_STATUS_FILE_ERROR); + } + + // Calculate the offset into the file. + fileOffset = ((svcControl.trackNo * CPM_SECTORS_PER_TRACK) + svcControl.sectorNo) * SECTOR_SIZE; + + // Seek to the correct location as directed by the track/sector. + result = f_lseek(&osControl.cpmDriveMap.drive[svcControl.fileNo]->File, fileOffset); + if(!result) + { + printf("Writing offset=%08lx\n", fileOffset); + for(uint16_t idx=0; idx < SECTOR_SIZE; idx++) + { + printf("%02x ", svcControl.sector[idx]); + if(idx % 32 == 0) + printf("\n"); + } + printf("\n"); + + result = f_write(&osControl.cpmDriveMap.drive[svcControl.fileNo]->File, (char *)svcControl.sector, SECTOR_SIZE, &writeSize); + if(!result) + { + f_sync(&osControl.cpmDriveMap.drive[svcControl.fileNo]->File); + } + } + + // No errors but insufficient bytes written, either the image is bad or there was an error! + if(!result && writeSize != SECTOR_SIZE) + { + result = FR_DISK_ERR; + } else + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastTrack = svcControl.trackNo; + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastSector = svcControl.sectorNo; + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Simple method to get the service record address which is dependent upon memory mode which in turn is dependent upon software being run. +// +uint32_t getServiceAddr(void) +{ + // Locals. + uint32_t addr = TZSVC_CMD_STRUCT_ADDR_TZFS; + uint8_t memoryMode = readCtrlLatch(); + + // Currently only CPM has a different service record address. + if(memoryMode == TZMM_CPM || memoryMode == TZMM_CPM2) + addr = TZSVC_CMD_STRUCT_ADDR_CPM; + + return(addr); +} // Method to process a service request from the z80 running TZFS or CPM. // @@ -2790,8 +3015,22 @@ void processServiceRequest(void) uint8_t status = 0; uint32_t copySize = TZSVC_CMD_STRUCT_SIZE; + // Update the service control record address according to memory mode. + // + z80Control.svcControlAddr = getServiceAddr(); + // Get the command and associated parameters. - copyFromZ80((uint8_t *)&svcControl, TZSVC_CMD_STRUCT_ADDR, TZSVC_CMD_SIZE, 0); + copyFromZ80((uint8_t *)&svcControl, z80Control.svcControlAddr, TZSVC_CMD_SIZE, 0); + + // Need to get the remainder of the data for the write operations. + if(svcControl.cmd == TZSVC_CMD_WRITESDDRIVE) + { + copyFromZ80((uint8_t *)&svcControl.sector, z80Control.svcControlAddr+TZSVC_CMD_SIZE, TZSVC_SECTOR_SIZE, 0); + } + + // Check this is a valid request. + if(svcControl.result != TZSVC_STATUS_REQUEST) + return; // Set status to processing. Z80 can use this to decide if the K64F received its request after a given period of time. setZ80SvcStatus(TZSVC_STATUS_PROCESSING); @@ -2820,23 +3059,12 @@ void processServiceRequest(void) status=svcReadFile(TZSVC_NEXT); break; - // Create or write a file stream and save the passed block of data into it. - case TZSVC_CMD_WRITEFILE: - case TZSVC_CMD_NEXTWRITEFILE: - // Need to get the remainder of the data for the write operation. - copyFromZ80((uint8_t *)&svcControl.sector, TZSVC_CMD_STRUCT_ADDR, TZSVC_SECTOR_SIZE, 0); - - // No need to copy the full record back to the Z80 for a write operation, just the command section. - // - copySize = TZSVC_CMD_SIZE; - break; - // Close an open dir/file. case TZSVC_CMD_CLOSE: svcReadDir(TZSVC_CLOSE); svcReadFile(TZSVC_CLOSE); - // No need to copy the full record back to the Z80 for a close operation, just the command section. + // Only need to copy the command section back to the Z80 for a close operation. // copySize = TZSVC_CMD_SIZE; break; @@ -2879,6 +3107,36 @@ void processServiceRequest(void) } break; + // Load the CPM CCP+BDOS from file into the address given. + case TZSVC_CMD_LOADBDOS: + if((status=loadZ80Memory((const char *)osControl.lastFile, MZF_HEADER_SIZE, svcControl.loadAddr+0x40000, svcControl.loadSize, 0, 1)) != FR_OK) + { + printf("Error: Failed to load BDOS:%s into tranZPUter memory.\n", (char *)osControl.lastFile); + } + break; + + // Add a CP/M disk to the system for read/write access by CP/M on the Sharp MZ80A. + // + case TZSVC_CMD_ADDSDDRIVE: + status=svcAddCPMDrive(); + break; + + // Read an assigned CP/M drive sector giving an LBA address and the drive number. + // + case TZSVC_CMD_READSDDRIVE: + status=svcReadCPMDrive(); + break; + + // Write a sector to an assigned CP/M drive giving an LBA address and the drive number. + // + case TZSVC_CMD_WRITESDDRIVE: + status=svcWriteCPMDrive(); + + // Only need to copy the command section back to the Z80 for a write operation. + // + copySize = TZSVC_CMD_SIZE; + break; + default: break; } @@ -2886,10 +3144,7 @@ void processServiceRequest(void) // Update the status in the service control record then copy it across to the Z80. // svcControl.result = status; - copyToZ80(TZSVC_CMD_STRUCT_ADDR, (uint8_t *)&svcControl, copySize, 0); - - // Return status. - setZ80SvcStatus(status); + copyToZ80(z80Control.svcControlAddr, (uint8_t *)&svcControl, copySize, 0); // Need to refresh the directory? Do this at the end of the routine so the Sharp MZ80A isnt held up. if(refreshCacheDir) diff --git a/include/tools.h b/include/tools.h index 8758a14..df268ea 100644 --- a/include/tools.h +++ b/include/tools.h @@ -177,6 +177,7 @@ static t_groupstruct groupTable[] = { { CMD_GROUP_EXEC, CMD_GROUP_EXEC_NAME }, { CMD_GROUP_MISC, CMD_GROUP_MISC_NAME }, { CMD_GROUP_APP, CMD_GROUP_APP_NAME }, + { CMD_GROUP_TZ, CMD_GROUP_TZ_NAME }, }; #endif diff --git a/include/tranzputer.h b/include/tranzputer.h index 16f1199..71b475d 100755 --- a/include/tranzputer.h +++ b/include/tranzputer.h @@ -46,10 +46,9 @@ #define TZMM_TZFS2 0x03 // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 1. #define TZMM_TZFS3 0x04 // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 2. #define TZMM_TZFS4 0x05 // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 3. -#define TZMM_CPM 0x14 // CPM main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used as the static CBIOS and F000-FFFF is the paged CBIOS, TPA is from 0000-D000(BDOS+CCP = 1800), all is in 64K Block 4, F000-FFFF is in 64K Block 4. -#define TZMM_CPM2 0x15 // CPM main memory configuration. E800-EFFF and TPA 0000-D000 are in tranZPUter RAM 64K block 4, CBIOS2 F000-FFFF is in 64K block 5 and video and memory control D000-E7FF are on the mainboard. -#define TZMM_CPM3 0x16 // CPM main memory configuration. E800-EFFF and TPA 0000-D000 are in tranZPUter RAM 64K block 4, CBIOS2 F000-FFFF is in 64K block 6 and video and memory control D000-E7FF are on the mainboard. -#define TZMM_CPM4 0x17 // CPM main memory configuration. E800-EFFF and TPA 0000-D000 are in tranZPUter RAM 64K block 4, CBIOS2 F000-FFFF is in 64K block 7 and video and memory control D000-E7FF are on the mainboard. +#define TZMM_CPM 0x06 // CPM main memory configuration, all memory on the tranZPUter board, 64K block 4 selected. Special case for F3C0:F3FF & F7C0:F7FF (floppy disk paging vectors) which resides on the mainboard. +#define TZMM_CPM2 0x07 // CPM main memory configuration, F000-FFFF are on the tranZPUter board in block 4, 0040-CFFF and E800-EFFF are in block 5, mainboard for D000-DFFF (video), E000-E800 (Memory control) selected. + // Special case for 0000:003F (interrupt vectors) which resides in block 4, F3C0:F3FF & F7C0:F7FF (floppy disk paging vectors) which resides on the mainboard. #define TZMM_TZPU0 0x18 // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 0 is selected. #define TZMM_TZPU1 0x19 // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 1 is selected. #define TZMM_TZPU2 0x1A // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 2 is selected. @@ -88,17 +87,24 @@ #define MZ_MEMORY_RESET 0xE010 // Address when read resets the memory to the default location 0000-0FFF. #define MZ_CRT_NORMAL 0xE014 // Address when read sets the CRT to normal display mode. #define MZ_CRT_INVERSE 0xE018 // Address when read sets the CRT to inverted display mode. -#define MZ_ROM_SA1510_40C "SA1510.ROM" // Original 40 character Monitor ROM. -#define MZ_ROM_SA1510_80C "SA1510-8.ROM" // Original Monitor ROM patched for 80 character screen mode. -#define MZ_ROM_TZFS "TZFS.ROM" // tranZPUter Filing System ROM. +#define MZ_ROM_SA1510_40C "0:\\TZFS\\SA1510.ROM" // Original 40 character Monitor ROM. +#define MZ_ROM_SA1510_80C "0:\\TZFS\\SA1510-8.ROM" // Original Monitor ROM patched for 80 character screen mode. +#define MZ_ROM_TZFS "0:\\TZFS\\TZFS.ROM" // tranZPUter Filing System ROM. + +// CP/M constants. +// +#define CPM_MAX_DRIVES 16 // Maximum number of drives in CP/M. +#define CPM_FILE_CCPBDOS "0:\\CPM\\CPM22.BIN" // CP/M CCP and BDOS for warm start reloads. +#define CPM_DRIVE_TMPL "0:\\CPM\\CPMDSK%02u.RAW" // Template for CPM disk drives stored on the SD card. +#define CPM_SECTORS_PER_TRACK 32 // Number of sectors in a track on the virtual CPM disk. +#define CPM_TRACKS_PER_DISK 1024 // Number of tracks on a disk. // Service request constants. // -#define TZSVC_CMD_STRUCT_ADDR 0xEC80 // Address of the command structure. +#define TZSVC_CMD_STRUCT_ADDR_TZFS 0x0ED80 // Address of the command structure within TZFS - exists in 64K Block 0. +#define TZSVC_CMD_STRUCT_ADDR_CPM 0x4F560 // Address of the command structure within CP/M - exists in 64K Block 4. #define TZSVC_CMD_STRUCT_SIZE 0x280 // Size of the inter z80/K64 service command memory. -#define TZSVC_CMD_SIZE 0x04+TZSVC_DIRNAME_SIZE+\ - +TZSVC_FILENAME_SIZE+\ - TZSVC_WILDCARD_SIZE // Size of the command/result portion of the control structure. +#define TZSVC_CMD_SIZE (sizeof(t_svcControl)-TZSVC_SECTOR_SIZE) #define TZVC_MAX_CMPCT_DIRENT_BLOCK TZSVC_SECTOR_SIZE/TZSVC_CMPHDR_SIZE // Maximum number of directory entries per sector. #define TZSVC_MAX_DIR_ENTRIES 255 // Maximum number of files in one directory, any more than this will be ignored. #define TZSVC_CMPHDR_SIZE 32 // Compacted header size, contains everything except the comment field, padded out to 32bytes. @@ -108,15 +114,17 @@ #define TZSVC_CMD_NEXTDIR 0x02 // Service command to return the next block of an open directory. #define TZSVC_CMD_READFILE 0x03 // Service command to open a file and return the first block. #define TZSVC_CMD_MEXTREADFILE 0x04 // Service command to return the next block of an open file. -#define TZSVC_CMD_WRITEFILE 0x05 // Service command to create a file and write the first block into it. -#define TZSVC_CMD_NEXTWRITEFILE 0x06 // Service command to write the next block into the open file. -#define TZSVC_CMD_CLOSE 0x07 // Service command to close any open file or directory. -#define TZSVC_CMD_LOADFILE 0x08 // Service command to load a file directly into tranZPUter memory. -#define TZSVC_CMD_SAVEFILE 0x09 // Service command to save a file directly from tranZPUter memory. -#define TZSVC_CMD_ERASEFILE 0x0A // Service command to erase a file on the SD card. -#define TZSVC_CMD_CHANGEDIR 0x0B // Service command to change active directory on the SD card. +#define TZSVC_CMD_CLOSE 0x05 // Service command to close any open file or directory. +#define TZSVC_CMD_LOADFILE 0x06 // Service command to load a file directly into tranZPUter memory. +#define TZSVC_CMD_SAVEFILE 0x07 // Service command to save a file directly from tranZPUter memory. +#define TZSVC_CMD_ERASEFILE 0x08 // Service command to erase a file on the SD card. +#define TZSVC_CMD_CHANGEDIR 0x09 // Service command to change active directory on the SD card. #define TZSVC_CMD_LOAD40BIOS 0x20 // Service command requesting that the 40 column version of the SA1510 BIOS is loaded. #define TZSVC_CMD_LOAD80BIOS 0x21 // Service command requesting that the 80 column version of the SA1510 BIOS is loaded. +#define TZSVC_CMD_LOADBDOS 0x30 // Service command to reload CPM BDOS+CCP. +#define TZSVC_CMD_ADDSDDRIVE 0x31 // Service command to attach a CPM disk to a drive number. +#define TZSVC_CMD_READSDDRIVE 0x32 // Service command to read an attached SD file as a CPM disk drive. +#define TZSVC_CMD_WRITESDDRIVE 0x33 // Service command to write to a CPM disk drive which is an attached SD file. #define TZSVC_DEFAULT_DIR "MZF" // Default directory where MZF files are stored. #define TZSVC_DEFAULT_EXT "MZF" // Default file extension for MZF files. #define TZSVC_DEFAULT_WILDCARD "*" // Default wildcard file matching. @@ -402,6 +410,21 @@ typedef struct __attribute__((__packed__)) { t_svcCmpDirEnt mzfHeader; // Compact Sharp header data of this file. } t_sharpToSDMap; +// Structure to define the control information for a CP/M disk drive. +// +typedef struct { + uint8_t *fileName; // FQFN of the CPM disk image file. + uint32_t lastTrack; // Track of last successful operation. + uint32_t lastSector; // Sector of last successful operation. + FIL File; // Opened file handle of the CPM disk image. +} t_cpmDrive; + +// Structure to define which CP/M drives are added to the system, mapping a number from CP/M into a record containing the details of the file on the SD card. +// +typedef struct { + t_cpmDrive *drive[CPM_MAX_DRIVES]; // 1:1 map of CP/M drive number to an actual file on the SD card. +} t_cpmDriveMap; + // Structure to hold a map of an entire directory of files on the SD card and their associated Sharp MZ0A filename. typedef struct __attribute__((__packed__)) { uint8_t valid; // Is this mapping valid? @@ -414,6 +437,7 @@ typedef struct __attribute__((__packed__)) { // typedef struct { #ifndef __APP__ + uint32_t svcControlAddr; // Address of the service control record within the 512K static RAM bank. uint8_t refreshAddr; // Refresh address for times when the K64F must issue refresh cycles on the Z80 bus. uint8_t disableRefresh; // Disable refresh if the mainboard DRAM isnt being used. uint8_t runCtrlLatch; // Latch value the Z80 is running with. @@ -431,11 +455,11 @@ typedef struct { uint8_t memorySwap; // A memory Swap event has occurred, 0000-0FFF -> C000-CFFF (1), or C000-CFFF -> 0000-0FFF (0) uint8_t crtMode; // A CRT event has occurred, Normal mode (0) or Reverse Mode (1) uint8_t scroll; // Hardware scroll offset. - volatile uint32_t portA; - volatile uint32_t portB; - volatile uint32_t portC; - volatile uint32_t portD; - volatile uint32_t portE; + volatile uint32_t portA; // ISR store of GPIO Port A used for signal decoding. + volatile uint32_t portB; // ISR store of GPIO Port B used for signal decoding. + volatile uint32_t portC; // ISR store of GPIO Port C used for signal decoding. + volatile uint32_t portD; // ISR store of GPIO Port D used for signal decoding. + volatile uint32_t portE; // ISR store of GPIO Port E used for signal decoding. #endif } t_z80Control; @@ -444,6 +468,8 @@ typedef struct { typedef struct { uint8_t tzAutoBoot; // Autoboot the tranZPUter into TZFS mode. t_dirMap dirMap; // Directory map of SD filenames to Sharp MZ80A filenames. + t_cpmDriveMap cpmDriveMap; // Map of file number to an open SD disk file to be used as a CPM drive. + uint8_t *lastFile; // Last file loaded - typically used for CPM to reload itself. } t_osControl; // Structure to contain inter CPU communications memory for command service processing and results. @@ -459,8 +485,11 @@ typedef struct __attribute__((__packed__)) { uint8_t dirSector; // Virtual directory sector number. uint8_t fileSector; // Sector within open file to read/write. }; - // uint16_t sectorNo; // Sector number of file to retrieve. + uint16_t trackNo; // For virtual drives with track and sector this is the track number + uint16_t sectorNo; // For virtual drives with tracl and sector this is the sector number. uint8_t fileNo; // File number of a file within the last directory listing to open/update. + uint16_t loadAddr; // Load address for ROM/File images which need to be dynamic. + uint16_t loadSize; // Size for ROM/File to be loaded. uint8_t directory[TZSVC_DIRNAME_SIZE]; // Directory in which to look for a file. If no directory is given default to MZF. uint8_t filename[TZSVC_FILENAME_SIZE]; // File to open or create. uint8_t wildcard[TZSVC_WILDCARD_SIZE]; // A basic wildcard pattern match filter to be applied to a directory search. @@ -545,13 +574,17 @@ void convertSharpFilenameToAscii(char *, char *, uint8_t); uint8_t setZ80SvcStatus(uint8_t); void svcSetDefaults(void); uint8_t svcReadDir(uint8_t); -uint8_t svcFindFile(char *, char *, uint32_t); +uint8_t svcFindFile(char *, char *, uint8_t); uint8_t svcReadDirCache(uint8_t); -uint8_t svcFindFileCache(char *, char *, uint32_t); +uint8_t svcFindFileCache(char *, char *, uint8_t); uint8_t svcCacheDir(const char *, uint8_t); uint8_t svcReadFile(uint8_t); uint8_t svcLoadFile(void); uint8_t svcEraseFile(void); +uint8_t svcAddCPMDrive(void); +uint8_t svcReadCPMDrive(void); +uint8_t svcWriteCPMDrive(void); +uint32_t getServiceAddr(void); void processServiceRequest(void); void loadTranZPUterDefaultROMS(void); void tranZPUterControl(void); diff --git a/libraries/lib/libimath2-k64f.a b/libraries/lib/libimath2-k64f.a index cfaadf8..d87743c 100644 Binary files a/libraries/lib/libimath2-k64f.a and b/libraries/lib/libimath2-k64f.a differ diff --git a/libraries/lib/libumansi-k64f.a b/libraries/lib/libumansi-k64f.a index 546bda2..391797f 100644 Binary files a/libraries/lib/libumansi-k64f.a and b/libraries/lib/libumansi-k64f.a differ diff --git a/libraries/lib/libummath-k64f.a b/libraries/lib/libummath-k64f.a index 26fe5a2..552f3f8 100644 Binary files a/libraries/lib/libummath-k64f.a and b/libraries/lib/libummath-k64f.a differ diff --git a/libraries/lib/libummathf-k64f.a b/libraries/lib/libummathf-k64f.a index 7c400ac..ba0453b 100644 Binary files a/libraries/lib/libummathf-k64f.a and b/libraries/lib/libummathf-k64f.a differ diff --git a/libraries/lib/libummisc-k64f.a b/libraries/lib/libummisc-k64f.a index 705e6f4..4abcbeb 100644 Binary files a/libraries/lib/libummisc-k64f.a and b/libraries/lib/libummisc-k64f.a differ diff --git a/libraries/lib/libumstdio-k64f.a b/libraries/lib/libumstdio-k64f.a index 7eabfef..60259da 100644 Binary files a/libraries/lib/libumstdio-k64f.a and b/libraries/lib/libumstdio-k64f.a differ diff --git a/startup/mk20dx128.c b/startup/mk20dx128.c index 0089c28..f53b856 100644 --- a/startup/mk20dx128.c +++ b/startup/mk20dx128.c @@ -37,7 +37,8 @@ #include #if defined __TRANZPUTER__ - #define FRESULT uint8_t + //#define FRESULT uint8_t + #include #include #endif diff --git a/zOS/src/zOS.cpp b/zOS/src/zOS.cpp index 077516b..c1fe5b9 100644 --- a/zOS/src/zOS.cpp +++ b/zOS/src/zOS.cpp @@ -264,7 +264,10 @@ void tranZPUterControl(void) // if(getZ80IO(&ioAddr, &ioData) == 1) { -printf("Got an IO request, addr:%02x, Data:%02x\n", ioAddr, ioData); + // Interrupt triggering has artifcats, ignore them! + if(ioData == 0xff) + continue; + switch(ioAddr) { // Service request. Actual data about the request is stored in the Z80 memory, so read the request and process.