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:
- 21 months ago
- Revision:
- 8:70f13d97eb44
- Parent:
- 7:0243d36a655a
File content as of revision 8:70f13d97eb44:
//////////////////////////////////////////////////////// // Sharp CE-140F diskette emulator // // v1 1/6/2022 - adaptation to MBed NUCLEO // of the version received by contact@pockemul.com // //////////////////////////////////////////////////////// #include "mbed.h" #include "commands.h" #define DEBUG 1 #define DEBUG_SIZE 1000 // print debug buffer #define NIBBLE_DELAY_1 100 // us #define NIBBLE_ACK_DELAY 100 // us #define BIT_DELAY_1 200 // 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 // us #define OUT_NIBBLE_DELAY 5000 // us // input ports DigitalIn in_BUSY (PC_0); InterruptIn irq_BUSY (PC_0); DigitalIn in_D_OUT (PC_1); InterruptIn irq_D_OUT (PC_1); DigitalIn in_X_OUT (D6); InterruptIn irq_X_OUT (D6); DigitalIn in_D_IN (D7); DigitalIn in_SEL_2 (D8); DigitalIn in_SEL_1 (D9); // output ports DigitalOut out_ACK (D10); DigitalOut out_D_OUT (D11); DigitalOut out_D_IN (D12); DigitalOut out_SEL_2 (D14); DigitalOut out_SEL_1 (D15); // info led DigitalOut infoLed (LED1); // D13 // timers Timer mainTimer; Timeout ackOffTimeout; Timeout inDataReadyTimeout; Ticker outDataTicker; // PC comms RawSerial pc(USBTX, USBRX); // D0, D1 ? InterruptIn btn(USER_BUTTON); volatile uint8_t deviceCode; volatile uint8_t bitCount; volatile char debugBuf[DEBUG_SIZE]; volatile char debugLine[80]; volatile bool highNibbleIn = false; volatile bool highNibbleOut = false; volatile uint8_t dataInByte; volatile uint8_t dataOutByte; volatile uint16_t outDataPointer; #ifdef DEBUG void debug_log(const char *fmt, ...) { va_list va; va_start (va, fmt); sprintf((char*)debugLine,"%d ",mainTimer.read_us()); strcat((char*)debugBuf,(char*)debugLine); vsprintf ((char*)debugLine, fmt, va); strcat((char*)debugBuf,(char*)debugLine); va_end (va); } #else void debug_log(const char *fmt, ...) { return; } #endif 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" ); } // a "timeout" on ACK line void ackOff ( void ) { out_ACK = 0; infoLed = 0; } /* only if we replace testOutputSequence with an asynchronous approach ... void outNibbleSend ( void ) { uint8_t t; pc.putc('o'); debug_log ( "outNibbleSend (%d)\n", highNibbleOut); if ( out_ACK == 0 ) { wait_us(OUT_NIBBLE_DELAY); if ( (outDataPointer < outBufPosition) & highNibbleOut ) { if ( highNibbleOut ) { highNibbleOut = false; t = (dataOutByte & 0x0F); debug_log ( " %1X\n", dataOutByte, t); } else { highNibbleOut = true; dataOutByte = outDataBuf[ outDataPointer++ ]; t = (dataOutByte >> 4); debug_log ( "dataOut 0x%02X %1X\n", dataOutByte, t); } out_SEL_1 = (t&0x01); out_SEL_2 = ((t&0x02)>>1); out_D_OUT = ((t&0x04)>>2); out_D_IN = ((t&0x08)>>3); out_ACK = 1; } else { debug_log ( "dataOut complete\n"); } } else { pc.putc('x'); debug_log ( "outNibbleSend out_ACK!=0\n" ); } } void outNibbleAck ( void ) { debug_log ( "outNibbleAck\n"); if ( out_ACK == 1 ) { wait_us ( NIBBLE_ACK_DELAY ); out_ACK = 0; infoLed = 0; } else { pc.putc('x'); debug_log ( "outNibbleAck out_ACK!=1\n" ); } } */ void testOutputSequence ( void ) { uint8_t t; uint32_t nTimeout; pc.putc('o'); pc.printf (" %d, %d", outDataPointer, outBufPosition); while ( outDataPointer < outBufPosition ) { wait_us (OUT_NIBBLE_DELAY); if ( highNibbleOut ) { highNibbleOut = false; t = (dataOutByte >> 4); outDataPointer++; } else { highNibbleOut = true; dataOutByte = outDataBuf[outDataPointer]; debug_log ("%d: 0x%02X\n", outDataPointer, dataOutByte); t = (dataOutByte & 0x0F); } //debug_log ( " %u(%d):0x%1X\n", outDataPointer, (!highNibbleOut), t ); out_SEL_1 = (t&0x01); out_SEL_2 = ((t&0x02)>>1); out_D_OUT = ((t&0x04)>>2); out_D_IN = ((t&0x08)>>3); // nibble ready for PC to process out_ACK = 1; infoLed = 1; ackOffTimeout.attach( &ackOff, ACK_TIMEOUT ); // max time high ack // wait for BUSY to go DOWN nTimeout = 50000; // max wait while ( in_BUSY && (nTimeout--) ) { wait_us (10); } if (!nTimeout) { pc.putc('x'); debug_log ( "PC error 1\n" ); out_ACK = 0; infoLed = 0; break; }; // then wait for busy to go UP nTimeout = 50000; // max wait while ( !in_BUSY && (nTimeout--) ) { wait_us (10); } if (!nTimeout) { pc.putc('x'); debug_log ( "PC error 2\n" ); out_ACK = 0; infoLed = 0; break; }; // data successfully received by PC // acknowledge before next nibble out_ACK = 0; infoLed = 0; } debug_log ( "\nsend complete\n" ); out_D_OUT = 0; out_D_IN = 0; out_SEL_2 = 0; out_SEL_1 = 0; } void inDataReady ( void ) { pc.putc('c'); // Command processing starts here ... debug_log ( "\nProcessing...\n" ) ; if ( inBufPosition > 0 ) { debug_log ( "inBufPosition %d...\n", inBufPosition) ; // Verify checksum checksum=0; for (int i=0;i<inBufPosition-1;i++) { checksum = (inDataBuf[i]+checksum) & 0xff; } debug_log ( "checksum 0x%02X vs 0x%02X\n" , checksum, inDataBuf[inBufPosition-1]); if ( checksum == inDataBuf[inBufPosition-1]) { // processing command pc.printf(" 0x%02X\n\r", inDataBuf[0]); debug_log ( "command 0x%02X\n" , inDataBuf[0]); outDataTicker.detach(); // safety ? // decode and process commands ProcessCommand (); inBufPosition = 0; if ( outBufPosition > 0 ) { // data ready for sending debug_log ( "dataout %u [%02X] -%d\n" , outBufPosition, checksum, in_BUSY); outDataPointer = 0; highNibbleOut = false; // irq_BUSY.fall(NULL); irq_BUSY.rise(NULL); // This should be asynchronous? testOutputSequence(); } else { pc.putc('x'); debug_log ( "data prepare error\n"); } } else { pc.putc('x'); debug_log ( "checksum error\n"); } } } void inNibbleReady ( void ) { // test lines char inNibble = ( in_SEL_1 + (in_SEL_2<<1) + (in_D_OUT<<2) + (in_D_IN<<3) ); //debug_log ( "(%d) %01X \n", highNibbleIn, inNibble ) ; if ( out_ACK == 0 ) { wait_us ( NIBBLE_DELAY_1 ); out_ACK = 1; infoLed = 1; ackOffTimeout.attach( &ackOff, ACK_TIMEOUT ); // max time high ack if ( highNibbleIn ) { highNibbleIn = false; inDataBuf[inBufPosition] = (inNibble << 4) + inDataBuf[inBufPosition]; checksum = (inDataBuf[inBufPosition] + checksum) & 0xff; //debug_log(" %u:%02X [%02X]\n", inBufPosition, inDataBuf[inBufPosition], checksum ) ; inBufPosition++; // should be circular for safety; may cut off data! // Data processing starts after last byte (timeout reset at each byte received) inDataReadyTimeout.attach_us( &inDataReady, IN_DATAREADY_TIMEOUT ); } else { highNibbleIn = true; inDataBuf[inBufPosition] = inNibble; //debug_log ( " %01X\n", inDataBuf[inBufPosition] ) ; } } else { pc.putc('x'); debug_log ( "inNibbleReady out_ACK!=0\n" ) ; } } void inNibbleAck ( void ) { // test lines //debug_log ( "ack (%01X)\n\r", ( in_SEL_1 + (in_SEL_2<<1) + (in_D_OUT<<2) + (in_D_IN<<3) )) ; if ( out_ACK == 1 ) { wait_us ( NIBBLE_ACK_DELAY ); out_ACK = 0; infoLed = 0; } else { pc.putc('x'); debug_log ( "inNibbleAck out_ACK!=1\n" ); } } // Serial bit receive void bitReady ( void ) { uint32_t nTimeout; 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, " bit %d: %d\n\r", bitCount, bit ); out_ACK = 0; // 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("d 0x%02X\n",deviceCode); debug_log ( "Device ID 0x%02X\n", deviceCode ); if ( deviceCode == 0x41 ) { // Sharp-PC is looking for a CE140F (device code 0x41) - Here we are! sprintf ( (char*)debugLine, "%d CE140F\n\r", mainTimer.read_us()); strcat ((char*)debugBuf, (char*)debugLine) ; inBufPosition = 0; highNibbleIn = false; checksum = 0; // set data handshake triggers on the BUSY input irq_BUSY.fall(&inNibbleAck); irq_BUSY.rise(&inNibbleReady); // check for both BUSY and X_OUT to go down, before starting data receive nTimeout = 100000; // timeout: 1s while ( ( in_X_OUT || in_BUSY ) && (nTimeout--) ) { wait_us (10); } if (nTimeout) { // ... start data handling ... out_ACK = 1; infoLed = 1; wait_us ( DATA_WAIT ); out_ACK = 0; infoLed = 0; } else { pc.putc('x'); debug_log ("Timeout!\n\r") ; } } } 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) ; //?? or, wait AFTER eabling trigger ?? // serial bit trigger irq_BUSY.rise(&bitReady); irq_BUSY.fall(NULL); wait_us (ACK_DELAY) ; } } int main(void) { uint8_t i = 20; pc.baud ( 9600 ); 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(PullDown); in_D_OUT.mode(PullDown); in_X_OUT.mode(PullDown); in_D_IN.mode(PullDown); in_SEL_2.mode(PullDown); in_SEL_1.mode(PullDown); // default outputs out_ACK = 0; out_D_OUT = 0; out_D_IN = 0; out_SEL_2 = 0; out_SEL_1 = 0; // initial triggers (device sequence handshake) 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 } }