diff --git a/demos/sdcard/README.md b/demos/sdcard/README.md new file mode 100644 index 0000000..1be7000 --- /dev/null +++ b/demos/sdcard/README.md @@ -0,0 +1,31 @@ +# SD CARD COMMANDS + +## I/O +READ,WRITE for pure binaries, +LOAD,BSAVE,RUN for basic/prodos format and .PRG +TYPE +DUMP + +## FILE +DEL +REN +COPY + +## DIR +DIR +CD +MKDIR +RMDIR + +## MISC +HELP +JMP +STAT +MOUNT +TIME +EXIT + +/* +1234567890123456789012345678901234567890 +0000: 00 00 00 00 00 00 00 00 12345678 +*/ diff --git a/demos/sdcard/apple1_sdcard/apple1_sdcard.ino b/demos/sdcard/apple1_sdcard/apple1_sdcard.ino index 4ddbe5b..e362353 100644 --- a/demos/sdcard/apple1_sdcard/apple1_sdcard.ino +++ b/demos/sdcard/apple1_sdcard/apple1_sdcard.ino @@ -1,57 +1,65 @@ +// 1 per SD card normale, 0 per SDFat + +#include + +#define USE_SD_H 0 + +#include + +#if USE_SD_H +#include +#else +#include "SdFat.h" +SdFat SD; +#endif + +#define SD_CS_PIN SS + +/* + +-----------+ +-----------------+ + | | | ARDUINO | + | VIA 6522 | | NANO | + | | | | + | PA0 |<------ bit 0 ------>| D2 | +-------\ + | PA1 |<------ bit 1 ------>| D3 D10 |----- SS ------>| \ + | PA2 |<------ bit 2 ------>| D4 D11 |----- MOSI ---->| SD | + | PA3 |<------ bit 3 ------>| D5 D12 |<---- MISO -----| CARD | + | PA4 |<------ bit 4 ------>| D6 D13 |----- SCK ----->| | + | PA5 |<------ bit 5 ------>| D7 | +--------+ + | PA6 |<------ bit 6 ------>| D8 | + | PA7 |<------ bit 7 ------>| D9 | + | | | | + | PB7 |<---- MCU_STROBE ----| D14/A0 | + | PB0 |--- CPU_STROBE ----->| D15/A1 | + | | | | + +-----------+ +-----------------+ +*/ + // pin definitions -// VIA 6522 connections +#define BIT0 2 +#define BIT1 3 +#define BIT2 4 +#define BIT3 5 +#define BIT4 6 +#define BIT5 7 +#define BIT6 8 +#define BIT7 9 -#define D0 1 /* I/O data bit 0, connects to PA0 on the VIA */ -#define D1 2 /* I/O data bit 1, connects to PA1 on the VIA */ -#define D2 3 /* I/O data bit 2, connects to PA2 on the VIA */ -#define D3 4 /* I/O data bit 3, connects to PA3 on the VIA */ -#define D4 5 /* I/O data bit 4, connects to PA4 on the VIA */ -#define D5 6 /* I/O data bit 5, connects to PA5 on the VIA */ -#define D6 7 /* I/O data bit 6, connects to PA6 on the VIA */ -#define D7 8 /* I/O data bit 7, connects to PA7 on the VIA */ +/* 10,11,12,13 reserved for SD card */ -#define CPU_STROBE 9 /* PB0 => MCU 1=cpu byte is available on the data port; 0 cpu is waiting */ -#define MCU_STROBE 10 /* PB1 <= MCU 1=mcu byte is available on the data port; 0 mcu is waiting */ +#define MCU_STROBE 14 +#define CPU_STROBE 15 // indicates that a timeout occurred during wait() int TIMEOUT = 0; -void setup() { - // debug on serial - Serial.begin(9600); - - pinMode(CPU_STROBE, INPUT); - pinMode(MCU_STROBE, OUTPUT); - - digitalWrite(MCU_STROBE, LOW); -} - -void loop() { - // applicazione di esempio: manda un messaggio quando riceve il comando 42 - TIMEOUT = 0; - - int data = receive_byte_from_cpu(); - - if(data == 42 && !TIMEOUT) { - - Serial.println("command 42 received from CPU"); - char *msg = "HELLO WORLD!\r\n"; - for(int t=0; t0) { + Serial.println("match!"); + } +} + +const int CMD_READ = 0; +const int CMD_WRITE = 1; +const int CMD_DIR = 2; + +const int ERR_RESPONSE = 255; +const int OK_RESPONSE = 0; + +char filename[64]; + +// ************************************************************************************** +// ************************************************************************************** +// ********************************* DIR *********************************************** +// ************************************************************************************** +// ************************************************************************************** + +void printDirectory(File dir, int numTabs) { + + while (true) { + if(TIMEOUT) break; + + File entry = dir.openNextFile(); + if (! entry) { + // no more files + break; + } + + // indentazione delle sottodirectory + for (uint8_t i = 0; i < numTabs; i++) { + Serial.print('\t'); + send_byte_to_cpu(32); + send_byte_to_cpu(32); + send_byte_to_cpu(32); + } + + // nome del file + char *msg; +#if USE_SD_H + msg = entry.name(); +#else + entry.getName(filename, 64); + msg = filename; +#endif + Serial.print(msg); + for(int t=0; t> 8) & 0xFF); + if(TIMEOUT) return; + Serial.println("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("timeout, bytes sent: "); + Serial.println(bytes_sent); + return; + } + + Serial.println("file read ok"); +} + +void send_string_to_cpu(char *msg) { + while(1) { + int c = *msg++; + send_byte_to_cpu(c); + if(TIMEOUT) break; + if(c==0) break; + } +} + +void receive_string_from_cpu(char *msg) { + while(1) { + int c = receive_byte_from_cpu(); + if(TIMEOUT) break; + *msg++ = c; + if(c==0) break; + } +} + +// ************************************************************************************** +// ************************************************************************************** +// ********************************* CMD_WRITE ***************************************** +// ************************************************************************************** +// ************************************************************************************** + +int receive_word_from_cpu() { + int lo = receive_byte_from_cpu(); + int hi = receive_byte_from_cpu(); + int data = lo | (hi << 8); + return data; +} + +void comando_write() { + Serial.println("command CMD_WRITE received from CPU"); + + // reads filename as 0 terminated string + receive_string_from_cpu(filename); + if(TIMEOUT) return; + Serial.print("file to write: "); + Serial.println(filename); + + if(SD.exists(filename)) { + Serial.println("file already exist"); + send_byte_to_cpu(ERR_RESPONSE); + send_string_to_cpu("?ALREADY EXISTS"); + return; + } + + // open the file + File myFile = SD.open(filename, FILE_WRITE); + if(!myFile) { + Serial.println("error opening file for write"); + send_byte_to_cpu(ERR_RESPONSE); + send_string_to_cpu("?CAN'T CREATE FILE"); + return; + } + Serial.println("file opened for write on the SD card"); + + // ok response + send_byte_to_cpu(OK_RESPONSE); + if(TIMEOUT) return; + Serial.println("first ok response sent to CPU"); + + // get file size low and high byte + int size = receive_word_from_cpu(); + if(TIMEOUT) return; + Serial.print("received file size: "); + Serial.println(size); + + int error = 0; + for(int t=0;t=start && t<=end)) continue; + + if(row == 0) { + woz_putc('\r'); + woz_print_hexword(t); + woz_puts(": "); + } + + woz_print_hex(data); + woz_putc(' '); + + row++; + row &= 7; + + if(apple1_readkey()) { + woz_puts("*BRK*\r"); + break; + } + } +} diff --git a/demos/sdcard/cmd_load.h b/demos/sdcard/cmd_load.h new file mode 100644 index 0000000..d260934 --- /dev/null +++ b/demos/sdcard/cmd_load.h @@ -0,0 +1,80 @@ +// LOAD: for basic files in prodos format +// it is like a normal CMD_READ + +// PRODOS format: +// "A","1", 510 bytes low memory, basic program + +void comando_load(char *filename, byte cmd) { + + // send command byte + send_byte_to_MCU(CMD_READ); + if(TIMEOUT) return; + + // send filename + send_string_to_MCU(filename); + if(TIMEOUT) return; + + // response + byte response = receive_byte_from_MCU(); + if(TIMEOUT) return; + + if(response == ERR_RESPONSE) { + // error with file, print message + print_string_response(); + return; + } + + // get file length + word len = receive_word_from_mcu(); + if(TIMEOUT) return; + + // get file bytes + byte *dest = (byte *) 0; + for(word t=0;t!=len;t++) { + byte data = receive_byte_from_MCU(); + if(TIMEOUT) return; + + if((t==0 && data!=0x41) || (t==1 && data!=0x31)) { + woz_puts("?NOT CFFA1/PRODOS FORMAT"); + return; + } + else if(t<0x004a) { + // skip zone $00-$49 + } + else if(t<0x0100) { + // writes in the zone $4a-$ff (BASIC pointers) + *dest = data; + } + else if(t<0x1ff) { + // skip zone $100-$1ff (stack) + } + else if(t==0x1ff) { + // basic program chuck follows, move the pointer + dest = (byte *) ((*BASIC_LOMEM) -1); // compensate for the increment in the loop + } + else { + // writes in the BASIC program zone + *dest = data; + } + + dest++; + if(((byte)t) == 0) woz_putc('.'); + } + + // print feedback to user + woz_putc('\r'); + woz_puts(filename); + woz_puts(": LOMEM="); + woz_print_hexword(*BASIC_LOMEM); + woz_puts(" HIMEM="); + woz_print_hexword(*BASIC_HIMEM); + woz_puts("\rOK"); + + // executes basic program $EFEC = RUN entry point + if(cmd == CMD_RUN) { + woz_putc('\r'); + asm { + jmp $EFEC + } + } +} diff --git a/demos/sdcard/cmd_read.h b/demos/sdcard/cmd_read.h new file mode 100644 index 0000000..bd1af3d --- /dev/null +++ b/demos/sdcard/cmd_read.h @@ -0,0 +1,50 @@ +// READ: +// CPU sends CMD_READ + filename as 0 terminated string +// MCU sends $00 + 2 bytes file length (MSB first) + file data bytes (if OK) +// MCU sends $FF + string error description (if error) +// +void comando_read(char *filename, word start) { + + // send command byte + send_byte_to_MCU(CMD_READ); + if(TIMEOUT) return; + + // send filename + send_string_to_MCU(filename); + if(TIMEOUT) return; + + // response + byte response = receive_byte_from_MCU(); + if(TIMEOUT) return; + + if(response == ERR_RESPONSE) { + // error with file, print message + print_string_response(); + return; + } + + // get file length + word len = receive_word_from_mcu(); + if(TIMEOUT) return; + + // get file bytes + byte *dest = (byte *) start; + for(word t=0;t!=len;t++) { + byte data = receive_byte_from_MCU(); + if(TIMEOUT) return; + *dest++ = data; + if(((byte)t) == 0) woz_putc('.'); + } + + // print feedback to user + woz_putc('\r'); + woz_puts(filename); + woz_puts(": "); + woz_print_hexword(start); + woz_putc('.'); + woz_print_hexword(start+len-1); + woz_puts(" ("); + utoa(len, filename, 10); // use filename as string buffer + woz_puts(filename); + woz_puts(" BYTES)\rOK"); +} diff --git a/demos/sdcard/cmd_save.h b/demos/sdcard/cmd_save.h new file mode 100644 index 0000000..8c3994e --- /dev/null +++ b/demos/sdcard/cmd_save.h @@ -0,0 +1,62 @@ +void comando_save(char *filename) { + + // send command byte + send_byte_to_MCU(CMD_WRITE); + if(TIMEOUT) return; + + // send filename + send_string_to_MCU(filename); + if(TIMEOUT) return; + + // get first response + byte response = receive_byte_from_MCU(); + if(TIMEOUT) return; + if(response == ERR_RESPONSE) { + // error with file, print message + print_string_response(); + return; + } + + // send file size + word len = ((word) *BASIC_HIMEM) - ((word)*BASIC_LOMEM) + 512; + send_word_to_mcu(len); + if(TIMEOUT) return; + + // send actual bytes + + // "A1" header + send_byte_to_MCU('A'); + send_byte_to_MCU('1'); + if(TIMEOUT) return; + + // lowmem + stack chuck + for(byte *ptr=(byte *)2; ptr<=(byte *)0x1ff; ptr++) { + send_byte_to_MCU(*ptr); + if(TIMEOUT) return; + } + + // basic data + for(word ptr=*BASIC_LOMEM; ptr<*BASIC_HIMEM; ptr++) { + send_byte_to_MCU(*((byte *)ptr)); + if(TIMEOUT) return; + if(((byte)ptr) == 0) woz_putc('.'); + } + + // get second response + response = receive_byte_from_MCU(); + if(TIMEOUT) return; + if(response == ERR_RESPONSE) { + // error with file, print message + print_string_response(); + return; + } + + // print feedback to user + woz_putc('\r'); + woz_puts(filename); + woz_puts(": LOMEM="); + woz_print_hexword(*BASIC_LOMEM); + woz_puts(" HIMEM="); + woz_print_hexword(*BASIC_HIMEM); + woz_puts("\rOK"); +} diff --git a/demos/sdcard/cmd_type.h b/demos/sdcard/cmd_type.h new file mode 100644 index 0000000..b082567 --- /dev/null +++ b/demos/sdcard/cmd_type.h @@ -0,0 +1,35 @@ +void comando_type(char *filename) { + + // send command byte + send_byte_to_MCU(CMD_READ); + if(TIMEOUT) return; + + // send filename + send_string_to_MCU(filename); + if(TIMEOUT) return; + + // response + byte response = receive_byte_from_MCU(); + if(TIMEOUT) return; + + if(response == ERR_RESPONSE) { + // error with file, print message + print_string_response(); + return; + } + + // get file length + word len = receive_word_from_mcu(); + if(TIMEOUT) return; + + // get file bytes + for(word t=0;t!=len;t++) { + byte data = receive_byte_from_MCU(); + if(TIMEOUT) return; + woz_putc(data); + if(apple1_readkey()) { + woz_puts("*BRK*\r"); + break; + } + } +} diff --git a/demos/sdcard/cmd_write.h b/demos/sdcard/cmd_write.h new file mode 100644 index 0000000..5942693 --- /dev/null +++ b/demos/sdcard/cmd_write.h @@ -0,0 +1,53 @@ +void comando_write(char *filename, word start, word end) { + + // send command byte + send_byte_to_MCU(CMD_WRITE); + if(TIMEOUT) return; + + // send filename + send_string_to_MCU(filename); + if(TIMEOUT) return; + + // get first response + byte response = receive_byte_from_MCU(); + if(TIMEOUT) return; + if(response == ERR_RESPONSE) { + // error with file, print message + print_string_response(); + return; + } + + // send file size + word len = end-start + 1; + send_word_to_mcu(len); + if(TIMEOUT) return; + + // send actual bytes + byte *ptr = (byte *) start; + for(word t=0;t +#include + +word *const BASIC_LOMEM = (word *) 0x004a; // lomem pointer used by integer BASIC +word *const BASIC_HIMEM = (word *) 0x004c; // himem pointer used by integer BASIC +byte *const KEYBUF = (byte *) 0x0200; // use the same keyboard buffer as in WOZ monitor + +#define KEYBUFSTART (0x200) +#define KEYBUFLEN (40) + +// keyboard buffer 0x200-27F uses only the first 40 bytes, the rest is recycled for free mem + +byte *const command = (byte *) (KEYBUFSTART+KEYBUFLEN ); // [6] stores a 5 character command +byte *const filename = (byte *) (KEYBUFSTART+KEYBUFLEN+6 ); // [33] stores a filename or pattern +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 ERR_RESPONSE = 0xFF; + +// command constants, which are also byte commands to send to the MCU +const byte CMD_READ = 0; +const byte CMD_WRITE = 1; +const byte CMD_DIR = 2; +const byte CMD_TIME = 3; +const byte CMD_LOAD = 4; +const byte CMD_RUN = 5; +const byte CMD_SAVE = 6; +const byte CMD_TYPE = 7; +const byte CMD_DUMP = 8; +const byte CMD_EXIT = 9; + +// the list of recognized commands +byte *DOS_COMMANDS[] = { + "READ", + "WRITE", + "DIR", + "TIME", + "LOAD", + "RUN", + "SAVE", + "TYPE", + "DUMP", + "EXIT" +}; + +// parse a string, get the first string delimited by space or end of string +// returns the parsed string in dest +// returns the number of character to advance the pointer +// leading and trailing spaces are ignored +// max is the (maximum) size of dest +byte get_token(byte *source, byte *dest, byte max) { + byte i = 0; + byte j = 0; + byte first_char_found = 0; + + while(1) { + byte c = source[i]; + if(c == 0) { + break; + } + else if(c == 32) { + if(first_char_found) { + break; + } + } + else { + first_char_found = 1; + dest[j++] = c; + } + if(j='0' && c<='9') res += (c-'0'); + else if(c>='A' && c<='F') res += (c-65)+0x0A; + else hex_to_word_ok = 0; + } + if(i>4 || i==0) hex_to_word_ok = 0; + return res; +} + +#include "cmd_read.h" +#include "cmd_write.h" +#include "cmd_load.h" +#include "cmd_save.h" +#include "cmd_type.h" +#include "cmd_dump.h" +#include "cmd_dir.h" + +void console() { + + VIA_init(); + + // 1234567890123456789012345678901234567890 + woz_puts("\rREAD,WRITE,LOAD,RUN,SAVE,TYPE,DUMP,DIR\r" + "TIME,EXIT\r"); + + woz_puts("\rSD CARD DOS 1.0\r"); + + // main loop + while(1) { + + // clear input buffer + for(byte i=0; i #include +#include byte *const PORTB = (byte *) 0xA000; // port B register byte *const PORTA = (byte *) 0xA001; // port A register @@ -9,19 +44,21 @@ byte *const DDRA = (byte *) 0xA003; // port B data direction register #define CPU_STROBE(v) (*PORTB = (v)) /* CPU strobe is bit 0 OUTPUT */ #define MCU_STROBE (*PORTB & 128) /* MCU strobe is bit 7 INPUT */ -byte TIMEOUT = 0; +__address(3) byte TIMEOUT; +__address(4) word TIMEOUT_MAX = 0x1388; +__address(6) word TIMEOUT_CNT; void wait_mcu_strobe(byte v) { if(TIMEOUT) return; - unsigned int time = 0; + TIMEOUT_CNT = 0; while((v==0 && MCU_STROBE != 0) || (v!=0 && MCU_STROBE == 0)) { - time++; - if(time > 5000) { + TIMEOUT_CNT++; + if(TIMEOUT_CNT > TIMEOUT_MAX) { TIMEOUT = 1; return; - } - } + } + } } void send_byte_to_MCU(byte data) { @@ -33,6 +70,7 @@ void send_byte_to_MCU(byte data) { wait_mcu_strobe(0); // wait for the MCU to set strobe low } +// note: allocates 1 byte for return value byte receive_byte_from_MCU() { *DDRA = 0; // set port A as input CPU_STROBE(0); // set listen @@ -49,47 +87,45 @@ void VIA_init() { CPU_STROBE(0); // initial state } -void messagio_test() { - - TIMEOUT = 0; // resetta il timeout - send_byte_to_MCU(42); // manda il comando 42 - - if(TIMEOUT) { - woz_puts("\rTIMEOUT\r"); - return; +// send a string to the MCY (0 terminator is sent as well) +void send_string_to_MCU(char *msg) { + while(1) { + byte data = *msg++; + send_byte_to_MCU(data); + if(TIMEOUT) break; + if(data == 0) break; } +} - // legge la stringa di ritorno +// print a string sent by the MCU +void print_string_response() { while(1) { byte data = receive_byte_from_MCU(); - if(TIMEOUT) { - woz_puts("\rTIMEOUT\r"); - break; - } - if(data == 0) break; // end of string - woz_putc(data); + if(TIMEOUT) break; + if(data == 0) break; // string terminator + else woz_putc(data); } } -void main() { - - VIA_init(); - - woz_puts("\rMCU TEST\r\r"); - woz_puts("[4] SEND 42 TO MCU\r"); - woz_puts("[0] EXIT\r"); - - byte data; - - // loop continuo - while(1) { - byte key = apple1_getkey(); - - if(key == '4') messagio_test(); - else if(key == '0') { - woz_puts("BYE\r"); - woz_mon(); - } - } +word receive_word_from_mcu() { + word data; + *((byte *)&data) = receive_byte_from_MCU(); + *((byte *)(&data+1)) = receive_byte_from_MCU(); + return data; +} + +void send_word_to_mcu(word data) { + send_byte_to_MCU( *((byte *)&data) ); + send_byte_to_MCU( *((byte *)(&data+1)) ); +} + +#include "console.h" + +void main() { +//#ifdef APPLE1_JUKEBOX +// apple1_eprom_init(); +//#endif + + console(); }