![](/media/cache/profiles/f93486bfff38ca69d76d85c089569a09.50x50_q85.jpg)
C1541-III mbed edition
IEC_bus.c
- Committer:
- gertk
- Date:
- 2011-08-22
- Revision:
- 0:28557a4d2215
- Child:
- 1:0cbbb66a6100
File content as of revision 0:28557a4d2215:
/*------------------------------------------------------------------------------------------*/ /*This is the lowlevel IEC-bus_driver, */ /* */ /*Note that this driver is dirty because it polls the drive for it's flags. The */ /*routines in this driver should therefore only be called from a low priority task */ /* */ /*Attention: routines tuned for 20MHz */ /*------------------------------------------------------------------------------------------*/ /* History: -------- 2007-04-28 KeepItSimpleStupid, so hanging on to this live style I removed the 'filter' from the IEC_receive routine (it was unneccessary nonsense anyway) 2007-04-25 CheckForCommand had an delay of 200uS to ensure the "frame to release of ATN'-time" of min 20uS, this is removed and now in the higher level IEC staemachine (main.c) 2007-04-23 IECundoturnaround() was checking for the clok, this is not reuiqed in this situation, that's why the timeout worked bettre then no timeout, simply because the CLOCK check is not required at THIS stage of the communication 2007-04-16 removed a semicolon in the acknowledge timeout check... there should not be a semicolon after the while :-) (oops) 2007-04-15 ADDED a ATN check on IEC_turnaround routine 2007-04-09 removed all code that was made commentary with // 2007-04-08 added the same 'filter' on FreeIEC and CheckForCommand... and then disabled the new code with "//" simply because the new improvements made it even worse... 2007-04-07 added some extra checks to make sure we trigger/test the clock/data signals correctly (software signal filtering). this is required because sometimes the device triggers incorrectly on signals meant for other devices on the bus which is best noticable during intense bus trafic. But it is very likely to happen at any moment (you can't predict glitches/spikes). 2007-04-01 the check IEC_CLOCK == 1 in UNDOTURNAROUND now has a time-out, simply to improve reliablillity... it helps a lot! 2006-11-01 the channel value was always extracted from the commandbyte, even if there was no channel value. This caused problems in the new statemachine (before the new statemachine the channel was used before the 3f/5f command so this problme was no problem then) 2006-10-31 added thefunction IEC_monitor this function can be used to monitor bus activity without interfering removed the setdeviceaddress function, since this is handled in MAIN.C 2006-10-30 the data and open command are identical when the errorchannel (15) is addressed 2006-10-22 changed the channel-variable to a variable outside the IEC_bus code, so it can be used in MAIN.c where it is declared 2006-10-20 added a function to set the timings of the IEC-bus (required for 25% faster VIC20-mode) 2006-07-03 changed 'FreeIEC' routine, this routine caused busylooping at an unwanted level, the busy-loop waiting for ATN going low is now located in main, so we can do other things while not communicating over the IEC-bus (manual file selection for instance) 2006-07-02 changed WaitForCommand into CheckForCommand, since the routine is no longer a while loop 2006-03-28 change in release and pulldown ATN-line routines, caused by new PCB design 2006-02-22 added frame_handshake signal for later use 2006-02-02 improvements in code layout... i.o.w. making it more readable 2005-10-18 start of IEC bus driver 2005-10-19 further details on EOI condition 2005-10-22 delay routines for IEC timing 2005-10-25 corrected the IEC-receive routine (without EOI detection) 2005-10-26 timeouts and EOI handling 2005-10-27 bus turnaround routines and testing of write routines. Optimisation of the send routines 2006-01-04 added IEC_send_string, is much more convinient then printf 2006-01-10 TALK and LISTEN definitions were mixed up, is corrected. FreeIEC-routine added and improved 2006-01-13 adjusted delay in waitforcommand routine, now external devices work 2006-01-16 rewritten the waitforcommand routine */ /* TO DO: ------ - EOI acknowledge delay, set/test this to correct timings for 20MHz */ /*------------------------------------------------------------------------------------------*/ /*--------------------------------------------------------*/ /* includes */ /*--------------------------------------------------------*/ #include <mbed.h> #include <stdio.h> #include <IEC_bus.h> #include <delay.h> #include <hardware.h> /*--------------------------------------------------------*/ /* constants */ /*--------------------------------------------------------*/ #define FALSE 0 /*FALSE*/ #define TRUE 1 /*TRUE*/ /*--------------------------------------------------------*/ /* globals */ /*--------------------------------------------------------*/ unsigned char IEC_receive_delay; /*set delay to ..uSec*/ unsigned char IEC_send_delay; /*set delay to ..uSec*/ /*external variables (variables also used in other .c files)*/ /*external variables (variables also used in other .c files)*/ extern bit EOI; /*End Of Indicator: this flag (when TRUE) indicates that the last byte is being transmitted or received*/ extern bit TimeOut; /*Timeout-flag*/ extern unsigned char channel; /*this register holds the channel value*/ /*--------------------------------------------------------*/ /* local functions */ /*--------------------------------------------------------*/ /*--------------------------------------------------------*/ /* external functions */ /*--------------------------------------------------------*/ //IEC_ATN : when IEC_ATN=0 it means that all devices must switch to listen (receive) mode immediatly /*set all IEC_signal lines in the correct mode*/ void InitIEC(void) { SetIEC_timings(TIMINGS_DEFAULT); /*set timings according the default settings (C64 compatible mode)*/ IEC_ATN_REL(); IEC_CLOCK_REL(); IEC_DATA_REL(); EOI = FALSE; TimeOut = FALSE; } void SetIEC_timings(unsigned char settings) { switch(settings) { case TIMINGS_VIC20: /*Timings for VIC-20 mode*/ { /*-----------------------*/ IEC_receive_delay = 52; /*set delay to ..uSec*/ IEC_send_delay = 60; /*set delay to ..uSec*/ break; } case TIMINGS_DTV: /*Timings for DTV (tuned IEC-bus speeds)*/ { /*--------------------------------------*/ IEC_receive_delay = 60; /*set delay to ..uSec*/ IEC_send_delay = 80; /*set delay to ..uSec*/ break; } default: /*Timings according (C64) IEC-specs*/ { /*---------------------------------*/ IEC_receive_delay = 70; /*set delay to ..uSec*/ IEC_send_delay = 80; /*set delay to ..uSec*/ break; } } } /*free the IEC bus for use by other devices, the bus is free'd until ATN is released*/ void FreeIEC(void) { IEC_ATN_REL(); IEC_CLOCK_REL(); IEC_DATA_REL(); while(IEC_ATN() == 0); } /*this routine will wait untill it receives one byte over the IEC-bus*/ unsigned char IEC_receive(unsigned char frame_handshake) { unsigned char data; unsigned char bitcount; unsigned long t; EOI = FALSE; /*clear the EOI flag*/ TimeOut = FALSE; /*clear the timeout flag*/ data = 0; /*clear destination register*/ bitcount = 8; /*the number of bits within a byte are still eight*/ /*-----Step1:Ready to send--------------------------------------------------------------------------------*/ while (IEC_CLOCK() == 0); /*wait (endlessly) until we see some action on the bus, we may respond whenever we please*/ /*Step2:Ready for data--------------------------------------------------------------------------------*/ IEC_DATA_REL(); /*response to talker READY FOR DATA, this signal has no time-limit since we may be busy formatting a disk or printing out a chunk of text to paper*/ /*-----intermission:EOI--------------------------------------------------------------------------------*/ t = 5000; /*timeout value for detecting an EOI condition*/ while (IEC_CLOCK() == 1) /*wait for the talker to indicate that the transmission will start (this signal must be received within 200uS, otherwise we may consider it as an EOI)*/ { t--; /*when timed out, we have detected an EOI condition*/ if (t == 0) /*timeout detected ?*/ { EOI = TRUE; /*set the EOI flag*/ IEC_DATA_PULL(); /*acknowledge the EOI condition*/ DelayBigUs(IEC_receive_delay); /*delay for .. uSec*/ IEC_DATA_REL(); /*acknowledge the EOI condition*/ } } /*-----Step3:receiving the bits--------------------------------------------------------------------------------*/ while (bitcount != 0) /*data is received with LSB first*/ { data>>=1; /*shift the byte one bit to the right*/ bitcount--; /*decrement bitcounter by one*/ t = 5000; /*timeout value*/ while (IEC_CLOCK() == 0); /*wait for the talker to release the clock line to signal "bit ready"*/ { t--; /*timeout detection is required to prevent "hanging" in case of data transmission erros*/ if (t == 0) /*timeout detected ?*/ { TimeOut = TRUE; /*set the timeout flag*/ return(0); /*exit*/ } } if (IEC_DATA() == 1) /*test if bit is set or cleared (attention:documentation states that signals are inverted ?!?! error ?!?!*/ data = data + 128; /*make bit 7 high*/ t = 5000; /*timeout value*/ while (IEC_CLOCK() == 1); /*wait for the talker to pulldown the clock line*/ { t--; /*timeout detection is required to prevent "hanging" in case of data transmission erros*/ if (t == 0) /*timeout detected ?*/ { TimeOut = TRUE; /*set the timeout flag*/ return(0); /*exit*/ } } } /*-----Step4:frame handshake--------------------------------------------------------------------------------*/ if (frame_handshake == FALSE) /*we must acknowledge within 1 mS that we have received the data, if not the talker knows there is something wrong*/ DelayBigUs(1250); /*delay for more then 1000 uSec*/ IEC_DATA_PULL(); return(data); /*exit with the received data*/ } /*this routine will send one byte over the IEC-bus*/ unsigned char IEC_send(unsigned char data) { unsigned char bitcount; unsigned long t; bitcount = 8; /*the number of bits within a byte are still eight*/ TimeOut = FALSE; /*clear the timeout flag*/ /*-----Step1:Ready to send--------------------------------------------------------------------------------*/ IEC_CLOCK_REL(); /*indicate to the listener that we are ready to send*/ /*-----Step2:Ready for data--------------------------------------------------------------------------------*/ while (IEC_DATA() == 0); /*wait for the listener (computer) to release the data-line (ready to receive data), this may take forever... (according the IEC-bus definition)*/ /*intermission:EOI--------------------------------------------------------------------------------*/ if (EOI == TRUE) { DelayUs(200); /*delay for more then 200 uSec*/ t = 5000; /*timeout value*/ while (IEC_DATA() == 1); /*check for acknowledge of the EOI. The acknowledge is simply pulling the data-line down for a brief period*/ { t--; /*timeout detection is required to prevent "hanging" in case of data transmission erros*/ if (t == 0) /*timeout detected ?*/ { TimeOut = TRUE; /*set the timeout flag*/ return(FALSE); /*exit*/ } } t = 5000; /*timeout value*/ while (IEC_DATA() == 0); /**/ { t--; /*timeout detection is required to prevent "hanging" in case of data transmission erros*/ if (t == 0) /*timeout detected ?*/ { TimeOut = TRUE; /*set the timeout flag*/ return(FALSE); /*exit*/ } } /*EOI is acknowledged by the listener (computer)*/ } IEC_CLOCK_PULL(); /*indicate to the listener (computer) that the transmission will start*/ DelayUs(IEC_send_delay); /*delay for .. uSec*/ /*-----Step3:receiving the bits--------------------------------------------------------------------------------*/ while (bitcount != 0) /*data is transmitted with LSB first*/ { IEC_CLOCK_PULL(); /*release the clockline in order to set the next bit on the data-line*/ DelayUs(IEC_send_delay); /*delay for .. uSec*/ bitcount--; if (data & 0x01) { IEC_DATA_REL(); } else { IEC_DATA_PULL(); } IEC_CLOCK_REL(); /*indicate to the listener (computer) "bit ready"*/ data>>=1; /*shift the byte one bit to the right*/ DelayUs(IEC_send_delay); /*delay for at least 60uSec when a C64 is listening*/ } IEC_CLOCK_PULL(); /*the whole byte has been transmitted*/ DelayUs(IEC_send_delay); /*delay for at least 60uSec when a C64 is listening*/ IEC_DATA_REL(); /*set clock and data in the correct order and wait for an acknowledge*/ DelayUs(25); /*wait a few uSec to prevent a false trigger*/ /*----Step4:frame handshake--------------------------------------------------------------------------------*/ t = 5000; /*timeout value*/ while (IEC_DATA() == 1) /*wait for the listener (computer) to acknowledge this byte it must respond within 1 mSec*/ { t--; /*timeout detection is required to prevent "hanging" in case of data transmission erros*/ if (t == 0) /*timeout detected ?*/ { TimeOut = TRUE; /*set the timeout flag*/ return(FALSE); /*exit*/ } } DelayUs(100); /*time between bytes is according IEC-specs at least 100 uSec*/ return(TRUE); /*the byte has been succesfully transmitted*/ } /*this routine will wait untill it receives one byte over the IEC-bus*/ /*this routine does NOT generate any signals, it only listens... */ unsigned char IEC_monitor(void) { unsigned char data; unsigned char bitcount; unsigned long t; EOI = FALSE; /*clear the EOI flag*/ TimeOut = FALSE; /*clear the timeout flag*/ data = 0; /*clear destination register*/ bitcount = 8; /*the number of bits within a byte are still eight*/ /*-----Step1:Ready to send--------------------------------------------------------------------------------*/ while (IEC_CLOCK() == 0); /*wait (endlessly) until we see some action on the bus, we may respond whenever we please*/ /*-----intermission:EOI--------------------------------------------------------------------------------*/ t = 5000; /*timeout value for detecting an EOI condition*/ while (IEC_CLOCK() == 1) /*wait for the talker to indicate that the transmission will start (this signal must be received within 200uS, otherwise we may consider it as an EOI)*/ { t--; /*when timed out, we have detected an EOI condition*/ if (t == 0) /*timeout detected ?*/ EOI = TRUE; /*set the EOI flag*/ } /*-----Step3:receiving the bits--------------------------------------------------------------------------------*/ while (bitcount != 0) /*data is received with LSB first*/ { data>>=1; /*shift the byte one bit to the right*/ bitcount--; /*decrement bitcounter by one*/ t = 5000; /*timeout value*/ while (IEC_CLOCK() == 0); /*wait for the talker to release the clock line to signal "bit ready"*/ { t--; /*timeout detection is required to prevent "hanging" in case of data transmission erros*/ if (t == 0) /*timeout detected ?*/ { TimeOut = TRUE; /*set the timeout flag*/ return(0); /*exit*/ } } if (IEC_DATA() == 1) /*test if bit is set or cleared (attention:documentation states that signals are inverted ?!?! error ?!?!*/ data = data + 128; /*make bit 7 high*/ t = 5000; /*timeout value*/ while (IEC_CLOCK() == 1); /*wait for the talker to pulldown the clock line*/ { t--; /*timeout detection is required to prevent "hanging" in case of data transmission erros*/ if (t == 0) /*timeout detected ?*/ { TimeOut = TRUE; /*set the timeout flag*/ return(0); /*exit*/ } } } return(data); /*exit with the received data*/ } /*this routine will handle strings, for sending strings over the IEC-bus*/ void IEC_send_string(const unsigned char *inputstring) { unsigned char lp; lp = 0; while(inputstring[lp] != 0) { IEC_send(inputstring[lp]); lp++; } } void IEC_send_number_as_ASCII(unsigned char number) { unsigned char i; i = 0x30 + (number/10); /*10's of the value*/ IEC_send(i); i = 0x30 + (number%10); /*1's of the value*/ IEC_send(i); } /*this routine will reverse the direction of the bus*/ void IEC_turnaround(void) { while (IEC_ATN() == 0); /*wait (endlessly) until the computer releases ATN*/ while (IEC_CLOCK() == 0); /*wait (endlessly) until the computer releases the clock line*/ IEC_DATA_REL(); DelayUs(25); /*small delay... to make the timing look like the timing of a real drive*/ IEC_CLOCK_PULL(); DelayUs(160); /*Tda = 80 uSec minimum acknowledge time for acknowledging the device is now a talker*/ } /*this routine will set the direction on the bus back to normal (the way it was when the computer was switched on)*/ void IEC_undoturnaround(void) { // unsigned int timeout_cnt; // DelayUs(50); /*Talk-Attention release (max. 100uS)*/ IEC_DATA_PULL(); IEC_CLOCK_REL(); DelayUs(25); /*small delay for .. uSec in order to prevent a false trigger*/ // timeout_cnt = 0; // while (IEC_CLOCK() == 1) /*wait until the computer pulls down the clock line or we timeout*/ // { // if (timeout_cnt == 1000) // break; /*timeout exceeded, leave routine*/ // else // timeout_cnt++; // } } /*this routine will let the bus lines go, as Jim Butterfield described, both talker and listener 'letgo'*/ void IEC_letgo(void) { DelayUs(50); /*after a suitable pause*/ IEC_DATA_REL(); /*dataline and clockline are released*/ IEC_CLOCK_REL(); } extern DigitalOut(myled3); unsigned char CheckForCommand(unsigned char *outputcommand, unsigned char frame_handshake) { if(IEC_ATN() == 1) /* wait untill there is relevant info (a command is indicated by the attention signal) on the bus */ { IEC_DATA_REL(); /*free the bus... (if not allready released)*/ return(FALSE); /*no command received, yet...*/ } IEC_DATA_PULL(); /*join the bus...*/ *outputcommand = IEC_receive(frame_handshake); /*save the received command to a register specified by the caller of this routine*/ if ((TimeOut == FALSE) && (IEC_ATN() == 0)) { if (*outputcommand == UNLISTEN) /*check for command:UNLISTEN, device:..., Attention-line must still be active*/ return(UNLISTEN); if (*outputcommand == UNTALK) /*check for command:UNTALK, device:..., Attention-line must still be active*/ return(UNTALK); if ((*outputcommand & 0xF0) == DATA) /*check for command:DATA, channel:..., Attention-line must still be active*/ { channel = *outputcommand & 0x0F; /*(if send) the channel is stored in the low-nibble of the command*/ return(DATA); } if ((*outputcommand & 0xF0) == CLOSE) /*check for command:CLOSE, channel:..., Attention-line must still be active*/ { channel = *outputcommand & 0x0F; /*(if send) the channel is stored in the low-nibble of the command*/ return(CLOSE); } if ((*outputcommand & 0xF0) == OPEN) /*check for command:OPEN, channel:..., Attention-line must still be active*/ { channel = *outputcommand & 0x0F; /*(if send) the channel is stored in the low-nibble of the command*/ return(OPEN); } return(*outputcommand); /*when none of the above... it's for another device, return the entire command for further analasys*/ } return(FALSE); /*code should never reach this point!!!*/ } /*--------------------------------------------------------*/ /* local functions */ /*--------------------------------------------------------*/ /* The person who writes their sourcecode without comments is either very smart or very stupid */