From 8f345b801eb01f3a751d3e7cbddf49792ec8d181 Mon Sep 17 00:00:00 2001 From: nino-porcino Date: Thu, 17 Mar 2022 12:42:35 +0100 Subject: [PATCH] tagged file names (LOAD, RUN, SAVE, DIR) --- demos/sdcard/apple1_sdcard/apple1_sdcard.ino | 219 ++++++++++++++++++- demos/sdcard/cmd_load.h | 106 ++++++++- demos/sdcard/cmd_read.h | 2 +- demos/sdcard/cmd_save.h | 13 +- demos/sdcard/cmd_write.h | 2 +- demos/sdcard/console.h | 58 ++++- demos/sdcard/sdcard.c | 10 + 7 files changed, 381 insertions(+), 29 deletions(-) diff --git a/demos/sdcard/apple1_sdcard/apple1_sdcard.ino b/demos/sdcard/apple1_sdcard/apple1_sdcard.ino index 9e48c96..08f3ffa 100644 --- a/demos/sdcard/apple1_sdcard/apple1_sdcard.ino +++ b/demos/sdcard/apple1_sdcard/apple1_sdcard.ino @@ -36,7 +36,7 @@ in Verilog syntax: data = { PORTB[1:0], PORTD[7:2] }; */ -#define FASTWRITE 1 +// #define FASTWRITE 1 #ifdef FASTWRITE #define get_cpu_strobe ((PORTC >> 1) & 1) @@ -47,7 +47,8 @@ in Verilog syntax: data = { PORTB[1:0], PORTD[7:2] }; #endif -#include +// #include + #include #include "SdFat.h" @@ -210,6 +211,7 @@ void send_byte_to_cpu(int data) { const int CMD_READ = 0; const int CMD_WRITE = 1; const int CMD_DIR = 2; +const int CMD_LOAD = 4; const int CMD_DEL = 11; const int CMD_LS = 12; const int CMD_CD = 13; @@ -218,8 +220,9 @@ const int CMD_PWD = 19; const int CMD_RMDIR = 15; const int CMD_TEST = 20; -const int ERR_RESPONSE = 255; -const int OK_RESPONSE = 0; +const int ERR_RESPONSE = 255; +const int WAIT_RESPONSE = 1; +const int OK_RESPONSE = 0; char filename[64]; char tmp[64]; @@ -260,6 +263,8 @@ void setup() { last_dir = -1; // no previous data direction set_data_port_direction(DIR_INPUT); + /* + // regex disabled for now MatchState ms; char buf [100] = { "The quick " }; ms.Target (buf); @@ -267,6 +272,7 @@ void setup() { if(result >0) { Serial.println("match!"); } + */ // set working directory to root strcpy(cd_path, "/"); @@ -400,6 +406,7 @@ void print_dir_entry(File dir, int list_files, int command) { // send file size or directory entry.getName(filename, 64); + strtoupper(filename); if((list_files == 0 && entry.isDirectory()) || (list_files == 1 && !entry.isDirectory())) { if(entry.isDirectory()) { @@ -429,16 +436,42 @@ void print_dir_entry(File dir, int list_files, int command) { send_byte_to_cpu('\r'); } else { - // BUG the following line does not work - // sprintf(tmp, "%5d %s", entry.size(), filename); - // use this instead + + char type[5]; + char address[5]; + + strcpy(type,""); + strcpy(address, ""); + + char *x = strchr(filename, '#'); + if(x != NULL) { + *x++ = 0; + if(x[0]=='0' && x[1]=='6') { + strcpy(type,"BIN "); + strcpy(address,x+2); + } + else if(x[0]=='F' && x[1]=='1') { + strcpy(type,"BAS "); + strcpy(address,x+2); + } + else { + strcpy(type,"??? "); + } + } + sprintf(tmp, "%-15s", filename); print_string_to_cpu(tmp); Serial.print(filename); - sprintf(tmp, "%6d", entry.size()); + sprintf(tmp, "%6d ", entry.size()); print_string_to_cpu(tmp); - Serial.println(tmp); + Serial.print(tmp); + + print_string_to_cpu(type); + Serial.print(type); + + print_string_to_cpu(address); + Serial.println(address); send_byte_to_cpu('\r'); } @@ -616,6 +649,173 @@ void comando_write() { send_byte_to_cpu(OK_RESPONSE); } +// ************************************************************************************** +// ************************************************************************************** +// ********************************* CMD_LOAD ****************************************** +// ************************************************************************************** +// ************************************************************************************** + +void comando_load() { + Serial.println(F("command CMD_LOAD received from CPU")); + + // reads filename as 0 terminated string + receive_string_from_cpu(filename); + if(TIMEOUT) return; + Serial.print(F("file to read: ")); + Serial.println(filename); + + // if a matching file name is found, use it + if(matchname(filename, tmp)==1) { + Serial.print(F("found matching file: ")); + Serial.println(tmp); + strcpy(filename, tmp); + } + + if(!SD.exists(filename)) { + Serial.println(F("error opening file")); + send_byte_to_cpu(ERR_RESPONSE); + send_string_to_cpu(FILE_NOT_FOUND); + return; + } + + // open the file + File myFile = SD.open(filename); + if(!myFile) { + Serial.println(F("error opening file")); + send_byte_to_cpu(ERR_RESPONSE); + send_string_to_cpu(CANT_OPEN_FILE); + return; + } + Serial.println(F("file opened on the SD card")); + + // ok response + send_byte_to_cpu(OK_RESPONSE); + if(TIMEOUT) return; + Serial.println(F("ok response sent to CPU")); + + // sends matched filename + send_string_to_cpu(filename); + + // sends size as low and high byte + int size = myFile.size(); + send_byte_to_cpu(size & 0xFF); + send_byte_to_cpu((size >> 8) & 0xFF); + if(TIMEOUT) return; + Serial.println(F("file size sent to CPU")); + + int bytes_sent = 0; + while(myFile.available() && !TIMEOUT) { + send_byte_to_cpu(myFile.read()); + if(!TIMEOUT) bytes_sent++; + } + myFile.close(); + + if(TIMEOUT) { + Serial.print(F("timeout, bytes sent: ")); + Serial.println(bytes_sent); + return; + } + + Serial.println(F("file read ok")); +} + +void strtoupper(char *str){ + int len = strlen(str), i; + + for(i=0;i='a' && str[i]<='z') + str[i] = str[i]-'a'+'A'; +} + +// splits filename into file_path and file_name +// e.g. "root/myfolder/pluto" => "root/myfolder" , "pluto" +// "myfolder/pluto" => "myfolder", "pluto" +// "pluto" => "", "pluto" +// "/pluto" => "/", "pluto" + +char file_path[64]; +char file_name[64]; + +void split_path(char *filename) { + strcpy(file_path, filename); + + for(int t=strlen(file_path)-1;t>0;t--) { + if(file_path[t] == '/') { + file_path[t] = 0; + strcpy(file_name, &file_path[t+1]); + if(t==0) strcpy(file_path, "/"); // case of root folder + return; + } + } + strcpy(file_path, ""); + strcpy(file_name, filename); +} + +// +// returns in dest the first file that matches (starts with) "filename" +// returns 1 if matching file is found +// returns 0 if no matching file is found +// +int matchname(char *filename, char *dest) { + // split filename into file_path and file_name + split_path(filename); + + Serial.print(F("after split file_path=")); + Serial.print(file_path); + Serial.print(F(", file_name=")); + Serial.println(file_name); + + if(strlen(file_path)==0) Serial.println(F("scanning the current directory")); + else Serial.println(F("scanning the file_path directory")); + + // open the directory containing the file + File dir = SD.open(strlen(file_path)==0 ? cd_path : file_path); + + if(!dir) return 0; + + Serial.println(F("dir opened")); + + // scan all the directory + while(1) { + + send_byte_to_cpu(WAIT_RESPONSE); + if(TIMEOUT) return 0; + + File F = dir.openNextFile(); + + // end of directory + if(!F) { + dir.close(); + return 0; + } + + // copy filename in dest and closes it + F.getName(dest, 64); + strtoupper(dest); + F.close(); + + Serial.println(F("file entry:")); + Serial.println(dest); + + // verify the match + if(strncmp(file_name, dest, strlen(filename))==0) { + Serial.println(F("it is matching!")); + // matching, dest already contains the matched file name + + // if file_path is empty then it's current directory, do nothing + // else file_path needs to be combined with file name + if(file_path[0]!=0) { + Serial.println(F("not on current dir, joining paths")); + strcpy(filename, dest); + if(file_path[0]=='/' && file_path[1]==0) sprintf(dest,"/%s", filename); // case of root folder + else sprintf(dest,"%s/%s", file_path, filename); // case of normal nested folder + } + dir.close(); + return 1; + } + } +} + // ************************************************************************************** // ************************************************************************************** // ********************************* CMD_DEL ******************************************** @@ -867,6 +1067,7 @@ void loop() { if(data == CMD_READ) comando_read(); else if(data == CMD_WRITE) comando_write(); + else if(data == CMD_LOAD) comando_load(); else if(data == CMD_DEL) comando_del(); else if(data == CMD_RMDIR) comando_rmdir(); else if(data == CMD_MKDIR) comando_mkdir(); diff --git a/demos/sdcard/cmd_load.h b/demos/sdcard/cmd_load.h index c4c892f..ae40348 100644 --- a/demos/sdcard/cmd_load.h +++ b/demos/sdcard/cmd_load.h @@ -6,30 +6,109 @@ // global cmd -void comando_load() { +void comando_load_bas() { // send command byte - send_byte_to_MCU(CMD_READ); + send_byte_to_MCU(CMD_LOAD); if(TIMEOUT) return; // send filename send_string_to_MCU(filename); if(TIMEOUT) return; - // response - byte response = receive_byte_from_MCU(); - if(TIMEOUT) return; + // wait for OK response, MCU sends many WAIT_RESPONSE to avoid TIMEOUT + while(1) { + byte response = receive_byte_from_MCU(); + if(TIMEOUT) return; - if(response == ERR_RESPONSE) { - // error with file, print message - print_string_response(); - return; + if(response == ERR_RESPONSE) { + // error with file, print message + print_string_response(); + return; + } + if(response == OK_RESPONSE) break; + } + + // get matching file name + receive_string_from_MCU(filename); + woz_puts("FOUND "); + woz_puts(filename); + woz_putc('\r'); + + byte filetype = 0; + token_ptr = filename; + while(1) { + if(*token_ptr == '#') { + if(token_ptr[1] == '0' && token_ptr[2] == '6') { filetype = 0x06; break; } + if(token_ptr[1] == 'F' && token_ptr[2] == '1') { filetype = 0xF1; break; } + } + if(*token_ptr == 0) break; + token_ptr++; + } + + // calculate start address for 0x06 binary file + if(filetype == 0x06) { + token_ptr+=2; + hex_to_word(token_ptr); + start_address = tmpword; } // get file length in tmpword receive_word_from_mcu(); if(TIMEOUT) return; + if(filetype != 0x06 && filetype != 0xF1) { + woz_puts("?INVALID FILE NAME TAG #"); + for(word t=0;t!=tmpword;t++) receive_byte_from_MCU(); // empty buffer + return; + } + + woz_puts("LOADING\r"); + + if(filetype == 0x06) { + // 0x06 BINARY FILE format + + // get file bytes + token_ptr = (byte *) start_address; + for(word t=0;t!=tmpword;t++) { + byte data = receive_byte_from_MCU(); + if(TIMEOUT) return; + *token_ptr++ = data; + + #ifdef LOADING_DOTS + if(((byte)t) == 0) woz_putc('.'); + #endif + } + + // decrease by one for display result + token_ptr--; + + // print feedback to user + woz_putc('\r'); + woz_puts(filename); + woz_putc('\r'); + woz_print_hexword(start_address); + woz_putc('.'); + woz_print_hexword((word)token_ptr); + woz_puts(" ("); + utoa(tmpword, filename, 10); // use filename as string buffer + woz_puts(filename); + woz_puts(" BYTES)\rOK"); + + // executes machine language program at start address + if(cmd == CMD_RUN) { + woz_putc('\r'); + tmpword = start_address; + asm { + jmp (tmpword) + } + } + + return; + } + + // 0xF1 BASIC FILE TYPE + // get file bytes token_ptr = (byte *) 0; for(word t=0;t!=tmpword;t++) { @@ -37,7 +116,9 @@ void comando_load() { if(TIMEOUT) return; if((t==0 && data!=0x41) || (t==1 && data!=0x31)) { - woz_puts("?NOT CFFA1/PRODOS FORMAT"); + woz_puts("?UNKNOWN FILE FORMAT"); + t=t+1; + for(;t!=tmpword;t++) receive_byte_from_MCU(); // empty buffer return; } else if(t<0x004a) { @@ -81,14 +162,15 @@ void bas_file_info() { // print feedback to user woz_putc('\r'); woz_puts(filename); - woz_puts(": "); + woz_putc('\r'); bas_info(); woz_puts("\rOK"); } void bas_info() { - woz_puts("LOMEM="); + woz_puts("(LOMEM="); woz_print_hexword((word) *BASIC_LOMEM); woz_puts(" HIMEM="); woz_print_hexword((word) *BASIC_HIMEM); + woz_putc(')'); } diff --git a/demos/sdcard/cmd_read.h b/demos/sdcard/cmd_read.h index 3874101..597abaa 100644 --- a/demos/sdcard/cmd_read.h +++ b/demos/sdcard/cmd_read.h @@ -50,7 +50,7 @@ void comando_read() { // print feedback to user woz_putc('\r'); woz_puts(filename); - woz_puts(": "); + woz_putc('\r'); woz_print_hexword(start_address); woz_putc('.'); woz_print_hexword((word)token_ptr); diff --git a/demos/sdcard/cmd_save.h b/demos/sdcard/cmd_save.h index eca2b4b..1226b5f 100644 --- a/demos/sdcard/cmd_save.h +++ b/demos/sdcard/cmd_save.h @@ -1,11 +1,20 @@ // global len -void comando_save() { +void comando_save_bas() { + + if(((word) *BASIC_HIMEM) < ((word) *BASIC_LOMEM)) { + woz_puts("?NO BASIC PROGRAM"); + return; + } // send command byte send_byte_to_MCU(CMD_WRITE); if(TIMEOUT) return; + strcat(filename, "#F1"); + tmpword = (word) *BASIC_LOMEM; + append_hex_tmpword(filename); + // send filename send_string_to_MCU(filename); if(TIMEOUT) return; @@ -19,6 +28,8 @@ void comando_save() { return; } + woz_puts("SAVING\r"); + // send file size //tmpword = ((word) *BASIC_HIMEM) - ((word)*BASIC_LOMEM) + 512; // in assembly: diff --git a/demos/sdcard/cmd_write.h b/demos/sdcard/cmd_write.h index 0e14758..cc3e888 100644 --- a/demos/sdcard/cmd_write.h +++ b/demos/sdcard/cmd_write.h @@ -60,7 +60,7 @@ void comando_write() { // print feedback to user woz_putc('\r'); woz_puts(filename); - woz_puts(": "); + woz_puts(":\r"); woz_print_hexword(start_address); woz_putc('.'); woz_print_hexword(end_address); diff --git a/demos/sdcard/console.h b/demos/sdcard/console.h index dbc1a10..4baad6e 100644 --- a/demos/sdcard/console.h +++ b/demos/sdcard/console.h @@ -1,5 +1,5 @@ #include -#include +#include byte **const BASIC_LOMEM = (byte **) 0x004a; // lomem pointer used by integer BASIC byte **const BASIC_HIMEM = (byte **) 0x004c; // himem pointer used by integer BASIC @@ -15,8 +15,9 @@ byte *const filename = (byte *) (KEYBUFSTART+KEYBUFLEN+6 ); // [33] stores a byte *const hex1 = (byte *) (KEYBUFSTART+KEYBUFLEN+6+33 ); // [5] stores a hex parameter byte *const hex2 = (byte *) (KEYBUFSTART+KEYBUFLEN+6+33+5); // [5] stores a hex parameter -const byte OK_RESPONSE = 0x00; -const byte ERR_RESPONSE = 0xFF; +const byte OK_RESPONSE = 0x00; +const byte WAIT_RESPONSE = 0x01; +const byte ERR_RESPONSE = 0xFF; // command constants, which are also byte commands to send to the MCU const byte CMD_READ = 0; @@ -127,6 +128,27 @@ void hex_to_word(byte *str) { if(i>4 || i==0) hex_to_word_ok = 0; } +void strcat(char *dest, char *src) { + while(*dest) dest++; + while(*src) *dest++ = *src++; + *dest = 0; +} + +void append_hex_digit(char *dest, byte digit) { + while(*dest) dest++; + if(digit<10) digit += '0'; + else digit += 'A' - 10; + *dest++ = digit; + *dest = 0; +} + +void append_hex_tmpword(char *dest) { + append_hex_digit(dest, *((byte *)&tmpword+1) >> 4); + append_hex_digit(dest, *((byte *)&tmpword+1) & 0x0F); + append_hex_digit(dest, *((byte *)&tmpword+0) >> 4); + append_hex_digit(dest, *((byte *)&tmpword+0) & 0x0F); +} + #include "cmd_read.h" #include "cmd_write.h" #include "cmd_load.h" @@ -238,7 +260,7 @@ void console() { woz_puts("?MISSING FILENAME"); continue; } - comando_load(); + comando_load_bas(); } else if(cmd == CMD_SAVE) { get_token(filename, 32); // parse filename @@ -246,7 +268,33 @@ void console() { woz_puts("?MISSING FILENAME"); continue; } - comando_save(); + //comando_save(); + + get_token(hex1, 4); + if(hex1[0] != 0) { + // it's SAVE binary file + hex_to_word(hex1); + if(!hex_to_word_ok) { + woz_puts("?BAD ADDRESS"); + continue; + } + start_address = tmpword; + + strcat(filename, "#06"); + append_hex_tmpword(filename); + + get_token(hex2, 4); + hex_to_word(hex2); + if(!hex_to_word_ok) { + woz_puts("?BAD ADDRESS"); + continue; + } + end_address = tmpword; + comando_write(); + } + else { + comando_save_bas(); + } } else if(cmd == CMD_TYPE) { get_token(filename, 32); // parse filename diff --git a/demos/sdcard/sdcard.c b/demos/sdcard/sdcard.c index 4e9a606..a353fa9 100644 --- a/demos/sdcard/sdcard.c +++ b/demos/sdcard/sdcard.c @@ -121,6 +121,16 @@ void send_string_to_MCU(char *msg) { } } +// receive a string sent by the MCU +void receive_string_from_MCU(char *dest) { + while(1) { + byte data = receive_byte_from_MCU(); + *dest++ = data; + if(TIMEOUT) break; + if(data == 0) break; // string terminator + } +} + // print a string sent by the MCU void print_string_response() { while(1) {