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.

Comments (0)

Files changed (2)

gb_emulator/include/gb_emulator/gb_comms_serial.h

 		// 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,

gb_emulator/src/gb_comms_serial.cpp

 		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
 			{