//
// Memory
//
// An Objective-C class for simulating main memory subsystems for computer simulations.
// The memory has n-bit words, and m-bit addresses.
//
// Can be used for main RAM memory, ROM, Cache memory, and microstore.
//


#import "Memory.h"
#import "compmath.h"
#import "utility.h"


@implementation Memory


- initType:(int)atype				// Type of memory.
	dataBits:(int)dbits			// Number of bits in a memory word.
	addressBits:(int)abits			// Number of bits in an address.
	physical:(int)nwords			// Amount of memory actually in subsystem.
	wait:(int)wcycles			// Number of cycles memory access takes.
	datain:din				// Node for data in.
	addrin:ain				// Node for address in.
	ready:rout				// Node for ready signal out.
	dataout:dout				// Node for data out.
	exception:eout				// Node for exception signal out.
	enable:e				// Node for enable.
	rw:rw					// Node for read/write select.
{
    switch(atype) {
        case MEM_RAM:
	case MEM_ROM:
	    type = atype;
	    break;
	default:
	    fprintf(stderr, "Memory: Attempt to create memory of invalid type %d!\n", atype);
	    exit(-1);
	    break;
    }
    
    if(dbits < 1) {
        fprintf(stderr, "Memory: Attempt to create Memory with less than one bit per word!\n");
	exit(-2);
    }
    
    databits = dbits;
    
    if(abits < 1) {
        fprintf(stderr, "Memory: Attempt to create Memory with less than one address bit!\n");
	exit(-3);
    }
    
    addressbits = abits;
    
    if(nwords < 1) {
        fprintf(stderr, "Memory: Attempt to create Memory with less than one word!\n");
	exit(-4);
    }
    
    numwords = nwords;
    
    if(wcycles < 0) {
        fprintf(stderr, "Memory: Attempt to create Memory with less than zero wait cycles!\n");
	exit(-5);
    }
    
    wait = wcycles;
    
    INITDEVTYPE("Memory");
    
    TESTNODE("DATAIN", din, databits);
    TESTNODE("ADDRIN", ain, addressbits);
    TESTNODE("READY", rout, 1);
    TESTNODE("DATAOUT", dout, databits);
    TESTNODE("EXC", eout, 1);
    TESTNODE("ENABLE", e, 1);
    TESTNODE("RW", rw, 1);
    
    DATAIN = din;
    ADDRIN = ain;
    READY = rout;
    DATAOUT = dout;
    EXC = eout;
    ENABLE = e;
    RW = rw;
    
    lastenable = 0;
    lastrw = 0;
    lastaddrin = 0;
    lastdatain = 0;
    
    cyclenum = 0;
    
    numbits = numwords * databits;
    
    data = (bit *)malloc(numbits * sizeof(bit));
    
    return self;
}


- free
{
    free(data);
    
    return [super free];
}


- cycle							// Execute one cycle
{
    bit		enablebit = *([ENABLE getBits]);
    bit		rwbit = *([RW getBits]);
    bit		*indata = [DATAIN getBits];
    bit		*inaddr = [ADDRIN getBits];
    long	addr = bits2long(inaddr, addressbits);
    long	curdata = bits2long(indata, databits);
    bit		one = 1;
    bit		zero = 0;
    bit		curexc = *([EXC getBits]);
    int		i;
    
    if(type == MEM_ROM) {
	[DATAOUT setBits:&(data[addr * databits])];
	return self;
    }
    
    if(enablebit)
        if(cyclenum < wait) {					// If not ready, simply block.
	    cyclenum++;
	    return self;
	}
	else {
	    cyclenum == 0;
	    [READY setBits:&one];
	    if(*[RW getBits]) {					//   If we are writing,
		for(i = 0; i < databits; i++)			//     copy input data to memory.
		    data[addr * databits + i] = indata[i];
	    }
	    else {						//   Else, we are reading, so
		[DATAOUT setBits:&(data[addr * databits])];	//     set DATAOUT to the word.
	    }
	}
    else {
        cyclenum = 0;
	return self;
    }
    
    return self;
}


- loadAt:(int)addr fromFile:(FILE *)aFile	// Load some data into the memory.
{
    int			curaddr;
    char		linebuffer[256];
    int			i;
    
    curaddr = addr;
    
    while(readline(aFile, linebuffer) == 0) {
        for(i = 0; i < databits; i++) {
	    if(linebuffer[databits - i - 1] == '0') {
	        data[curaddr * databits + i] = 0;
	    }
	    else {
	        data[curaddr * databits + i] = 1;
	    }
	}
	curaddr++;
    }
    
    return self;
}


- dumpWord:(int)addr toFile:(FILE *)aFile
{
    int i;
    int sum;
    
    for(i = (databits - 1); i >= 0; i--) {			// Dump binary form.
        fprintf(aFile, "%d", data[addr * databits + i]);
    }

    fprintf(aFile, "  :  0x");					// Dump hex form.
    sum = 0;
    for(i = (databits - 1); i >= 0; i--) {
        switch(i % 4) {
	    case 0:
	        sum += data[addr * databits + i];
		fprintf(aFile, "%X", sum);
		sum = 0;
		break;
	    case 1:
	        sum += 2 * data[addr * databits + i];
		break;
	    case 2:
	        sum += 4 * data[addr * databits + i];
		break;
	    case 3:
	        sum += 8 * data[addr * databits + i];
		break;
	}
    }
    
    return 0;
}


- dumpStart:(int)start				// Dump a portion of memory to a file.
	stop:(int)stop
	toFile:(FILE *)aFile
{
    int curaddr;
    
    if(start < 0) {
        fprintf(stderr, "Memory: Cannot dump memory below 0!\n");
	return nil;
    }
    
    if(stop < start) {
        fprintf(stderr, "Memory: Dump stop address must not be less than start address!\n");
	return nil;
    }
    
    if(aFile != stdout) {
        fprintf(stdout, "Dumping memory [%d..%d]...\n", start, stop);
    }
    
    fprintf(aFile, "\n\nMemory content :  \n\n");
    
    for(curaddr = start; curaddr <= stop; curaddr++) {	// For all words in range:
        fprintf(aFile, "%p: ", curaddr);		//   Dump the address.
	[self dumpWord:curaddr toFile:aFile];		//   Dump the word.
	fprintf(aFile, "\n");				//   Dump end of line.
    
    }
    
    return self;
}


@end


//
// End of file.
//