An English professor wants you to write a program that counts the number of words in a text file.
7200rpm, 120Hz, 1sec/120Hz / 2 semi-circle = 4.167 ms.average latency + 8ms average seek time.
Suggestion: put program and data in one track (if it fits)
Aside: Note that this limit of 32 bits means that we could only address 232 sectors * 29(512 bytes/sector) = 2TB of disk space. This is the reason that the industry is currently (April 2010) working on increasing the number of bytes each sector could store.
In the disk controller, there are several "disk controller registers" that allows communication between the CPU and the disk drive. These registers are addressable by the CPU. The register mapped to 0x1F7 is known as the status register. When the register reads 01, representing the ready state, the CPU could communicate with the disk drive. The CPU requests for data by storing the number of sectors it wants to read into disk controller register 0x1F2. A sector is simply a unit of disk space (512 bytes). Each sector on the disk drive is indexed in a similar fashion as arrays. The CPU then stores a sector offset into registers 0x1F3 through 0x1F6. A sector offset is simply the index number of the first sector it wants to be read. Note that each register could only store 8 bytes. Thus, since we can only store the offset into 4 registers, the longest offset could only be 32 bytes long.
Once the number of bits and the offset is stored, the CPU then stores the READ command into register 0x1F7 and wait for the disk to go in the READY state. Then, the CPU will obtain results of the read as data out of register 0x1F0 and store them into RAM. At this point, you might wander what would happen if the required data would not fit in a single register at once. (8 bytes). This actually does not present a problem since the data is obtained in a streaming fashion that allows for continuous data flow from the disk to the CPU and then to RAM.
It occurs when the computer is powered on, and the Program Counter (PC) points to an EEPROM (Electrically Erasable Programmable Read-Only Memory) chip which contains a small program called a BIOS (Basic Input Output System). This BIOS program can be changed by flashing the EEPROM with the new BIOS, but the BIOS will typically come installed on a machine at purchase.
We are assuming here that there are only 19 sectors worth of code.
// Starting from 1 because sector 0 is the VBR for (i = 1, i < 20; i++) { read_ide_sector (i, (char *) 0x100000 + (i - 1)*512); // we have to cast the address as a char * or we would have a type mismatch. } goto (char *)0x100000; // jump to our program's address
// s is the sector number, a is the address to read to void read_ide_sector (int s, char *buf) { // this compares the 7th and 8th bits in 0x1F7 (11000000) to 0x40 // (01000000) // this is the "check for ready" loop while ((inb(0x1F7) & 0xC0) != 0x40) continue; // store value of '1' into the number of sectors register outb (0x1F2,1); // set the sector offset registers outb (0x1F3, s & 0xFF); outb (0x1F4, (s >> 8) & 0xFF); outb (0x1F5, (s >> 16) & 0xFF); outb (0x1F6, (s >> 24) & 0xFF); // Set ready register outb (0x1F7, 0x20); // again wait until device is ready (void wait_for_ready (void) // 0x1F0 is the device register location while ((inb(0x1F7) & 0xC0) != 0x40) continue; // and 128 is the amount of words (which = 4bytes) we will be inserting (1 sector = 512 bytes = 4 bytes * 128 words). //insl: write buffer gets written 128 times from the register via CPU. insl (0x1F0, buf, 128); }
void main(void) { int words = 0; // has not read anything yet bool inword = false; // if the cursor is pointing at an alphabet, the previous character was an alphabet. // We are arbitrarily choosing this sector for our reading location (sector/byte) int s = 1000000; for ( ; ; s++) // increment the sector for read_sector { // create a buffer to read in to char buf[512]; // read the ide sector to our buffer read_ide_sector (s, buf); for (int j = 0; j < 512; j++) { // if we are at the end of file quit if (buf[j] == '\0') goto eof; // check if we are currently in a word bool this_alpha = isalpha((unsigned char) buf[j]) != 0; // /this will increment the number of words at the beginning of each word words += ~inword & this_alpha; //both bool. // update inword inword = this_alpha; } } EOF: words += inword; display (words); } // Another possibility: we could take out the repeating routine as a function void wait_for_ready(void) { while (inb(0x1F7) & 0xC0) != 0x40) // busy wait continue; }
Each pixel on the screen consists of two bytes. The first byte is an ASCII text character. The second is the pixel color.
Display:
The first pixel's address is 0xB8000.
Each pixel has 2 bytes, 16 bits. There are 25*80*2 = 4000 bytes total. Low order byte stores the character to display, and the high order byte stores the color.
void display (int i) { //starting position on screen. Write from right to left. unsigned char *screen = (unsigned char*) 0xB8000 + (25*80*2/2); //right center of the screen. 2 bytes, 2 to be in the middle. do { screen[0] = (i%10) + '0'; // ASCII, alphabet // gray text on black screen[1] = 7; // writing from back to front screen -= 2; i /= 10; } while (i != 0); }
Read_sector for MBR, VBR, and application. Instead of multiple functions, we can have it on BIOS. This was the original intention for BIOS, but this approach has been discarded for the sake of performance.
The slowest part of the program is the wait_for_ready and we will improve it next time.
Function that needs to be improved:
void read_ide_sector (int s, char *addr);
What if we want to read from other devices other than a hard drive?
void read_sector (int s, char *addr) { switch (device) { case IDE: // current code case CDROM: // other code } }
We can also do this:
// d represents multiple devices void read_sector (int d, int s, char *addr);
If we want to change the number of bytes read or where to start reading? The function is changed from read_sector to read because the amount we read is no longer specific to sectors.
// o is the byte offset into device. len is the # of bytes to read void read (int d, int o, char *addr, int len);
If there was a hardware failure in reading the text? Change function to int to return if read was successful.
int read (int d, int o, char *addr, int len);
int is only 32-bit which means we can only read roughly 4GB on the device. Our hard drive is 1TB. How do we fix this?
// off_t and size_t types are both 64-bit int read (int d, off_t o, char *addr, size_t len);