The boot sector only loads 3 sectors. This was enough to get quite a bit done. However, we still need to write more code and we are out of space. So now we will write a loader.
Create a file called loader.asm. We will then copy some boot sector code to the loader. First copy the header and modify as follows:
org 00000h jmp short ldrstart ; Jump to boot sector start nop ; place holder oemname: db " " ; Operating system name bytespersec: dw 0 ; Bytes per sector secperclust: db 0 ; Sectors per cluster ressectors: dw 0 ; Reserved Sectors fats: db 0 ; Number of FATs on disk rootdirents: dw 0 ; Number of directory entries sectors: dw 0 ; Sectors per disk media: db 0 ; Always F0 hex for floppy fatsecs: dw 0 ; Sectors per FAT secpertrack: dw 0 ; Sectors per track heads: dw 0 ; Number of heads hiddensecs: dd 0 ; Hidden sectors hugesectors: dd 0 ; Huge sectors drivenumber: db 0 ; Drive number 000h for floppy currenthead: db 0 ; Reserved byte we are using for disk read bootsignature: db 0 ; Boot signature volumeid: dd 0 ; Serial Number vlumelabel: db " " ; Disk label filesystype: db " " ; File system track: dw 0 ; Track for disk read sector: db 0 ; Sector for disk read root: dw 0 ; lba for root rootmod: dw 0 ; lba modulous for root data: dw 0 ; lba for data datamod: dw 0 ; lba modulous for data ldrstart:
Then copy over the strout, calcchs, and readsec subroutines from the bios. We already have a strout in our os so rename the strout routine print and modify the jumps and other labels.
;------------------------------------------------------- ; String Print ; Input: ; DS:SI = Message offset ; Output: ; AX, BX, SI are destroyed ;------------------------------------------------------- print: lodsb ; Get character from string or al, al ; Check if our character is zero jz print done ; if it is zero we have finished mov ah, 0Eh ; BIOS print call mov bx, 00007h ; Character attribute int 010h ; Call the video BIOS jmp print ; Get next character printdone: ret ; Return from subroutine ;------------------------------------------------------- ; Calculate CHS ; Input: ; AX:DX = Logical Sector ; Output: ; sector ; head ; track/cylinder ;------------------------------------------------------- ; sector = (lba % sectors per track) + 1 ; head = lba / sectors per track % number of heads ; track = lba / sectors per track / number of heads calcchs: cmp dx, [secpertrack] ; Check within track jnc calcchsfail ; if not fail div word [secpertrack] ; LBA / secpertrack inc dl ; LBA % secpertrack + 1 mov [sector], dl ; Save in sector xor dx, dx ; Clear the modulous div word [heads] ; lba / secpertrack / heads mov [currenthead], dl ; lba / secpertrack % heads mov [track], ax ; lba / secpertrack / heads clc ; Clear carry to report success ret ; Return from subroutine calcchsfail: stc ; Set carry to report failure ret ; Return from subroutine ;------------------------------------------------------- ; Read Sector ; Input: ; bsTrack = Track ; bsSector = Sector ; bsHead = Head ; ES:BX = Location to load sector at ; Output: ; AX, BX, CX, DX are destroyed ;------------------------------------------------------- readsec: mov dx, [track] ; Get the track mov cl, 006h ; Rotate the bits 8 and 9 into position shl dh, cl or dh, [sector] ; Get the sector with out changing high bits mov cx, dx ; Place in the correct register xchg ch, cl ; Swap bytes to complete the process mov dl, [drivenumber] ; Drive number mov dh, [currenthead] ; Current Head mov ax, 00201h ; BIOS Disk Read 1 Sector int 013h ret ; Return from subroutine
We will also copy the loaderror code from the boot sector and rename it.
ldrfatal: mov si, ldrfatalmsg ; Get offset to fetal message call print ; Print fatal message xor ax, ax ; Wait for key press int 016h int 019h ; Bootstrap (Reboot)
Let's not forget the message for it to print
ldrfatalmsg: db 00Dh, 00Ah, "Fatal Error: Loader...", 00Dh, 00Ah, 0
Next we will add the code to read a fat12 byte and load a sector into the scratch area. This is because fat 12 data can cross a sector boundry.
;------------------------------------------------------- ; Get byte from fat (FAT12) ; Input: ; AX = Byte to read ; Output: ; AL = Byte read ;------------------------------------------------------- ldrfatread12: push bx ; Save registers push cx push es push ax ; Set ES to 0000h xor ax, ax mov es, ax pop ax mov bx, 00500h ; Read into scratch area xor dx, dx ; Clear for divide div word [bytespersec] ; Sector of file = FAT index / bytes per sector inc ax ; FAT starts at sector 2 advance ax cmp [ldrsectorload], ax ; Check if this sector is already in scratch jz ldrfatread12noload ; If it is don't load it again mov [ldrsectorload], ax ; Set current loaded FAT sector push dx ; Save FAT index MOD bytes per sector xor dx, dx ; Clear for divide in calcchs call calcchs ; Calculate Cylinder Head Sector jc ldrfatread12error ; If error exit routine call readsec ; Read sector jc ldrfatread12error ; If error exit routine pop dx ; Restore FAT index MOD bytes per sector ldrfatread12noload: add bx, dx ; Byte to read = file index % bytes per sector + scratch mov al, [es:bx] ; Get byte pop es ; Restore registers pop cx pop bx clc ; Carry clear on success ret ; Return from subroutine ldrfatread12error: pop dx ; Restore FAT index MOD bytes per sector pop es ; Restore registers pop cx pop bx stc ; Set carry on fail ret ; Return from subroutine
We also need some place to keep track of which sector is loaded. To the header add the following
ldrsectorload: dw 0
Then we will need the code to get the next cluster from the fat
;------------------------------------------------------- ; Get Next Cluster (FAT12) ; Input: ; AX = Current Cluster ; Output: ; AX = Next Cluster ;------------------------------------------------------- ldrnextcl12: push cx ; Save register push ax ; Save a copy of input cluster mov cx, ax ; FAT index = n*3/2 shl ax, 1 ; Multiply by 2 add ax, cx ; Add 1 shr ax, 1 ; Divide by 2 push ax ; Save input byte to read call ldrfatread12 ; Read first byte of fat12 jc ldrnextcl12errorpop ; error exit routine mov cl, al ; Save byte read pop ax ; Restore input byte to read inc ax ; Get next byte call ldrfatread12 ; Read second byte of fat12 jc ldrnextcl12error ; error exit routine xchg ah, al ; Make second byte high mov al, cl ; Make first byte low pop cx ; Restore the saved input cluster test cx, 0x0001 ; Check if it was odd or even jz ldrnextcl12done ; If even do nothing mov cl, 4 ; If odd we need to shift by 4 shr ax, cl ldrnextcl12done: and ax, 0x0FFF ; Clear top 4 bits we only need 12 bits pop cx ; Restore register clc ; Clear carry for success ret ; Return from subroutine ldrnextcl12errorpop: pop ax ; We had an extra push clear it ldrnextcl12error: pop ax ; Restore input cluster pop cx ; Restore register stc ; Set carry for fail ret ; Return from subroutine
Now we need a way to read an entire cluster.
;------------------------------------------------------- ; Read Data Cluster ; Input: ; AX = Cluster start ; ES:BX = Buffer ; Output: ; Loaded buffer ;------------------------------------------------------- ldrreaddatacluster: xor ch, ch ; Clear ch mov cl, [secperclust] ; CX = Sectors per cluster ldrreadclusternext: push ax ; Save cluster start mul byte [secperclust] add ax, [data] ; Add the data start cluster dec ax ; Subtract 2 because FAT starts at 2 dec ax push cx ; Save count read will destroy it xor dx, dx ; Clear for calcchs call calcchs ; Calculate Cylinder Head Sector jc ldrreaddataclusterfail ; If error exit routine call readsec ; Read sector into buffer jc ldrreaddataclusterfail ; If error exit routine pop cx ; Restore counter pop ax ; Restore cluster start add bx, word [bytespersec] ; Add bytes per sec for next read inc ax ; Increment sector loop ldrreadclusternext ; Get next sector of cluster clc ; Clear carry for success ret ; Return from subroutine ldrreaddataclusterfail: pop cx ; Restore registers pop ax stc ; Set carry for fail ret ; Return from subroutine
Next we need a chain loader this is going to walk through the entire fat chain and load every cluster in the chain.
;------------------------------------------------------- ; FAT Chain Loader ; Input: ; AX = First Cluster ; ES:BX = Buffer ; Output: ; Loaded buffer ;------------------------------------------------------- ldrchainload: mov [ldrsectorload], word 0 ldrchainloadnext: cmp ax, 00FFFh ; Last cluster found jz ldrchainloaddone ; We are done push ax ; Save cluster call ldrreaddatacluster ; Read cluster from data area jc ldrchainloadfailpop pop ax ; Restore cluster call ldrnextcl12 ; Get next cluster jc ldrchainloadfail jmp ldrchainloadnext ; Loop until last cluster ldrchainloaddone: clc ; Carry clear for success ret ; Return from subroutine ldrchainloadfailpop: pop ax ldrchainloadfail: stc ret
Let's do some basic setup of registers.
cli ; Turn off interrupts cld ; Make sure we are going forward push cs ; Set ES to current segment pop es mov ss, ax ; Make sure stack segment is zero mov sp, 07C00h ; Place stack ptr before boot sector sti ; Turn on interrupts
We also need a bunch of data from the boot sector we will add code to copy the header
mov di, oemname ; Copy disk header to oemname mov si, 07C03h ; From oemname of disk header mov cx, ldrsectorload-oemname ; Header size rep movsb ; Copy all bytes
Now let's grab the first cluster to load from the root directory left by the boot sector.
mov si, 0051Ah ; BIOS left root directory at 500h mov ax, [si] ; Read cluster of first file
We are done using 0 as our data segment let's update ds.
push cs ; Set DS to current segment pop ds
We have already loaded 3 sectors. We will have to fix this for clusters in the future. but for now we will skip 3 clusters.
mov cx, 3 ; Read first 3 clusters from FAT ldrnextskipcluster: call ldrnextcl12 ; Load next cluster from fat jc ldrfatal ; If error goto fatal cmp ax, 00FFFh ; Last cluster found jz start ; We are loaded goto demoos loop ldrnextskipcluster ; Check next cluster
Now let's load the rest of the fat chain and jump to demoos start.
mov bx, 00600h ; Start loading at 70:600h call ldrchainload jc ldrfatal jmp start
;======================================================= ; Loader - loader.asm ; ----------------------------------------------------- ; Written in NASM ; Written by Marcus Kelly ;======================================================= org 00000h ; Assemble code for this offset jmp short ldrstart oemname: db " " ; Operating system name bytespersec: dw 0 ; Bytes per sector secperclust: db 0 ; Sectors per cluster ressectors: dw 0 ; Reserved Sectors fats: db 0 ; Number of FATs on disk rootdirents: dw 0 ; Number of directory entries sectors: dw 0 ; Sectors per disk media: db 0 ; Always F0 hex for floppy fatsecs: dw 0 ; Sectors per FAT secpertrack: dw 0 ; Sectors per track heads: dw 0 ; Number of heads hiddensecs: dd 0 ; Hidden sectors hugesectors: dd 0 ; Huge sectors drivenumber: db 0 ; Drive number 000h for floppy currenthead: db 0 ; Reserved byte we are using for disk read bootsignature: db 0 ; Boot signature volumeid: dd 0 ; Serial Number vlumelabel: db " " ; Disk label filesystype: db " " ; File system track: dw 0 ; Track for disk read sector: db 0 ; Sector for disk read root: dw 0 ; lba for root rootmod: dw 0 ; lba modulous for root data: dw 0 ; lba for data datamod: dw 0 ; lba modulous for data ldrsectorload: dw 0 ldrfatalmsg: db 00Dh, 00Ah, "Fatal Error: Loader...", 00Dh, 00Ah, 0 ldrstart: cli ; Turn off interrupts cld ; Make sure we are going forward push cs ; Set ES to current segment pop es mov ss, ax ; Make sure stack segment is zero mov sp, 07C00h ; Place stack ptr before boot sector sti ; Turn on interrupts mov di, oemname ; Copy disk header to oemname mov si, 07C03h ; From oemname of disk header mov cx, ldrsectorload-oemname ; Header size rep movsb ; Copy all bytes mov si, 0051Ah ; BIOS left root directory at 500h mov ax, [si] ; Read cluster of first file push cs ; Set DS to current segment pop ds mov cx, 3 ; Read first 3 clusters from FAT ldrnextskipcluster: call ldrnextcl12 ; Load next cluster from fat jc ldrfatal ; If error goto fatal cmp ax, 00FFFh ; Last cluster found jz start ; We are loaded goto demoos loop ldrnextskipcluster ; Check next cluster mov bx, 00600h ; Start loading at 70:600h call ldrchainload jc ldrfatal jmp start ldrfatal: mov si, ldrfatalmsg ; Get offset to fetal message call print ; Print fatal message xor ax, ax ; Wait for key press int 016h int 019h ; Bootstrap (Reboot) ;------------------------------------------------------- ; FAT Chain Loader ; Input: ; AX = First Cluster ; ES:BX = Buffer ; Output: ; Loaded buffer ;------------------------------------------------------- ldrchainload: mov [ldrsectorload], word 0 ldrchainloadnext: cmp ax, 00FFFh ; Last cluster found jz ldrchainloaddone ; We are done push ax ; Save cluster call ldrreaddatacluster ; Read cluster from data area jc ldrchainloadfailpop pop ax ; Restore cluster call ldrnextcl12 ; Get next cluster jc ldrchainloadfail jmp ldrchainloadnext ; Loop until last cluster ldrchainloaddone: clc ; Carry clear for success ret ; Return from subroutine ldrchainloadfailpop: pop ax ldrchainloadfail: stc ret ;------------------------------------------------------- ; Get Next Cluster (FAT12) ; Input: ; AX = Current Cluster ; Output: ; AX = Next Cluster ;------------------------------------------------------- ldrnextcl12: push cx ; Save register push ax ; Save a copy of input cluster mov cx, ax ; FAT index = n*3/2 shl ax, 1 ; Multiply by 2 add ax, cx ; Add 1 shr ax, 1 ; Divide by 2 push ax ; Save input byte to read call ldrfatread12 ; Read first byte of fat12 jc ldrnextcl12errorpop ; error exit routine mov cl, al ; Save byte read pop ax ; Restore input byte to read inc ax ; Get next byte call ldrfatread12 ; Read second byte of fat12 jc ldrnextcl12error ; error exit routine xchg ah, al ; Make second byte high mov al, cl ; Make first byte low pop cx ; Restore the saved input cluster test cx, 0x0001 ; Check if it was odd or even jz ldrnextcl12done ; If even do nothing mov cl, 4 ; If odd we need to shift by 4 shr ax, cl ldrnextcl12done: and ax, 0x0FFF ; Clear top 4 bits we only need 12 bits pop cx ; Restore register clc ; Clear carry for success ret ; Return from subroutine ldrnextcl12errorpop: pop ax ; We had an extra push clear it ldrnextcl12error: pop ax ; Restore input cluster pop cx ; Restore register stc ; Set carry for fail ret ; Return from subroutine ;------------------------------------------------------- ; Get byte from fat (FAT12) ; Input: ; AX = Byte to read ; Output: ; AL = Byte read ;------------------------------------------------------- ldrfatread12: push bx ; Save registers push cx push es push ax ; Set ES to 0000h xor ax, ax mov es, ax pop ax mov bx, 00500h ; Read into scratch area xor dx, dx ; Clear for divide div word [bytespersec] ; Sector of file = FAT index / bytes per sector inc ax ; FAT starts at sector 2 advance ax cmp [ldrsectorload], ax ; Check if this sector is already in scratch jz ldrfatread12noload ; If it is don't load it again mov [ldrsectorload], ax ; Set current loaded FAT sector push dx ; Save FAT index MOD bytes per sector xor dx, dx ; Clear for divide in calcchs call calcchs ; Calculate Cylinder Head Sector jc ldrfatread12error ; If error exit routine call readsec ; Read sector jc ldrfatread12error ; If error exit routine pop dx ; Restore FAT index MOD bytes per sector ldrfatread12noload: add bx, dx ; Byte to read = file index % bytes per sector + scratch mov al, [es:bx] ; Get byte pop es ; Restore registers pop cx pop bx clc ; Carry clear on success ret ; Return from subroutine ldrfatread12error: pop dx ; Restore FAT index MOD bytes per sector pop es ; Restore registers pop cx pop bx stc ; Set carry on fail ret ; Return from subroutine ;------------------------------------------------------- ; Read Data Cluster ; Input: ; AX = Cluster start ; ES:BX = Buffer ; Output: ; Loaded buffer ;------------------------------------------------------- ldrreaddatacluster: xor ch, ch ; Clear ch mov cl, [secperclust] ; CX = Sectors per cluster ldrreadclusternext: push ax ; Save cluster start mul byte [secperclust] add ax, [data] ; Add the data start cluster dec ax ; Subtract 2 because FAT starts at 2 dec ax push cx ; Save count read will destroy it xor dx, dx ; Clear for calcchs call calcchs ; Calculate Cylinder Head Sector jc ldrreaddataclusterfail ; If error exit routine call readsec ; Read sector into buffer jc ldrreaddataclusterfail ; If error exit routine pop cx ; Restore counter pop ax ; Restore cluster start add bx, word [bytespersec] ; Add bytes per sec for next read inc ax ; Increment sector loop ldrreadclusternext ; Get next sector of cluster clc ; Clear carry for success ret ; Return from subroutine ldrreaddataclusterfail: pop cx ; Restore registers pop ax stc ; Set carry for fail ret ; Return from subroutine ;------------------------------------------------------- ; String Output ; Input: ; DS:SI = Message offset ; Output: ; AX, BX, SI are destroyed ;------------------------------------------------------- print: lodsb ; Get character from string or al, al ; Check if our character is zero jz printdone ; if it is zero we have finished mov ah, 0Eh ; BIOS print call mov bx, 00007h ; Character attribute int 010h ; Call the video BIOS jmp print ; Get next character printdone: ret ; Return from subroutine ;------------------------------------------------------- ; Calculate CHS ; Input: ; AX:DX = Logical Sector ; Output: ; sector ; head ; track/cylinder ;------------------------------------------------------- ; sector = (lba % sectors per track) + 1 ; head = lba / sectors per track % number of heads ; track = lba / sectors per track / number of heads calcchs: cmp dx, [secpertrack] ; Check within track jnc calcchsfail ; if not fail div word [secpertrack] ; LBA / secpertrack inc dl ; LBA % secpertrack + 1 mov [sector], dl ; Save in sector xor dx, dx ; Clear the modulous div word [heads] ; lba / secpertrack / heads mov [currenthead], dl ; lba / secpertrack % heads mov [track], ax ; lba / secpertrack / heads clc ; Clear carry to report success ret ; Return from subroutine calcchsfail: stc ; Set carry to report failure ret ; Return from subroutine ;------------------------------------------------------- ; Read Sector ; Input: ; bsTrack = Track ; bsSector = Sector ; bsHead = Head ; ES:BX = Location to load sector at ; Output: ; AX, BX, CX, DX are destroyed ;------------------------------------------------------- readsec: mov dx, [track] ; Get the track mov cl, 006h ; Rotate the bits 8 and 9 into position shl dh, cl or dh, [sector] ; Get the sector with out changing high bits mov cx, dx ; Place in the correct register xchg ch, cl ; Swap bytes to complete the process mov dl, [drivenumber] ; Drive number mov dh, [currenthead] ; Current Head mov ax, 00201h ; BIOS Disk Read 1 Sector int 013h ret
Now we need to fix a couple of things in demoos.asm.
Remove the following code:
cli ; Turn off interrupts cld ; Make sure we are going forward mov ax, cs ; Set all segments to code segment mov ds, ax ; Make sure data segment is zero mov es, ax ; Make sure extra segment is zero mov ss, ax ; Make sure stack segment is zero mov sp, 07C00h ; Place stack ptr before boot sector sti ; Turn on interrupts
Replace the following:
org 00000h ; Assemble code for this offset
with
%include "loader.asm"
Also replace the following:
version: db 00Dh, 00Ah, "DEMO OS V0.11" ; Message to be printed db 00Dh, 00Ah, '$'
with
version: db 00Dh, 00Ah, "DEMO OS V0.12" ; Message to be printed db 00Dh, 00Ah, '$'
;======================================================= ; Demo OS - demoos.asm ; ----------------------------------------------------- ; Written in NASM ; Written by Marcus Kelly ;======================================================= %include "loader.asm" start: call intinit ; Initialize interrupts push ds ; Save DS mov ax, 00FFFh ; Set first MCB mov [mcbfirst], ax ; Save first MCB mov ds, ax mov bx, 07000h ; Size to initialize call meminit ; Initialize memory pop ds ; Restore DS call ver main: call crlf ; Print carriage return / line feed mov ah, 002h ; Service standard output mov dl, '>' ; Print '>' int 021h mov dl, ' ' ; Print Space int 021h mov ah, 00Ah ; Service string input mov dx, buffer ; Set dx to our buffer int 021h call crlf ; Print carriage return / line feed mov di, buffer+2 ; Point to the string we typed mov si, verstr ; Point to command text call cmpcmd ; Compare jc cmdnver ; Carry set if not equal call ver ; Call function jmp main ; We're done checking cmdnver: mov si, clsstr ; Point to command text call cmpcmd ; Compare jc cmdncls ; Carry set if not equal call cls ; Call function jmp main ; We're done checking cmdncls: mov si, echostr ; Point to command text call cmpcmd ; Compare jc cmdnecho ; Carry set if not equal call echo ; Call function jmp main ; We're done checking cmdnecho: mov si, mcbstr ; Point to command text call cmpcmd ; Compare jc cmdnmcb ; Carry set if not equal call mcb ; Call function jmp main ; We're done checking cmdnmcb: mov si, allocstr ; Point to command text call cmpcmd ; Compare jc cmdnalloc ; Carry set if not equal call alloc ; Call function jmp main ; We're done checking cmdnalloc: mov si, freestr ; Point to command text call cmpcmd ; Compare jc cmdnfree ; Carry set if not equal call free ; Call function jmp main ; We're done checking cmdnfree: mov si, resizestr ; Point to command text call cmpcmd ; Compare jc cmdnresize ; Carry set if not equal call resize ; Call function jmp main ; We're done checking cmdnresize: mov ah, 009h ; Service Print String mov dx, invalid ; Invalid command int 021h ; Our Service call crlf ; New line jmp main ; Repeat forever ;------------------------------------------------------- ; Compare Command ; Input: ; DS:SI = Command String ; ES:DI = Buffer String ; Output: ; Carry set if not equal ; Carry clear if equal ;------------------------------------------------------- cmpcmd: push ax ; Save registers push si push di cmpcmdnext: mov al, [es:di] ; Load char from command line inc di ; Point to next character call toup ; Convert to uppercase mov ah, [ds:si] ; Load char to test against inc si ; Point to next character cmp ah, al ; Compare both characters jz cmpcmdnext ; If they are the same check next dec si ; Adjust to point to last char dec di cmp [ds:si], byte 0 ; Our test command end should be 0 jnz cmpcmdne ; If it isn't not our command cmp [es:di], byte ' ' ; Our input cmd might be space jz cmpcmde ; If it is this is our command cmp [es:di], byte 00Dh ; Our input cmd might be CR jz cmpcmde ; If it is this is our command jmp cmpcmdne ; Otherwise not our command cmpcmde: pop di ; Restore registers pop si pop ax clc ; Clear carry for match ret ; Return from subroutine cmpcmdne: pop di ; Restore registers pop si pop ax stc ; Set carry if not a match ret ; Return from subroutine ;------------------------------------------------------- ; Version Command ; Input: ; None ; Output: ; AX, DX are Destroyed ;------------------------------------------------------- ver: mov ah, 009h ; Service string output mov dx, version ; dx is set to offset version int 021h ; Call the service ret ;------------------------------------------------------- ; Clear Screen Command ; Input: ; None ; Output: ; AX is Destroyed ;------------------------------------------------------- cls: mov ah, 00Fh ; Get video mode int 010h mov ah, 000h ; Set video mode int 010h ; this automatically clears the screen ret ;------------------------------------------------------- ; Echo Command ; Input: ; None ; Output: ; None ;------------------------------------------------------- echo: push ax ; Save registers push dx push di xor ah, ah ; Clear upper part of ax mov al, [buffer+1] ; Get count from buffer mov di, buffer+2 ; Get start of buffer add di, ax ; Add for end of buffer mov al, '$' ; Place '$' at end for printing mov [di], al mov ah, 009h ; Service print string mov dx, buffer+7 ; Get start of string to print int 021h ; 7 to exclude echo call crlf pop di ; Restore regsiters pop dx pop ax ret ; Return from subroutine ;------------------------------------------------------- ; Memory Control Block Command ; Input: ; None ; Output: ; AX is Destroyed ;------------------------------------------------------- mcb: push ax ; Save Registers push dx push ds call crlf ; Print CR / LF mov ax, [mcbfirst] ; DS = first mcb mov ds, ax mcbnext: mov ax, ds ; Print arena segment call hexwordout mov ah, 002h mov dl, ' ' ; Print space int 021h mov ah, 002h ; Print signature mov dl, [ds:memarena.signature] push dx ; Save signature int 021h mov ah, 002h mov dl, ' ' ; Print space int 021h mov ax, [ds:memarena.owner] ; Print owner call hexwordout mov ah, 002h mov dl, ' ' ; Print space int 021h mov ax, [ds:memarena.size] ; Print size call hexwordout mov ah, 002h mov dl, ' ' ; Print space int 021h xor si, si ; Print name mov cx, 8 mcbnextname: mov dl, [ds:si+memarena.name] ; Read name byte mov ah, 002h ; Print name byte int 021h inc si ; Increment name byte loop mcbnextname ; Get next name byte pop dx ; Restore signature cmp dl, 'Z' ; Compare to end arena jz mcbdone ; if yes were done call memarenanext ; Get next arena jc mcbdone ; failed were done call crlf ; Print CR / LF for next arena jmp mcbnext ; Next arena mcbdone: call crlf ; Print CR / LF pop ds ; Restore registers pop dx pop ax ret ; Return from subroutine ;------------------------------------------------------- ; Memory Allocate Command ; Input: ; Command line hex number ; Output: ; None ;------------------------------------------------------- alloc: push ax ; Save registers push bx push si mov si, buffer+8 ; Get buffer + 8 point to hex input call hexwordin ; Get hex input mov bx, ax ; Allocate input size mov ah, 048h ; Service Allocate int 021h jc allocerror call crlf ; Print CR / LF call hexwordout ; Show allocated page start call crlf ; Print CR / LF allocerror: pop si ; Restore registers pop bx pop ax ret ; Return from subroutine ;------------------------------------------------------- ; Memory Free Command ; Input: ; Command line hex number ; Output: ; None ;------------------------------------------------------- free: push ax ; Save registers push si push es mov si, buffer+7 ; Get buffer + 8 point to hex input call hexwordin ; Get hex input mov es, ax ; Free page mov ah, 049h ; Service Free int 021h jc freeerror freeerror: pop es pop si ; Restore registers pop ax ret ; Return from subroutine ;------------------------------------------------------- ; Memory Resize Command ; Input: ; Command line hex number ; Output: ; None ;------------------------------------------------------- resize: push ax ; Save registers push si push es mov si, buffer+9 ; Get buffer + 8 point to hex input call hexwordin ; Get hex input mov es, ax ; Free page mov si, buffer+14 call hexwordin ; Get hex input mov bx, ax mov ah, 04Ah ; Service Resize int 021h jc resizeerror resizeerror: pop es pop si ; Restore registers pop ax ret ; Return from subroutine %include "io.asm" %include "int.asm" %include "mem.asm" ;------------------------------------------------------- ; Data Area ;------------------------------------------------------- verstr: db "VER", 0 clsstr: db "CLS", 0 echostr: db "ECHO", 0 mcbstr: db "MCB", 0 allocstr: db "ALLOC", 0 freestr: db "FREE", 0 resizestr: db "RESIZE", 0 version: db 00Dh, 00Ah, "DEMO OS V0.12" ; Message to be printed db 00Dh, 00Ah, '$' invalid: db 00Dh, 00Ah, "Invalid Command" ; Message to be printed db 00Dh, 00Ah, '$' buffer: db 64 ; Buffer for string input db 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 db 0, 0, 0, 0, 0, 0, 0, 0 mcbfirst: dw 0
Now that we have all the code to read the fat we need a fat in our disk image.
The following routine is a macro to generate a fat chain so we don't have to type it manually.
;------------------------------------------------------- ; FAT12 Generator ; Usage: ; FAT12 Cluster, Filesize ;------------------------------------------------------- %macro FAT12 2 ; FAT STARTBLOCK FILESIZE %assign i %1 ; FAT Block Number %assign j %2 ; File Size %assign k 512 ; Cluster Size %assign m j %assign j 0 ; j = j mod k %rep 100 ; repeate 100 times or m is less than 512 %assign j j+1 ; j ++ %assign m m-k ; subtract sector size %if m0 ; if m is still greater than zero increment j %assign j j+1 %endif %exitrep ; we are done with our divide %endif %endrep %assign l i+j-1 ; l = FAT Number + Number of FAT Blocks for file %assign j j*3/2 ; Position in FAT %rep j ; Numver of blocks to write %if i=l %assign x 0FFFh ; Last FAT Block for File 0xFFF %if i&1 ; If odd db ((x<<4)&0F0h)+(i>>8) db (x>>4)&0FFh) %else ; If even db x&0FFh db (x>>8)&00Fh db 000h %endif %exitrep %endif %if i&1 ; If odd db ((i+1<<4)&0xF0)+(i>>8) db (i+1>>4)&0xFF) %else db i+1&0xFF %endif %assign i i+1 ; increment i %endrep %endmacro
Replace the following:
incbin "demoos2.bin" ; include our demoos2.bin file as binary
with
times (002h*3/2)+512-($-$$) db 0 ; Fat for demoos.bin 002h is Cluster FAT12 002h, 1846 ; 002h Cluster, 1846 file size
;======================================================= ; Disk Image - osdisk.asm ; ----------------------------------------------------- ; Written in NASM ; Written by Marcus Kelly ;======================================================= ;------------------------------------------------------- ; FAT12 Generator ; Usage: ; FAT12 Cluster, Filesize ;------------------------------------------------------- %macro FAT12 2 ; FAT STARTBLOCK FILESIZE %assign i %1 ; FAT Block Number %assign j %2 ; File Size %assign k 512 ; Cluster Size %assign m j %assign j 0 ; j = j mod k %rep 100 ; repeate 100 times or m is less than 512 %assign j j+1 ; j ++ %assign m m-k ; subtract sector size %if m0 ; if m is still greater than zero increment j %assign j j+1 %endif %exitrep ; we are done with our divide %endif %endrep %assign l i+j-1 ; l = FAT Number + Number of FAT Blocks for file %assign j j*3/2 ; Position in FAT %rep j ; Numver of blocks to write %if i=l %assign x 0FFFh ; Last FAT Block for File 0xFFF %if i&1 ; If odd db ((x<<4)&0F0h)+(i>>8) db (x>>4)&0FFh) %else ; If even db x&0FFh db (x>>8)&00Fh db 000h %endif %exitrep %endif %if i&1 ; If odd db ((i+1<<4)&0xF0)+(i>>8) db (i+1>>4)&0xFF) %else db i+1&0xFF %endif %assign i i+1 ; increment i %endrep %endmacro ;------------------------------------------------------- ; Boot Sector ;------------------------------------------------------- incbin "boot.bin" ; include our boot.bin file as binary ;------------------------------------------------------- ; FAT Table 1 ;------------------------------------------------------- db 0F0h, 0FFh, 0FFh times (002h*3/2)+512-($-$$) db 0 ; Fat for demoos.bin 002h is Cluster FAT12 002h, 1846 ; 002h Cluster, 1846 file size ;------------------------------------------------------- ; Root Directory ;------------------------------------------------------- times 9728-($-$$) db 0 ; Place our file at 9728 db "DEMOOS BIN" ; Filename db 021h ; Attributes db 0 ; Reserved db 0, 0, 0, 0, 0 ; Reserved db 0, 0 ; Access Date dw 0 ; First Cluster Address High db 0C0h, 032h, 0BFh, 01Ch ; Written Date/Time dw 00002h ; First Cluster Address Low dd demoosend-demoosstart ; File Size ;------------------------------------------------------- ; Data Area ;------------------------------------------------------- times 16896-($-$$) db 0 ; (32 * 224 + 512 - 1) / 512 = 14 demoosstart: incbin "demoos.bin" ; 14 * 512 + 9728 = 16896 demoosend: ;------------------------------------------------------- ; Correct Disk Size ;------------------------------------------------------- times 1474560-($-$$) db 0 ; Make file size 1,474,560 bytes long ; This is the size of a floppy disk
Now lets build our disk. If you are running the previous tutorial in VirtualBox Remember to remove the disk or close virtual box before assembling the disk image.
> nasm demoos.asm -o demoos.bin > nasm osdisk.asm -o osdisk.img
Your osdisk.img can now be loaded with VirtualBox. The result should be as follows:
DEMO OS V0.12 >Memory: Resize | Index | Disk: VOL