Skip to content
Nicholas Prado edited this page Jan 21, 2013 · 2 revisions

Up wiki heirarchy; Across to Cpu; Across to Driver

Ram is the store of active values in a computer, whether data or operations.

<>code file

class Ram( object ): constructor bound checks get & set error handling loader

The simpletron is described in the array chapter, so appropriately, Ram is simulated with a big array. Of integers, of course, as simpletron codes are integers. Deitel first specifies 100 locations and extends it to 1000.

This is simply done in the constructor. The ram size won't change, so it can be static. The maximum value specified is 9999. While we could use the magic number, a ratio of maxAddr is safer to update. <>code maxAddr = 100 maxVal = maxAddr * 100 - 1 minVal = -1 * maxVal <>

def __init__( self ):
	self.memory = [ 0 ] * Ram.maxAddr

With a limited address space, one needs a means of validating read & write indicies. Obviously, negative indicies aren't meaningful. (Python has sugar so that a negative index counts in reverse from the other end of a sequence. That's not an appropriate design to emulate at this level.) Similarly, values input must be checked against acceptable bounds to prevent overflows.

<>code def exceedAddrBound( self, anAddr ): return anAddr < 0 or Ram.maxAddr < anAddr

def exceedValue( self, value ) :
	return value < Ram.minVal or Ram.maxVal < value

The heart of memory accesses is reading and writing, here embodied in get/set(). The actual implementation defers validating the address to the client Cpu. Here, I'll let Ram validate addresses. Safest would be to have both check since a physical chip could be paired with unknown physical ram and ignore that contract. The set also checks the value used. Get can ignore it as set has already validated anything within ram.

<>code def setAt( self, addr, val ): if Ram.exceedAddrBound( self, addr ) : Ram.error( self, addr, ) elif Ram.exceedValue( self, val ) : Ram.error( self, val ) else : self.memory[ addr ] = val

def getFrom( self, addr ):
	if Ram.exceedAddrBound( self, addr ) :
		Ram.error( self, addr )
	else :
		return self.memory[ addr ]

So, anticipating an error means handling it. This involves emitting a report to the hard drive for later study. In this case, a text file will do. Again, this code will differ from v1.0 because I trusted Cpu to handle the checks. Where it does not, Ram needs error() to form its own writer.

<>code def error( self, verboten ) : dump = open( "dump.txt", 'w' ) dump.write( '\nRAM fault: ' + str( verboten ) + ' outside range\n' ) Ram.coreDump( self, dump ) dump.close()

The core dump involves writing ram's contents to the file in a grid. One could write one value for each line, but the dump would be exceedingly sparse. Outputting ten values to a line renders the whole visible on a single screen. Here, the magic numbers reflect an expected width in the form of the right justification with prepended zeros. One could make it more generic by discerning the digits in maxVal, but such is unlikely to change. Endl represents the row heading.

<>code def coreDump( self, dumpSite ): 'prints memory (only) to a file because of some error' columns = Ram.maxAddr / 10 endl = 0 # header for yy in range( 0, columns ) : # should reflect memory size dumpSite.write( '\t' + str( yy ) ) for nn in self.memory: # left margin if endl % columns == 0 : dumpSite.write( '\n' + str( endl ) + '\t' ) # values dumpSite.write( str( nn ).rjust( 4, '0' ) + '\t' ) # doesn't need to be \n\r endl += 1

The final aspect of ram involves loading in values. Here, I broke with Deitel's description slightly. The initial simpletron is fed values from the console. Honestly, I'd rather (hyperbole) than type in op values by hand. Plus, then there's no record outside my scratch paper. Obviously, the point is to have the budding programmer cut his'er teeth on the tedium of ISA level programming.

Given my disgust, I went straight to a loader. Initially, I put it in the computer running the cpu, but Ram is better suited to loading itself. Further, because I quickly forgot what an sml file represents, I used ## as a sentinel to stop the loading process. That way, I could leave comments below the code to explain what's what. Later, the Simple compiler's disassembler does exactly that to explain what the simpletron instructions mean.

Opening a file is a dicey proposition, so we wrap it in a try on the lookout for an IOError. That would signify the file wasn't found. We track the number of input values with memNext, which plays the index for setting that value. The value is tested against acceptable limits and set if alright. Otherwise, the loader informs the user and breaks loading. It doesn't exit though, because a program could still run if it even if truncated (though probably aberrantly).

<>code def loader( self, file ) : memNext = 0 tempVal = 0 try: hdSector = open( file, 'r' ) for line in hdSector: if not line.startswith( "##" ) : tempVal = int( line[ :-1 ] ) if Ram.exceedAddrBound( self, memNext ) : print "Too many values to load.", print " Loader halted at line" + str( memNext ) break elif Ram.exceedValue( self, tempVal ) : print "Value overflow at " + str( memNext ), print " with " + str( tempVal ) break else : Ram.setAt( self, memNext, tempVal ) memNext += 1 else: break hdSector.close( ) except IOError: print "File to load not found, perhaps it's not here with me?" exit( 1 )

Clone this wiki locally