SD card early prototype

This commit is contained in:
nino-porcino 2022-02-07 08:50:35 +01:00
parent 8ea8231ac5
commit 10ce1dfd31
11 changed files with 1119 additions and 119 deletions

31
demos/sdcard/README.md Normal file
View File

@ -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
*/

View File

@ -1,57 +1,65 @@
// 1 per SD card normale, 0 per SDFat
#include <Regexp.h>
#define USE_SD_H 0
#include <SPI.h>
#if USE_SD_H
#include <SD.h>
#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; t<strlen(msg); t++) {
send_byte_to_cpu(msg[t]);
}
send_byte_to_cpu(0); // terminatore stringa
if(!TIMEOUT) Serial.println("message sent to CPU");
else Serial.println("timeout during send to CPU");
}
}
void wait(int pin, int value) {
unsigned long start_time = millis();
unsigned long elapsed;
if(TIMEOUT) break;
if(TIMEOUT) return;
while(digitalRead(pin) != value) {
elapsed = millis() - start_time;
@ -62,18 +70,46 @@ void wait(int pin, int value) {
}
}
int receive_byte_from_cpu() {
// set data port as input
pinMode(D0, INPUT);
pinMode(D1, INPUT);
pinMode(D2, INPUT);
pinMode(D3, INPUT);
pinMode(D4, INPUT);
pinMode(D5, INPUT);
pinMode(D6, INPUT);
pinMode(D7, INPUT);
const int DIR_INPUT = 0;
const int DIR_OUTPUT = 1;
int last_dir; // remember last data port pins direction
void set_data_port_direction(int dir) {
// check if no need to set pins
if(dir == last_dir) return;
if(dir == DIR_INPUT) {
pinMode(BIT0, INPUT);
pinMode(BIT1, INPUT);
pinMode(BIT2, INPUT);
pinMode(BIT3, INPUT);
pinMode(BIT4, INPUT);
pinMode(BIT5, INPUT);
pinMode(BIT6, INPUT);
pinMode(BIT7, INPUT);
}
else {
pinMode(BIT0, OUTPUT);
pinMode(BIT1, OUTPUT);
pinMode(BIT2, OUTPUT);
pinMode(BIT3, OUTPUT);
pinMode(BIT4, OUTPUT);
pinMode(BIT5, OUTPUT);
pinMode(BIT6, OUTPUT);
pinMode(BIT7, OUTPUT);
}
// remember direction
last_dir = dir;
}
int receive_byte_from_cpu() {
// set data port pins as INPUT pins
set_data_port_direction(DIR_INPUT);
// both strobes are 0
// CPU deposits data byte and sets strobe high
@ -81,14 +117,14 @@ int receive_byte_from_cpu() {
// read the data byte
int data =
(digitalRead(D0) << 0) |
(digitalRead(D1) << 1) |
(digitalRead(D2) << 2) |
(digitalRead(D3) << 3) |
(digitalRead(D4) << 4) |
(digitalRead(D5) << 5) |
(digitalRead(D6) << 6) |
(digitalRead(D7) << 7);
(digitalRead(BIT0) << 0) |
(digitalRead(BIT1) << 1) |
(digitalRead(BIT2) << 2) |
(digitalRead(BIT3) << 3) |
(digitalRead(BIT4) << 4) |
(digitalRead(BIT5) << 5) |
(digitalRead(BIT6) << 6) |
(digitalRead(BIT7) << 7);
// after reading the byte, MCU sets strobe high
digitalWrite(MCU_STROBE, HIGH);
@ -103,28 +139,21 @@ int receive_byte_from_cpu() {
}
void send_byte_to_cpu(int data) {
// set data port as output
pinMode(D0, OUTPUT);
pinMode(D1, OUTPUT);
pinMode(D2, OUTPUT);
pinMode(D3, OUTPUT);
pinMode(D4, OUTPUT);
pinMode(D5, OUTPUT);
pinMode(D6, OUTPUT);
pinMode(D7, OUTPUT);
// set data port pins as OUTPUT pins
set_data_port_direction(DIR_OUTPUT);
// both strobes are 0
// put byte on the data port
digitalWrite(D0, data & 1);
digitalWrite(D1, data & 2);
digitalWrite(D2, data & 4);
digitalWrite(D3, data & 8);
digitalWrite(D4, data & 16);
digitalWrite(D5, data & 32);
digitalWrite(D6, data & 64);
digitalWrite(D7, data & 128);
digitalWrite(BIT0, data & 1);
digitalWrite(BIT1, data & 2);
digitalWrite(BIT2, data & 4);
digitalWrite(BIT3, data & 8);
digitalWrite(BIT4, data & 16);
digitalWrite(BIT5, data & 32);
digitalWrite(BIT6, data & 64);
digitalWrite(BIT7, data & 128);
// after depositing data byte, MCU sets strobe high
digitalWrite(MCU_STROBE, HIGH);
@ -138,3 +167,312 @@ void send_byte_to_cpu(int data) {
// wait for CPU to set strobe low
wait(CPU_STROBE, LOW);
}
// *********************************************************************************************
// *********************************************************************************************
// *********************************************************************************************
// *********************************************************************************************
// *********************************************************************************************
// *********************************************************************************************
// *********************************************************************************************
// *********************************************************************************************
void setup() {
// debug on serial
Serial.begin(9600);
#if USE_SD_H
Serial.println(F("SDCARD library: SD.h"));
#else // USE_SD_H
Serial.println(F("SDCARD library: SDFat.h"));
#endif // USE_SD_H
// initialize SD card
if (!SD.begin(SD_CS_PIN)) Serial.println("SD card initialization failed");
else Serial.println("SD card initialized");
// control pins setup
pinMode(CPU_STROBE, INPUT);
pinMode(MCU_STROBE, OUTPUT);
digitalWrite(MCU_STROBE, LOW);
// data pins setup
last_dir = -1; // no previous data direction
set_data_port_direction(DIR_INPUT);
MatchState ms;
char buf [100] = { F("The quick brown fox jumps over the lazy wolf") };
ms.Target (buf);
char result = ms.Match ("f.x");
if(result >0) {
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<strlen(msg); t++) {
send_byte_to_cpu(msg[t]);
}
if (entry.isDirectory()) {
Serial.println("/");
send_byte_to_cpu('/');
send_byte_to_cpu('\r');
printDirectory(entry, numTabs + 1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
send_byte_to_cpu(' ');
send_byte_to_cpu(' ');
/*
// file size
Serial.println(entry.size(), DEC);
msg = itoa(entry.size());
for(int t=0; t<strlen(msg); t++) {
send_byte_to_cpu(msg[t]);
}
*/
send_byte_to_cpu('\r');
}
entry.close();
}
}
// **************************************************************************************
// **************************************************************************************
// ********************************* CMD_READ ******************************************
// **************************************************************************************
// **************************************************************************************
void comando_read() {
Serial.println("command CMD_READ received from CPU");
// reads filename as 0 terminated string
receive_string_from_cpu(filename);
if(TIMEOUT) return;
Serial.print("file to read: ");
Serial.println(filename);
if(!SD.exists(filename)) {
Serial.println("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("error opening file");
send_byte_to_cpu(ERR_RESPONSE);
send_string_to_cpu("?CAN'T OPEN FILE");
return;
}
Serial.println("file opened on the SD card");
// ok response
send_byte_to_cpu(OK_RESPONSE);
if(TIMEOUT) return;
Serial.println("ok response sent to CPU");
// 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("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<size;t++) {
int data = receive_byte_from_cpu();
if(TIMEOUT) break;
int n = myFile.write((unsigned char)data);
if(n!=1) error = 1;
}
// close the file
myFile.close();
if(TIMEOUT) return;
// report write issues
if(error) {
Serial.println("file write error");
send_byte_to_cpu(ERR_RESPONSE);
if(TIMEOUT) return;
send_string_to_cpu("?WRITE ERROR");
return;
}
Serial.println("file read ok");
send_byte_to_cpu(OK_RESPONSE);
}
// **************************************************************************************
// **************************************************************************************
// ********************************* LOOP **********************************************
// **************************************************************************************
// **************************************************************************************
void loop() {
TIMEOUT = 0;
int data = receive_byte_from_cpu();
if(TIMEOUT) return;
if(data == CMD_READ) {
comando_read();
if(TIMEOUT) Serial.println("TIMEOUT during CMD_READ");
}
else if(data == CMD_WRITE) {
comando_write();
if(TIMEOUT) Serial.println("TIMEOUT during CMD_WRITE");
}
else if(data == CMD_DIR) {
Serial.println("command DIR received from CPU");
File root = SD.open("/");
printDirectory(root, 0);
root.close();
// terminates
send_byte_to_cpu(0);
if(TIMEOUT) Serial.println("TIMEOUT during DIR");
Serial.println("command DIR ended");
}
else {
Serial.print("unknown command ");
Serial.print(data);
Serial.println(" received");
}
}

5
demos/sdcard/cmd_dir.h Normal file
View File

@ -0,0 +1,5 @@
void comando_dir() {
send_byte_to_MCU(CMD_DIR);
if(TIMEOUT) return;
print_string_response();
}

53
demos/sdcard/cmd_dump.h Normal file
View File

@ -0,0 +1,53 @@
void comando_dump(char *filename, word start, word end) {
// 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;
// 1234567890123456789012345678901234567890
// 0000: 00 00 00 00 00 00 00 00
// get file bytes
byte row = 0;
for(word t=0;t!=len;t++) {
byte data = receive_byte_from_MCU();
if(TIMEOUT) return;
if(!(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;
}
}
}

80
demos/sdcard/cmd_load.h Normal file
View File

@ -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
}
}
}

50
demos/sdcard/cmd_read.h Normal file
View File

@ -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");
}

62
demos/sdcard/cmd_save.h Normal file
View File

@ -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");
}

35
demos/sdcard/cmd_type.h Normal file
View File

@ -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;
}
}
}

53
demos/sdcard/cmd_write.h Normal file
View File

@ -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<len;t++) {
send_byte_to_MCU(*ptr++);
if(TIMEOUT) return;
if(((byte)t) == 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(": ");
woz_print_hexword(start);
woz_putc('.');
woz_print_hexword(end);
woz_puts(" (");
utoa(len, filename, 10); // use filename as string buffer
woz_puts(filename);
woz_puts(" BYTES)\rOK");
}

257
demos/sdcard/console.h Normal file
View File

@ -0,0 +1,257 @@
#include <stdlib.h>
#include <stdlib.h>
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<max) i++;
else break;
}
dest[j] = 0;
return i+1;
}
// returns the command code or 0xff if not recognized
byte find_command() {
for(byte cmd=0; cmd<sizeof(DOS_COMMANDS); cmd++) {
if(strcmp(command, DOS_COMMANDS[cmd]) == 0) return cmd;
}
return 0xFF;
}
// converts the hexadecimal string argument to 16 bit word
byte hex_to_word_ok;
word hex_to_word(byte *str) {
hex_to_word_ok = 1;
word res=0;
byte c;
byte i;
for(i=0; c=str[i]; ++i) {
res = res << 4;
if(c>='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<KEYBUFLEN; i++) KEYBUF[i] = 0;
// input from keyboard
woz_puts("\r]");
apple1_input_line(KEYBUF, KEYBUFLEN);
woz_putc('\r');
// decode command
byte *ptr = KEYBUF;
ptr += get_token(ptr, command, 5);
byte cmd = find_command();
if(cmd == CMD_READ) {
ptr += get_token(ptr, filename, 32); // parse filename
if(filename[0] == 0) {
woz_puts("?MISSING FILENAME");
continue;
}
ptr += get_token(ptr, hex1, 4); // parse hex start address
word start = hex_to_word(hex1);
if(!hex_to_word_ok) {
woz_puts("?BAD ADDRESS");
continue;
}
comando_read(filename, start);
}
else if(cmd == CMD_WRITE) {
// parse filename
ptr += get_token(ptr, filename, 32);
if(filename[0] == 0) {
woz_puts("?MISSING FILENAME");
continue;
}
// parse hex start address
ptr += get_token(ptr, hex1, 4);
word start = hex_to_word(hex1);
if(!hex_to_word_ok) {
woz_puts("?BAD ADDRESS");
continue;
}
// parse hex end address
ptr += get_token(ptr, hex2, 4);
word end = hex_to_word(hex2);
if(!hex_to_word_ok) {
woz_puts("?BAD ADDRESS");
continue;
}
comando_write(filename, start, end);
}
else if(cmd == CMD_DIR) {
comando_dir();
}
else if(cmd == CMD_TIME) {
ptr += get_token(ptr, hex1, 4); // parse hex timeout value
if(strlen(hex1)!=0) {
TIMEOUT_MAX = hex_to_word(hex1);
if(!hex_to_word_ok) {
woz_puts("?BAD ARGUMENT");
continue;
}
}
woz_puts("TIMEOUT_MAX: ");
woz_print_hexword(TIMEOUT_MAX);
}
else if(cmd == CMD_LOAD || cmd == CMD_RUN) {
ptr += get_token(ptr, filename, 32); // parse filename
if(filename[0] == 0) {
woz_puts("?MISSING FILENAME");
continue;
}
comando_load(filename, cmd);
}
else if(cmd == CMD_SAVE) {
ptr += get_token(ptr, filename, 32); // parse filename
if(filename[0] == 0) {
woz_puts("?MISSING FILENAME");
continue;
}
comando_save(filename);
}
else if(cmd == CMD_TYPE) {
ptr += get_token(ptr, filename, 32); // parse filename
if(filename[0] == 0) {
woz_puts("?MISSING FILENAME");
continue;
}
comando_type(filename);
}
else if(cmd == CMD_DUMP) {
ptr += get_token(ptr, filename, 32); // parse filename
if(filename[0] == 0) {
woz_puts("?MISSING FILENAME");
continue;
}
// parse hex start address
ptr += get_token(ptr, hex1, 4);
word start = hex_to_word(hex1);
if(!hex_to_word_ok) {
woz_puts("?BAD ADDRESS");
continue;
}
// parse hex end address
ptr += get_token(ptr, hex2, 4);
word end = hex_to_word(hex2);
if(!hex_to_word_ok) {
woz_puts("?BAD ADDRESS");
continue;
}
if(end<start) end = 0xFFFF;
comando_dump(filename, start, end);
}
else if(cmd == CMD_EXIT) {
woz_puts("BYE\r");
woz_mon();
}
else {
if(strlen(command)!=0) {
woz_puts(command);
woz_puts("??");
}
}
if(TIMEOUT) {
woz_puts("?I/O ERROR");
TIMEOUT = 0;
}
}
}

View File

@ -1,5 +1,40 @@
// TODO comando CD
// TODO comando MKDIR
// TODO comando RMDIR
// TODO comando DEL
// TODO comando REN
// TODO comando COPY
// TODO comando JMP
// TODO comando RUN a vuoto
// TODO questione del puntino che va in timeout
#define APPLE1_USE_WOZ_MONITOR 1
#pragma start_address(0x8000)
#pragma zp_reserve(0x4a)
#pragma zp_reserve(0x4b)
#pragma zp_reserve(0x4c)
#pragma zp_reserve(0x4d)
#pragma zp_reserve(0x4e)
#pragma zp_reserve(0x4f)
#pragma zp_reserve(0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f)
#pragma zp_reserve(0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f)
#pragma zp_reserve(0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f)
#pragma zp_reserve(0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f)
#pragma zp_reserve(0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f)
#pragma zp_reserve(0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f)
#pragma zp_reserve(0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf)
#pragma zp_reserve(0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf)
#pragma zp_reserve(0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf)
#pragma zp_reserve(0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf)
#pragma zp_reserve(0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef)
#pragma zp_reserve(0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff)
#include <utils.h>
#include <apple1.h>
#include <string.h>
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();
}