1. spencercw
  2. gb_emulator

Commits

spencercw  committed 4078c4f

#33 Fix an issue with serial data being lost when an internal clock request is received at a bad time.

An internal clock request is now delayed when it is received when an external clock transfer is already in progress but data has already been received for it.

  • Participants
  • Parent commits 1d5527d
  • Branches serial

Comments (0)

Files changed (2)

File gb_emulator/include/gb_emulator/gb_comms_serial.h

View file
  • Ignore whitespace
 		// An external clock request has been made, the transfer duration has not yet elapsed and a
 		// response has been received
 		STATE_EXT_WAIT_DATA,
+		// An external clock request has been made, the transfer duration has not yet elapsed, a
+		// response has been received and a new internal clock request has been received which the
+		// program is not ready to handle
+		STATE_EXT_WAIT_DATA_WITH_DELAY,
 		// An external clock request has been made and the transfer duration has elapsed, but no
 		// response has been received yet. The emulator is still running
 		STATE_EXT_WAIT_TIMEOUT,

File gb_emulator/src/gb_comms_serial.cpp

View file
  • Ignore whitespace
 		gb_.mem_->ioPorts[IF] |= SERIAL_INTR;
 		break;
 
+	case STATE_EXT_WAIT_DATA_WITH_DELAY:
+		// Data arrived and another internal clock request was received
+		assert(intDelay_);
+		state_ = STATE_INT_DELAY;
+		cycles_ = SERIAL_DELAY_ALLOWANCE;
+		gb_.mem_->ioPorts[SB]  = data_;
+		gb_.mem_->ioPorts[SC] &= ~0x80;
+		gb_.mem_->ioPorts[IF] |= SERIAL_INTR;
+		break;
+
 	case STATE_INT_DELAY:
 		// An internal clock request was received but the program is still not ready to handle it;
 		// send back a negative response
 			gb_.mem_->ioPorts[ptr] = val & 0x81;
 		}
 
+		// Make a note if the transfer is being abandoned after data has already been sent because
+		// that is likely to cause problems
+		if (state_ == STATE_EXT_WAIT_DATA || state_ == STATE_INT_WAIT_DATA ||
+			state_ == STATE_EXT_WAIT_DATA_WITH_DELAY)
+		{
+			clog << pt::microsec_clock::local_time() << ": Abandoning transfer after data has "
+			        "already been sent\n";
+		}
+
 		// Start the transfer if necessary
 		if (val & 0x80)
 		{
 				if (gb_.mem_->ioPorts[SC] & 0x01)
 				{
 					// Internal clock
-					if (state_ == STATE_INT_DELAY)
+					if (state_ == STATE_INT_DELAY || state_ == STATE_EXT_WAIT_DATA_WITH_DELAY)
 					{
 						// Send a negative response to the internal clock request
 						clog << pt::microsec_clock::local_time() << ": An internal clock request "
 				else
 				{
 					// External clock
-					if (state_ == STATE_INT_DELAY)
+					if (state_ == STATE_INT_DELAY || state_ == STATE_EXT_WAIT_DATA_WITH_DELAY)
 					{
 						// Send a response to the delayed internal clock request
 						SerialDataMessage response;
 				cycles_ = SERIAL1_CYCLES;
 			}
 		}
-		else if (state_ != STATE_INT_DELAY)
+		else
 		{
 			// Cancel transfer
-			state_ = STATE_IDLE;
-			cycles_ = numeric_limits<int>::max();
+			if (state_ == STATE_EXT_WAIT_DATA_WITH_DELAY)
+			{
+				state_ = STATE_INT_DELAY;
+				cycles_ = SERIAL_DELAY_ALLOWANCE;
+			}
+			else if (state_ != STATE_INT_DELAY)
+			{
+				state_ = STATE_IDLE;
+				cycles_ = numeric_limits<int>::max();
+			}
 		}
 
 		break;
 		case STATE_INT_DELAY:
 			// Allow the program some time to make an external clock request before sending a
 			// negative response
+			if (state_ == STATE_INT_DELAY)
+			{
+				clog << pt::microsec_clock::local_time() << ": Another internal clock request was "
+				        "received before the previous one could be handled; the previous one will "
+						"be ignored\n";
+			}
+
 			state_ = STATE_INT_DELAY;
 			intDelay_ = *data;
 			cycles_ = SERIAL_DELAY_ALLOWANCE;
 			break;
 
+		case STATE_EXT_WAIT_DATA:
+		case STATE_EXT_WAIT_DATA_WITH_DELAY:
+			// Another internal clock request has been received before the previous one has finished
+			// being processed; allow some time for the program to make its next external clock
+			// request
+			if (state_ == STATE_EXT_WAIT_DATA_WITH_DELAY)
+			{
+				clog << pt::microsec_clock::local_time() << ": Another internal clock request was "
+				        "received before the previous one could be handled; the previous one will "
+						"be ignored\n";
+			}
+
+			state_ = STATE_EXT_WAIT_DATA_WITH_DELAY;
+			intDelay_ = *data;
+			break;
+
 		default:
 			// The program is not ready to receive data; send back a negative response
 			{