In the last article I managed to load the first sector of a disk. Now this first sector has to load a second sector. This excellent article describes how to do that (but intentionally does not provide a working example).
There are three relevant BIOS functions used here.
INT 0x10 with AH=0x0E displays a character.
INT 0x13 with AH=0x00 "resets" a disk (basically initialises it).
INT 0x13 with AH=0x02 reads a sector from the disk into the address specified by ES:BX.
The first is used in the writes function to write text to the screen. The second and third are now used to read data off the same floppy disk from which the boot sector was read.
Isn't it wonderful?
But let's have a look at some of the weirder parts of this program.
The resetdisk function is rather straightforward. It calls the reset function of the BIOS for drive 0.
But the readnextsector function is a bit tricky. At least it took me a while to figure it out.
The 512 bytes read from the first sector are located at (20 bit) address 0x7C00 because the firmware put it there. Hence the continuation of the data on disk starts at address 0x7E00 (which is roughly the result of adding 512 to 0x7C00, try it out). The function readnextsector attempts to load the next 512 bytes off the disk which necessarily includes the first 30 bytes of that sector which is the string located at s_nextsector in the program.
To make this completely clear, everything after s_nextsector in the program is located in the second sector of the disk because it comes after the first 512 bytes of the program.
tl;dr two 512B sectors on one disk
To generate a 20 bit address (which the Intel 8086 chip uses to address memory) from 16 bit numbers (the width of the registers) the CPU uses segment registers, shifts the address in them 4 bits to the left (which is the same as multiplying by 16) and then adds a 16 bit offset to that address as needed. It basically creates areas of memory, 64 KB large, to jump around in using 16 bit pointers.
Reading a sector off a disk needs a buffer in memory specified by the address specified by the extra segment register ES for the area and an offset in BX. To generate the address 0x7E00 the segment address must be 0x7E0 which is then shifted to the left to generate 0x7E00 (and an offset of 0 is added from BX).
does exactly that. (XOR results in zero whenever both inputs are the same. So XORing a number with itself results in zero. I think. At least I wouldn't rule out the possibility.)
Then comes preparing the BIOS for reading the sector.
Floppy disks apparently have sides, and sides have tracks, and tracks have sectors. A floppy disk can have two sides, side 0 and side 1. Each side has some number of tracks, starting from track 0. Each track has a number of sectors, starting from sector 1 (for some reason). Finally, each computer has some number of floppy disk drives, starting from drive 0.
MOV AH,2 prepares the BIOS call for reading a sector
MOV AL,1 tells it to read only 1 sector
MOV CH,0 tells it to read from track 0, the first track (zero-based)
MOV CL,2 tells it to read from sector 2, the second sector (one-based)
MOV DH,0 tells it to read from side 0, the first (or only) side
MOV DL,0 tells it to read from drive 0, the first drive (zero-based)
INT 0x13 calls the BIOS to execute all that
After the program has done this, it displays the string from the beginning of that newly-loaded sector in the same way it displayed the previous hard-coded string.
It looks like so:
To my horror, I have no idea why the cursor moved a step forward after printing the two strings (which are followed by carriage returns and linefeeds).
And this is what the memory looks like between the two hard-coded strings.
I have no idea why a 01 follows the linefeed after the "Welcome to the next sector." and carriage return. Maybe this is what causes the cursor to move one step forward in the next line. I don't know where the "j" comes from either, but I am not nearly as worried about it as I am about the 01.