Basics: Service | Index | Basics: CLS

Write Your Own OS from Scratch - Chapter 2. Basics

Basics: Command

Now we are going to make our operating system do something when we type a command.

In demoos.asm take the following code:

    mov     ah, 009h                                    ; Service string output
    mov     dx, version                                 ; dx is set to offset version
    int     021h 
		

Move it down below jmp main then just before it add the following:

 ;-------------------------------------------------------
; Version Command
; Input:
;      None
; Output:
;      AX, DX are Destroyed
;-------------------------------------------------------
ver:
		

Just after it add the following:

    ret
		

Where you copied the code from add the following:

     call   ver
		

Now we need to be able to test if we typed a command. Add the following code

;-------------------------------------------------------
; 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
		

Now we need something to compare what we typed to. In the data area add:

verstr:         db      "VER", 0
		

Also while you are in the data area lets add a string for invalid commands.

invalid:        db      00Dh, 00Ah, "Invalid Command"   ; Message to be printed
                db      00Dh, 00Ah, '$'
		

Inside the main loop after we call the buffer lets do the check and call our new command.

    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    ah, 009h                                     ; Service Print String
    mov    dx, invalid                                  ; Invalid command
    int    021h                                         ; Our Service
    call   crlf                                         ; New line
		

Then change:

version:        db      00Dh, 00Ah, "DEMO OS V0.04"     ; Message to be printed
                db      00Dh, 00Ah, '$'
		

to

version:        db      00Dh, 00Ah, "DEMO OS V0.05"     ; Message to be printed
                db      00Dh, 00Ah, '$'
		

demoos.asm

;=======================================================
; Demo OS - demoos.asm
; -----------------------------------------------------
; Written in NASM
; Written by Marcus Kelly
;=======================================================

    org     00000h                                      ; Assemble code for this offset

start:
    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

    call   intinit                                      ; Initialize interrupts
    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    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
    call    crlf
    ret

%include "io.asm"
%include "int.asm"

;-------------------------------------------------------
; Data Area
;-------------------------------------------------------

verstr:         db      "VER", 0

version:        db      00Dh, 00Ah, "DEMO OS V0.05"     ; 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
		

Add toup function in io.asm

Open io.asm and add the following:

;-------------------------------------------------------
; To Upper Case
; Input:
;      al = Character
; Output:
;      al = Character
;-------------------------------------------------------
toup:
    cmp    al, 'a'                                      ; Compare to "a"
    jl     toupdone                                     ; If less than a don't change
    cmp    al, 'z'                                      ; Compare to "z"
    jg     toupdone                                     ; If greater than z don't change
    and    al, 0DFh                                     ; Remove uppercase bit
toupdone:
    ret                                                 ; Return from subroutine
		

io.asm

;=======================================================
; Input / Output - io.asm
; -----------------------------------------------------
; Written in NASM
; Written by Marcus Kelly
;=======================================================

;-------------------------------------------------------
; Standard Output
; Input:
;       DL = Character to print
; Output:
;       AL = Input Character
;-------------------------------------------------------
stdout:
    mov    al, dl                                       ; Place character in al 
    push   ax						; Save ax
    push   bx						; Save bx
    mov    ah, 00Eh                                     ; BIOS Service Print Character
    mov    bx, 00007h
    int    010h
    pop    bx                                           ; Restore bx
    pop    ax                                           ; Restore ax
    ret							; Return from subroutine

;-------------------------------------------------------
; String Output
; Input:
;       DS:DX = String to print
; Output:
;       AL = '$'
;-------------------------------------------------------
strout:
    push   dx                                           ; Save dx
    push   si                                           ; Save si
    mov    si, dx                                       ; Move string pointer to si
stroutnext:
    lodsb                                               ; Get character from string
    cmp    al, '$'                                      ; Check end of string
    jz     stroutdone                                   ;    if yes we are done
    mov    dl, al                                       ; Move char to dl for stdout
    call   stdout                                       ; Call stdout
    jmp    stroutnext                                   ; Next character
stroutdone:
    pop    si                                           ; Restore si
    pop    dx                                           ; Restore di
    mov    ah, 9                                        ; Place 9 back into ah
    ret							; Return from subroutine


;-------------------------------------------------------
; Standard Input
; Input:
;       None
; Output:
;       AL = Character
;-------------------------------------------------------
stdin:
    xor    ax, ax                                       ; Wait for key BIOS Call
    int    016h
    mov    ah, 8                                        ; Restore ah to 8
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; String Input
; Input:
;       DS:DX = Input Buffer Structure
;       db Buffer Size
;       dw 0  (This has a function we aren't using)
;       db rep[Buffer Size]
; Output:
;       DS:DX+3 = Input String
;-------------------------------------------------------
strin:
    push   ax                                           ; Save registers
    push   bx
    push   cx
    push   dx
    push   di
    push   es
    push   ds                                           ; Move ds to es
    pop    es
    mov    di, dx                                       ; Move dx to di
    xor    ch, ch
    mov    cl, [es:di]                                  ; cl = buffer size remaining
    or     cl, cl                                       ; if cl is zero we are done
    jz     strindone
    add    di, 2                                        ; Skip to start of buffer
    mov    bx, di                                       ; Save start of buffer
strinnext:
    call   stdin                                        ; Wait for key press
    cmp    al, 0x0d                                     ; Check if enter was pressed
    jz     strinenter                                   ;   if yes exit routine
    cmp    al, 0x08                                     ; Check if backspace was pressed
    jz     strinbs                                      ;   if yes goto backspace
    or     cl, cl                                       ; Check if buffer is full
    jz     strinbf                                      ;   if yes goto buffer full
    mov    dl, al                                       ; Otherwise output character
    call   stdout
    stosb                                               ; Store character in the buffer
    dec    cl                                           ; Decriment buffer size remaining
    jmp    strinnext                                    ; Get next character
strinbs:
    cmp    di, bx                                      ; Check if we are at buffer start
    jz     strinnext                                   ;   if yes skip and get next key
    dec    di                                          ; Overwrite previous character 
    mov    al, 0                                       ;    in memory
    stosb
    dec    di                                          ; Backup 1 space in memory
    mov    dl, 8                                       ; Overwrite previous character
    call   stdout                                      ;   on the screen
    mov    dl, 0
    call   stdout
    mov    dl, 8                                       ; Backup 1 space on screen
    call   stdout
    inc    cl                                          ; Add 1 to buffer size remaining
    jmp    strinnext                                   ; Get next character
strinbf:
    jmp    strinnext                                   ; Buffer full get next character
strinenter:
    stosb                                              ; Store 00Dh in the buffer
    dec    cl                                          ; Add it to the count
    mov    ax, [es:bx-2]                               ; get buffer size
    sub    ax, cx                                      ; subtract remaining to get length
    dec    ax                                          ; 0D does not count in lenght
    mov    [es:bx-1], al                               ; Store length in buffer
strindone:
    pop    es                                          ; Restore registers
    pop    di
    pop    dx
    pop    cx
    pop    bx
    pop    ax
    ret                                                ; Return from subroutine

;-------------------------------------------------------
; Carrige Return / Line Feed
; Input:
;       None
; Output:
;       None
;-------------------------------------------------------
crlf:
    push   ax                                           ; Save registers
    push   dx
    mov    dl, 00Dh                                     ; Output carriage return
    call   stdout
    mov    dl, 00Ah                                     ; Output line feed
    call   stdout
    pop    dx                                           ; Restore registers
    pop    ax
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; To Upper Case
; Input:
;      al = Character
; Output:
;      al = Character
;-------------------------------------------------------
toup:
    cmp    al, 'a'                                      ; Compare to "a"
    jl     toupdone                                     ; If less than a don't change
    cmp    al, 'z'                                      ; Compare to "z"
    jg     toupdone                                     ; If greater than z don't change
    and    al, 0DFh                                     ; Remove uppercase bit
toupdone:
    ret                                                 ; Return from subroutine
		

Modify the disk image

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.05

> ver

DEMO OS V0.05

>
		
Basics: Service | Index | Basics: CLS

Support This Project On Patreon

Copyright © 2021 Marcus Kelly