// This file implements basic I/O functions using Memory Mapped I/O. // In particular: printString, readString, printInt, readInt, // readBlock, writeBlock int blockDev = 0x40000010; int charDev = 0x40000000; // Wait until the block device is ready to accept a command // (not busy and no exception signalled) _waitForBlockIdle(int* p) { int* pCharDev = charDev; // this is just a sanity check, not strictly part of what needs to happen in this routine if ( (p[0] & 0x2000) != 0 ) { // the exception/completion from the last operation has not been cleared -- that's almost certainly a mistake _printString(pCharDev, "Error: call to _waitForBlockIdle but device is showing an uncleared exception\n"); p[0] = 0x2000; // clear it... } // here's what this routine really does while ((p[0] & 0xe000) != 0) {} } // Wait until the block device has signalled done or exception // Returns the exception/completion code; _waitForBlockDone(int* p) { int* pCharDev = charDev; int exceptionCode; while ((p[0] & 0x2000) == 0) {} // wait until the device is done exceptionCode = (p[0] & 0xff); if ( exceptionCode != 0 ) { _printString(pCharDev, "Block device exception: "); _printInt(pCharDev, exceptionCode); _printString(pCharDev, "\n"); } p[0] = 0x2000; // make device ready again } // Wait until the device has an input character _waitForCharInput(int* p) { while ((p[0] & 0x4000) == 0) {} } // Wait until the device is ready for output _waitForCharOutput(int* p) { while ((p[0] & 0x8000) == 0) {} } // The actual read string function _readString(int* dev, char* buf, int len) { int i, ch; i=0; len = len - 1; while (i < len) { _waitForCharInput(dev); ch = dev[0] & 0xff; // fetch the character dev[0] = 0x4000; // clear the input char ready flag if (ch == 13) { buf[i] = 0; return 1; } else { if (ch != 10) { buf[i] = ch; } else { buf[i] = 0; return 1; } } i = i + 1; } return 0; } // Do a read string into this buffer readString(char* buf, int len) { _readString(charDev, buf, len); } int exit() {} // Return an integer int readInt() { int x = _readInt(charDev); return x; } // The actual readInt function _readInt(int* dev) { char buffer[80]; int negative = 0; int i = 0; int result = 0; _readString(dev, buffer, 80); _printString(dev, "\nRead buffer: " ); _printString(dev, buffer); _printString(dev, "\nConverting: " ); if (buffer[0] == 45) { negative = 1; i = 1; } while (buffer[i] != 0) { result = (result * 10) + (buffer[i] - 48); i = i + 1; } _printInt(dev, result); if (negative) { return -result; } else { return result; } } _strlen(char* s) { int i=0; while (s[i] != 0) { i = i + 1; } return i; } printString(char* buf) { _printString(charDev, buf); } // my very own print string, using memory mapped IO _printString(int* dev, char* buf) { int i; i=0; while (buf[i] != 0) { _waitForCharOutput(dev); dev[0] = 0x00008000 + buf[i]; i = i + 1; } } printInt(int val) { _printInt(charDev, val); } _printInt(char* dev, int val) { char buffer1[20]; char buffer2[20]; int i, j; j=0; if (val < 0) { val = -val; buffer2[0] = 45; // - sign j = 1; } buffer1[0] = (val % 10) + 48; i = 1; val = val / 10; while (val != 0) { buffer1[i] = (val % 10) + 48; val = val / 10; i = i + 1; } i=i-1; while (i >= 0) { buffer2[j] = buffer1[i]; j = j + 1; i = i - 1; } buffer2[j] = 0; _printString(dev, buffer2); } _readWriteBlock(int* blockDevice, int blockCommand, char* array, int block, int cnt) { int pCharDev = charDev; _waitForBlockIdle(blockDevice); blockDevice[1] = array; // mem address blockDevice[2] = block; // disk address blockDevice[3] = cnt; // #blocks blockDevice[0] = blockCommand; // start the operation _waitForBlockDone(blockDevice); } readBlock(char* array, int block, int cnt) { int* pBlockDevice = blockDev; return _readWriteBlock( pBlockDevice, 0x8000, array, block, cnt); } writeBlock(char* array, int block, int cnt) { int* pBlockDevice = blockDev; return _readWriteBlock(pBlockDevice, 0x4000, array, block, cnt); }