Developer notes

Sample driver

package be.lmenten.avr.core.driver.eeprom;

import be.lmenten.avr.core.Core;
import be.lmenten.avr.core.analysis.AccessEvent;
import be.lmenten.avr.core.analysis.AccessEventListener;
import be.lmenten.avr.core.data.CoreData;
import be.lmenten.avr.core.data.CoreRegister;
import be.lmenten.avr.core.driver.DriverBase;

public class EepromDriver
	extends DriverBase
	implements AccessEventListener
{
	private final CoreRegister EECR;
	private final CoreRegister EEARL;
	private final CoreRegister EEARH;
	private final CoreRegister EEDR;

	private final int interruptVector;

	// ------------------------------------------------------------------------

	private boolean writePending;
	private int writeMode;
	private double programmingTime;

	private long ticksCount;
	private long interruptsCount;

	// ========================================================================
	// = Constructor ==========================================================
	// ========================================================================

	public EepromDriver( Core core )
	{
		super( core );

		EECR = core.getRegister( "EECR" );
		EECR.addAccessListener( this );

		EEARL = core.getRegister( "EEARL" );
		EEARH = core.getRegister( "EEARH" );
		EEDR = core.getRegister( "EEDR" );

		interruptVector = core.getDescriptor().getInterruptVector( "EE_READY" );
	}

	// ========================================================================
	// = Driver interface =====================================================
	// ========================================================================

	/**
	 * Returns a unique ID for this driver instance.
	 */
	@Override
	public String getId()
	{
		return "Eeprom";
	}

	// ------------------------------------------------------------------------

	/**
	 * Every time the core is reset, this method is called. Default values
	 * for I/O register is normal set by the core itself but may be set
	 * here.
	 */
	@Override
	public void onReset()
	{
		writePending = false;
		writeMode = 0b00;
		programmingTime = 0.0d;

		ticksCount = 0l;
		interruptsCount = 0l;
	}

	/**
	 * After every instruction simulation, this method is called.
	 */
	@Override
	public void onTick( long ticksCount )
	{
		// --------------------------------------------------------------------
		// - Master write enable time out -------------------------------------
		// --------------------------------------------------------------------

		if( writePending )
		{
			if( ((ticksCount - this.ticksCount) > 4)
					|| (core.getInterruptsCount() != this.interruptsCount) )
			{
				EECR.bit( "EEMPE" );
				writePending = false;
			}

			return;
		}

		// --------------------------------------------------------------------
		// - Simulate eeprom programming time ---------------------------------
		// --------------------------------------------------------------------

		if( EECR.bit( "EEPE" )
				&& (ticksCount - this.ticksCount) >= core.tickToMillis( programmingTime ) )
		{
			EECR.bit( "EEPE", false );
			if( EECR.bit("EEIE") )
			{
				core.interrupt( interruptVector );
			}
		}
	}

	// ========================================================================
	// = AccessEventListener interface ========================================
	// ========================================================================

	/**
	 * EECR (control register) content has changed.
	 */
	@Override
	public void onAccessEvent( AccessEvent event )
	{
		// --------------------------------------------------------------------
		// - Eeprom read ------------------------------------------------------
		// --------------------------------------------------------------------

		if( EECR.bit( "EERE" ) )
		{
			int address = ((EEARH.getData() & 0xFF) << 8) | (EEARL.getData() & 0xFF);

			CoreData data = core.getEepromCell( address );
			EEDR.silentSetData( data.getData() );

			core.updateClockCyclesCounter( 4l );
		}

		// --------------------------------------------------------------------
		// - Eeprom write -----------------------------------------------------
		// --------------------------------------------------------------------

		else if( EECR.bit("EEMPE") && ! EECR.bit( "EEPE" )  )
		{
			writeMode = EECR.bits( "EEPM1", "EEPM0" );
			writePending = true;
		}

		else if( writePending && EECR.bit("EEPE") )
		{
			int address = ((EEARH.getData() & 0xFF) << 8) | (EEARL.getData() & 0xFF);
			CoreData data = core.getEepromCell( address );

			switch( writeMode )
			{
				// ------------------------------------------------------------

				case 0b00:		// Atomic erase and write
					programmingTime = 1.8d;

					data.silentSetData( 0 );

				case 0b10:		// Write only
					programmingTime += 1.8d;

					data.silentSetData( EEDR.getData() );

					writePending = false;
					core.updateClockCyclesCounter( 2l );
					break;

				// ------------------------------------------------------------

				case 0b01:		// Erase only
					programmingTime = 1.8d;

					data.silentSetData( 0 );

					writePending = false;
					core.updateClockCyclesCounter( 2l );
					break;

				// ------------------------------------------------------------

				case 0b11:
				default:
					throw new RuntimeException( "Invalid eeprom write mode" );
			}
		}
	}	
}