Basics: Output | Index | Basics: Service

Write Your Own OS from Scratch - Chapter 2. Basics

Basic: Input

We will start off by adding a keyboard input routine to io.asm

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

Next we add our string input routine. It may look like a lot of code but it can make a lot of future tasks simpler.

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

The next code we are going to add is an output not an input, but we will use it quite a bit. The routine prints a carrage return and a line feed to return the cursor to the beginning of the line and move it down a line.

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

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
		

Testing String Input

In demoos.asm we will add some code to test the String Input Function. The first thing we need to do is reserve space for the buffer.

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
		

Then replace the die loop with our test code. Replace lines:

die:                                                    ; This is our die loop so the
    jmp     die                                         ;  computer doesn't run random
                                                        ;  code
		

with

main:
    call   crlf                                         ; Print carriage return / line feed
    mov    dl, '>'                                      ; Print '>'
    call   stdout
    mov    dl, ' '                                      ; Print Space
    call   stdout
    mov    dx, buffer                                   ; Set dx to our buffer
    call   strin                                        ; Call strin
    call   crlf                                         ; Print carriage return / line feed
    jmp    main                                         ; Repeat forever
		

Replace this:

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

with

version:        db      00Dh, 00Ah, "DEMO OS V0.03"     ; 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
    xor     ax, ax                                      ; Zero the ax register
    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

    push    cs                                          ; Make sure data segment = code segment
    pop     ds

    mov     dx, version                                 ; dx is set to offset version
    call    strout                                      ; call our string output routine

main:
    call   crlf                                         ; Print carriage return / line feed
    mov    dl, '>'                                      ; Print '>'
    call   stdout
    mov    dl, ' '                                      ; Print Space
    call   stdout
    mov    dx, buffer                                   ; Set dx to our buffer
    call   strin                                        ; Call strin
    call   crlf                                         ; Print carriage return / line feed
    jmp    main                                         ; Repeat forever

%include "io.asm"


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

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

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

You should also be able to type text.

Basics: Output | Index | Basics: Service

Support This Project On Patreon

Copyright © 2021 Marcus Kelly