Emulator for the Sharp CE-140F Diskette Driver

Dependencies:   mbed SDFileSystem

This is an attempt (for the time being, it isn't complete yet!) of emulating the Sharp CE-140F disk drive with a Nucleo board, attached to a Sharp Pocket Computer though its platform-proprietary 11-pin interface.

main.cpp

Committer:
ffxx68
Date:
24 months ago
Revision:
2:8b6005719a58
Parent:
1:cbecc06355fa
Child:
3:f829cb6eea98
Child:
4:6e0a5ddfcb82

File content as of revision 2:8b6005719a58:

#include "mbed.h"

#define BUF_SIZE 1024 // data depth
#define DEBUG_SIZE 1024 // print debug buffer
#define NIBBLE_DELAY_1 20 // us
#define NIBBLE_DELAY_2 20 // us
#define BIT_DELAY_1 100 // us
#define BIT_DELAY_2 2000 // us
#define ACK_DELAY 20000 // us
#define ACK_TIMEOUT 1 // s
#define DATA_WAIT 9000 // us
#define IN_DATAREADY_TIMEOUT 50000 // ms

// input ports
DigitalIn   in_BUSY   (D4);    
InterruptIn irq_BUSY  (D4);    
DigitalIn   in_D_OUT  (D5);    
InterruptIn irq_DOUT  (D5);
DigitalIn   in_X_OUT  (D7);     
InterruptIn irq_X_OUT (D7);
DigitalIn   in_D_IN   (D8);  
DigitalIn   in_SEL_2   (D10);     
DigitalIn   in_SEL_1   (D11);
// output ports  
DigitalOut        out_ACK   (D9);     
// info led
DigitalOut        infoLed   (LED1);
// timers
Timer             mainTimer;
Timeout           ackOffTimeout;
Timeout           inDataReadyTimeout;
Ticker            outDataTicker;


// PC comms
RawSerial         pc(USBTX, USBRX);
InterruptIn       btn(USER_BUTTON);

volatile uint8_t  deviceCode;
volatile uint8_t  bitCount;
volatile char     debugBuf[DEBUG_SIZE];
volatile char     debugLine[80];
volatile bool     highNibble = false;
volatile char     inDataBuf[BUF_SIZE];
volatile char     outDataBuf[BUF_SIZE];
volatile uint16_t inBufPosition;
volatile uint16_t outBufPosition;
volatile int checksum = 0;

void debug_log(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    sprintf((char*)debugLine,format,args);
    sprintf((char*)debugLine,"%d %s\n\r",mainTimer.read_us(),(char*)debugLine);
    strcat((char*)debugBuf,(char*)debugLine);
    va_end(args);
}

// a "timeout" on ACK line 
void  ackOff ( void ) {
    
    out_ACK = 0; 
    infoLed = 0;
     
}

char CheckSum(char b) {
    checksum = (checksum + b) & 0xff;
    return b;
}

void outDataAppend(char b) {
    outDataBuf[(outBufPosition++)%BUF_SIZE ] = b;
}

void sendString(char* s) {
    for (int i=0;i<strlen(s);i++){
        outDataAppend(CheckSum(s[i]));
    }
}

void inDataReady ( void ) {
    // Command processing here ...
    sprintf ( (char*)debugLine, "%d Processing...\n\r", mainTimer.read_us()) ; strcat ((char*)debugBuf, (char*)debugLine) ;
    if (  inBufPosition > 0 ) {
        sprintf ( (char*)debugLine, "%d inBufPosition %d...\n\r", mainTimer.read_us(), inBufPosition) ; strcat ((char*)debugBuf, (char*)debugLine) ;

        // Verify checksum
        checksum=0;
        for (int i =0;i<inBufPosition;i++) {
            checksum = (inDataBuf[i]+checksum) & 0xff;
        }   
        debug_log ( "checksum %d vs %d" , checksum, inDataBuf[inBufPosition]); 
        
        // ....
        debug_log ( "command 0x%02X" , inDataBuf[0]); 
        checksum=0;
        outBufPosition = 0;
        
        if ( inDataBuf[0] == 0x07 ) {
            // case 0x07: process_FILES_LIST(1);break;
            outDataAppend(0x00);
            sendString("X:TEST    .BAS ");        
            outDataAppend(checksum);
            debug_log ( "dataout %d" , outBufPosition); 
        }
    }
}

void nibbleReady ( void ) {
    
    //pc.putc('n');
    if ( out_ACK == 0 ) {
        uint8_t dataByte;
        wait_us ( NIBBLE_DELAY_1 );
        if ( highNibble ) {
            highNibble = false;
            dataByte <<= 4;
            dataByte |= in_SEL_1 + (in_SEL_2<<1) + (in_D_OUT<<2) + (in_D_IN<<3);
            //pc.putc(dataByte);
            inDataBuf [ (inBufPosition++)%BUF_SIZE ] = dataByte ; // circular storage for safety; may cut off data!
            // Data processing starts after last byte (timeout reset at each byte received) 
            inDataReadyTimeout.attach( &inDataReady, IN_DATAREADY_TIMEOUT );
        } else {
            dataByte = in_SEL_1 + (in_SEL_1<<1) + (in_D_OUT<<2) + (in_D_IN<<3);
            //pc.putc(dataByte);
            highNibble = true;
        }
        sprintf ( (char*)debugLine, "%d nibble %d 0x%02X\n\r", mainTimer.read_us(), highNibble, dataByte ); strcat ( (char*)debugBuf, (char*)debugLine) ;
        out_ACK = 1;
        infoLed = 1;
        ackOffTimeout.attach( &ackOff, ACK_TIMEOUT ); // max time high ack
    } else {
        out_ACK = 0;
    }
}

void nibbleAck ( void ) {
    if ( out_ACK == 1 ) {
        wait_us ( NIBBLE_DELAY_2 );
        out_ACK = 0;
        infoLed = 0;
    }
}


// Serial bit receive
void bitReady ( void ) {
    uint32_t nTimeout;
    
    //pc.putc('b');
    if ( out_ACK == 1 ) {
        bool bit;
        wait_us ( BIT_DELAY_1 );
        
        bit = in_D_OUT; // get bit value
        //pc.putc(0x30+bit);pc.putc(' ');
        sprintf ( (char*)debugLine, "%d bit %d: %d\n\r", mainTimer.read_us(), bitCount, bit ); strcat ( (char*)debugBuf, (char*)debugLine) ;
        out_ACK = 0; // tell PC a bit has been received
        infoLed = 0;
        
        deviceCode>>=1;
        if (bit) deviceCode|=0x80;
        if ((bitCount=(++bitCount)&7)==0) {
            // 8 bits received
            irq_BUSY.rise(NULL); // detach this IRQ
            pc.printf(" Device 0x%02X\n\r",deviceCode);
            sprintf ( (char*)debugLine, "%d device 0x%02X\n\r", mainTimer.read_us(), deviceCode ); strcat ((char*)debugBuf, (char*)debugLine) ;
            if ( deviceCode == 0x41 ) {
                // PC is asking for a CE140F (device code SHOULD be 0x41) - Here we are!
                sprintf ( (char*)debugLine, "%d CE140F\n\r", mainTimer.read_us()); strcat ((char*)debugBuf, (char*)debugLine) ;
                // ... start data handling ...
                highNibble = false;
                // wait for both BUSY and X_OUT to go down, before starting data receive
                nTimeout = 10000; // timeout: 1s
                while ( ( in_X_OUT || in_BUSY ) && (nTimeout--) ) {
                    wait_us (100);
                }
                if (nTimeout) {
                    sprintf ( (char*)debugLine, "%d Data...\n\r", mainTimer.read_us()) ; strcat ((char*)debugBuf, (char*)debugLine) ;
                    out_ACK = 1;
                    infoLed = 1;
                    wait_us ( DATA_WAIT ); 
                    highNibble = false;             
                    out_ACK = 0; // ready for receiving new data
                    infoLed = 0;
                    inBufPosition = 0;
                    irq_BUSY.rise(&nibbleReady); 
                    irq_BUSY.fall(&nibbleAck);
                } else {
                    pc.putc('x');
                    sprintf ( (char*)debugLine, "%d Timeout!\n\r", mainTimer.read_us()) ; strcat ((char*)debugBuf, (char*)debugLine) ;
                }
            } 
        } else {
            wait_us ( BIT_DELAY_2 );
            out_ACK = 1; // ready for a new bit
            infoLed = 1;
            ackOffTimeout.attach( &ackOff, ACK_TIMEOUT ); // max time high ack
        }
    }
    
}



void startDeviceCodeSeq ( void ) {
    
    wait_us (BIT_DELAY_1);
    if ( in_D_OUT == 1 ) {
        // Device Code transfer starts with both X_OUT and DOUT high
        // (X_OUT high with DOUT low is for cassette write)
        
        infoLed = 1;
        out_ACK = 1;
        ackOffTimeout.attach( &ackOff, ACK_TIMEOUT ); // max time high ack
        
        bitCount = 0;
        deviceCode = 0;
        sprintf ( (char*)debugBuf, "Device\n\r" ); 

        wait_us (ACK_DELAY) ;   //?? wait after eabling trigger?
        // serial bit trigger
        irq_BUSY.rise(&bitReady);
        irq_BUSY.fall(NULL);
        wait_us (ACK_DELAY) ;
    
    }
}


void btnRaised() 
{
    uint8_t i = 20;
    while (i--) { infoLed =! infoLed; wait_ms(20); }
    // printout debug buffer
    pc.printf ( "%s", (char*)debugBuf );
    // reset status
    out_ACK = 0;
    infoLed = 0;
    irq_BUSY.rise(NULL);
    irq_BUSY.fall(NULL);
    sprintf ( (char*)debugBuf, "Reset\n\r" ); 
}

int main(void) {
    uint8_t i = 20;

    pc.baud ( 57600 ); 
    pc.printf ( "CE140F emulator init\n\r" );
    while (i--) { infoLed =! infoLed; wait_ms(20); }
    
    btn.rise(&btnRaised);
    inBufPosition = 0;
    
    // default input pull-down
    in_BUSY.mode(PullNone);
    in_D_OUT.mode(PullNone);
    in_X_OUT.mode(PullNone);
    in_D_IN.mode(PullNone);
    in_SEL_2.mode(PullNone);
    in_SEL_1.mode(PullNone);    
    
    // initial triggers
    irq_X_OUT.rise(&startDeviceCodeSeq);
    
    mainTimer.reset();
    mainTimer.start();
    //sprintf ( (char*)debugBuf, "Start\n\r" );
    
    while (1) {
        wait (1); // logic handled with interrupts and timers
    }
}