Memory: Initialize | Index | Memory: Free

Write Your Own OS from Scratch - Chapter 3. Memory

Memory: Allocate

In mem.asm we are going to add the allocation function and merge arena function

;-------------------------------------------------------
; Memory Allocate
; Input:
;      BX = number of paragraphs to allocate
; Output:
;      CF clear if successful
;      AX = segment of allocated block
;      CF set on error
;      AX = error code (07h,08h) (Not Implemented)
;      BX = size of largest available block
;-------------------------------------------------------
memalloc:
    push   cx                                           ; Save registers
    push   ds
    xor    ax, ax                                       ; Reset found arenas to zero
    mov    [cs:memfirst], ax
    mov    [cs:membest], ax
    mov    [cs:memlargest], ax
    mov    [cs:memlast], ax
    mov    ax, [cs:mcbfirst]                            ; Get first arena
    mov    ds, ax
memallocnext:
    call   memchecksignature                            ; Check valid arena
    jc     memallocerror
    mov    ax, [ds:memarena.owner]                      ; Check if owned
    or     ax, ax
    jnz    memallocinc                                  ;    If owned goto next arena
    call   memmergefree                                 ; Not owned merge next arena if free
    jc     memallocerror
    mov    cx, [ds:memarena.size]                       ; Check if big enough
    cmp    bx, cx                                       ; If request is bigger in size
    jg     memallocinc                                  ;   If not big enough goto next arena
    cmp    [cs:memfirst], word 0                        ; Check if first found is set
    jne    memallocbest
    mov    [cs:memfirst], ds                            ; If not set first found
memallocbest:
    cmp    [cs:membest], word 0                         ; Check if best found is set
    jz     memallocsbest
    cmp    [cs:membest], cx                             ; See if this arena is smaller
    jl     memalloclargest
memallocsbest:
    mov    [cs:membest], ds                             ; If smaller set new best
memalloclargest:
    cmp    [cs:memlargest], word 0                      ; Check if largest found is set
    jz     memallocslargest
    cmp    [cs:memlargest], cx                          ; See if this arena is bigger
    jg     memalloclast
memallocslargest:
    mov    [cs:memlargest], ds                          ; Set largest found   
    mov    [cs:memlargestsize], cx                      ; Set largest available size
memalloclast:
    mov    [cs:memlast], ds                             ; Always set last found
memallocinc:
    cmp    [ds:memarena.signature], byte 'Z'            ; Check if arena is the last block
    jz     memallocdone                                 ;   If it is we are done
    call   memarenanext                                 ; Point to next arena
    jc     memallocerror
    jmp    memallocnext                                 ; Process next arena
memallocdone:
    cmp    [cs:memmode], byte 1                         ; If mode = 1 use memfirst
    jnz     memallocnfirst
    mov    ax, [cs:memfirst]
    jmp    memallocmake
memallocnfirst:
    cmp    [cs:memmode], byte 0                         ; If mode = 0 use membest
    jnz    memallocnbest
    mov    ax, [cs:membest]
    jmp    memallocmake
memallocnbest:
    cmp    [cs:memmode], byte 2                         ; If mode = 2 use memlargest
    jnz    memallocnlargest
    mov    ax, [cs:memlargest]
    jmp    memallocmake
memallocnlargest:
    mov    ax, [cs:memlast]                             ; Else use memlast
memallocmake:
    mov    ds, ax                                       ; Point to selected arena  
    mov    cx, [ds:memarena.size]                       ; Get size of arena
    sub    cx, bx                                       ; Subtract request size
    jz     memallocset
    add    ax, bx                                       ; Add size +1 to arena
    inc    ax                                           ;   for next arena
    push   es                                           ; Save ES
    mov    es, ax                                       ; Point to next arena
    dec    cx                                           ; Add a new arena
    mov    [es:memarena.size], cx                       ; Set size of next arena
    mov    [ds:memarena.size], bx                       ; Set allocated arena size
    mov    al, [ds:memarena.signature]                  ; Move signature from this arena
    mov    [es:memarena.signature], al                  ;    to the next arena
    mov    [ds:memarena.signature], byte 'M'            ; Set this arenas signature
    mov    [es:memarena.owner], word 0                  ; Make sure next arena owner is none
    pop    es                                           ; Restore ES
memallocset:
    mov    ax, ds                                       ; Get allocated arena
    inc    ax                                           ; Allocated page starts right after arena
    mov    [ds:memarena.owner], ax
    sub    [cs:memlargestsize], bx                      ; Subtract request size form largest
    mov    bx, [cs:memlargestsize]                      ; BX = largest allocation size
    pop    ds                                           ; Restore registers
    pop    cx
    clc                                                 ; Clear carry for success
    ret                                                 ; Return from subroutine
memallocerror:
    pop    ds                                           ; Restore registers
    pop    cx
    stc                                                 ; Set carry for error
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Memory Merge Free
; Input:
;      DS = Arena
; Output:
;      None
;-------------------------------------------------------
memmergefree:
    push   cx                                            ; Save registers
    push   es
memmergefreenext:
    cmp    [ds:memarena.signature], byte 'Z'             ; If last block don't attempt merge
    jz     memmergefreedone
    push   ds                                            ; Save current arena
    call   memarenanext                                  ; Get next arena
    jc     memmergefreepop
    push   ds                                            ; ES = Next arena
    pop    es
    pop    ds                                            ; Restore current arena
    cmp    [es:memarena.owner], word 0                   ; Check if next arena is free
    jnz    memmergefreedone                              ; If not don't merge
    mov    cx, [es:memarena.size]                        ; Get next arena size
    inc    cx                                            ; add 1 for arena erase
    add    [ds:memarena.size], cx                        ; add to current arena size
    mov    cl, [es:memarena.signature]                   ; Copy signature backwards
    mov    [ds:memarena.signature], cl                   ;   incase it is 'Z'
    jmp    memmergefreenext                              ; Try another merge
memmergefreepop:
    pop    ds                                            ; We were done before pop do it now
memmergefreedone:
    pop    es                                            ; Restore registers
    pop    cx
    ret                                                  ; Return from subroutine

		

mem.asm

;=======================================================
; Memory - mem.asm
; -----------------------------------------------------
; Written in NASM
; Written by Marcus Kelly
;=======================================================
struc memarena
    .signature  resb 1
    .owner      resw 1
    .size       resw 1
    .reserved   resb 3
    .name       resb 8
endstruc

memfirst:       dw      0
membest:        dw      0
memlargest:     dw      0
memlast:        dw      0
memlargestsize: dw      0
memmode:        db      0

;-------------------------------------------------------
; Memory Initialize
; Input:
;      ds = mcb first block
;      bx = size
; Output:
;      None
;-------------------------------------------------------
meminit:
    push   ax                                           ; Save registers
    push   cx
    mov    al, 'Z'                                      ; Set arena signature to end
    mov    [ds:memarena.signature], al
    mov    [ds:memarena.size], bx                       ; Set arena size
    xor    ax, ax                                       ; Set arena owner
    mov    [ds:memarena.owner], ax
    mov    [memmode], byte 1                            ; Memory mode allocate first
    pop    cx                                           ; Restore registers
    pop    ax
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Memory Allocate
; Input:
;      BX = number of paragraphs to allocate
; Output:
;      CF clear if successful
;      AX = segment of allocated block
;      CF set on error
;      AX = error code (07h,08h) (Not Implemented)
;      BX = size of largest available block
;-------------------------------------------------------
memalloc:
    push   cx                                           ; Save registers
    push   ds
    xor    ax, ax                                       ; Reset found arenas to zero
    mov    [cs:memfirst], ax
    mov    [cs:membest], ax
    mov    [cs:memlargest], ax
    mov    [cs:memlast], ax
    mov    ax, [cs:mcbfirst]                            ; Get first arena
    mov    ds, ax
memallocnext:
    call   memchecksignature                            ; Check valid arena
    jc     memallocerror
    mov    ax, [ds:memarena.owner]                      ; Check if owned
    or     ax, ax
    jnz    memallocinc                                  ;    If owned goto next arena
    call   memmergefree                                 ; Not owned merge next arena if free
    jc     memallocerror
    mov    cx, [ds:memarena.size]                       ; Check if big enough
    cmp    bx, cx                                       ; If request is bigger in size
    jg     memallocinc                                  ;   If not big enough goto next arena
    cmp    [cs:memfirst], word 0                        ; Check if first found is set
    jne    memallocbest
    mov    [cs:memfirst], ds                            ; If not set first found
memallocbest:
    cmp    [cs:membest], word 0                         ; Check if best found is set
    jz     memallocsbest
    cmp    [cs:membest], cx                             ; See if this arena is smaller
    jl     memalloclargest
memallocsbest:
    mov    [cs:membest], ds                             ; If smaller set new best
memalloclargest:
    cmp    [cs:memlargest], word 0                      ; Check if largest found is set
    jz     memallocslargest
    cmp    [cs:memlargest], cx                          ; See if this arena is bigger
    jg     memalloclast
memallocslargest:
    mov    [cs:memlargest], ds                          ; Set largest found   
    mov    [cs:memlargestsize], cx                      ; Set largest available size
memalloclast:
    mov    [cs:memlast], ds                             ; Always set last found
memallocinc:
    cmp    [ds:memarena.signature], byte 'Z'            ; Check if arena is the last block
    jz     memallocdone                                 ;   If it is we are done
    call   memarenanext                                 ; Point to next arena
    jc     memallocerror
    jmp    memallocnext                                 ; Process next arena
memallocdone:
    cmp    [cs:memmode], byte 1                         ; If mode = 1 use memfirst
    jnz     memallocnfirst
    mov    ax, [cs:memfirst]
    jmp    memallocmake
memallocnfirst:
    cmp    [cs:memmode], byte 0                         ; If mode = 0 use membest
    jnz    memallocnbest
    mov    ax, [cs:membest]
    jmp    memallocmake
memallocnbest:
    cmp    [cs:memmode], byte 2                         ; If mode = 2 use memlargest
    jnz    memallocnlargest
    mov    ax, [cs:memlargest]
    jmp    memallocmake
memallocnlargest:
    mov    ax, [cs:memlast]                             ; Else use memlast
memallocmake:
    mov    ds, ax                                       ; Point to selected arena  
    mov    cx, [ds:memarena.size]                       ; Get size of arena
    sub    cx, bx                                       ; Subtract request size
    jz     memallocset
    add    ax, bx                                       ; Add size +1 to arena
    inc    ax                                           ;   for next arena
    push   es                                           ; Save ES
    mov    es, ax                                       ; Point to next arena
    dec    cx                                           ; Add a new arena
    mov    [es:memarena.size], cx                       ; Set size of next arena
    mov    [ds:memarena.size], bx                       ; Set allocated arena size
    mov    al, [ds:memarena.signature]                  ; Move signature from this arena
    mov    [es:memarena.signature], al                  ;    to the next arena
    mov    [ds:memarena.signature], byte 'M'            ; Set this arenas signature
    mov    [es:memarena.owner], word 0                  ; Make sure next arena owner is none
    pop    es                                           ; Restore ES
memallocset:
    mov    ax, ds                                       ; Get allocated arena
    inc    ax                                           ; Allocated page starts right after arena
    mov    [ds:memarena.owner], ax
    sub    [cs:memlargestsize], bx                      ; Subtract request size form largest
    mov    bx, [cs:memlargestsize]                      ; BX = largest allocation size
    pop    ds                                           ; Restore registers
    pop    cx
    clc                                                 ; Clear carry for success
    ret                                                 ; Return from subroutine
memallocerror:
    pop    ds                                           ; Restore registers
    pop    cx
    stc                                                 ; Set carry for error
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Memory Merge Free
; Input:
;      DS = Arena
; Output:
;      None
;-------------------------------------------------------
memmergefree:
    push   cx                                            ; Save registers
    push   es
memmergefreenext:
    cmp    [ds:memarena.signature], byte 'Z'             ; If last block don't attempt merge
    jz     memmergefreedone
    push   ds                                            ; Save current arena
    call   memarenanext                                  ; Get next arena
    jc     memmergefreepop
    push   ds                                            ; ES = Next arena
    pop    es
    pop    ds                                            ; Restore current arena
    cmp    [es:memarena.owner], word 0                   ; Check if next arena is free
    jnz    memmergefreedone                              ; If not don't merge
    mov    cx, [es:memarena.size]                        ; Get next arena size
    inc    cx                                            ; add 1 for arena erase
    add    [ds:memarena.size], cx                        ; add to current arena size
    mov    cl, [es:memarena.signature]                   ; Copy signature backwards
    mov    [ds:memarena.signature], cl                   ;   incase it is 'Z'
    jmp    memmergefreenext                              ; Try another merge
memmergefreepop:
    pop    ds                                            ; We were done before pop do it now
memmergefreedone:
    pop    es                                            ; Restore registers
    pop    cx
    ret                                                  ; Return from subroutine

;-------------------------------------------------------
; Memory Next Arena
; Input:
;      DS = Arena
; Output:
;      DS = Next Arena
;-------------------------------------------------------
memarenanext:
    push   ax                                         ; Save AX
    mov    ax, ds                                     ; Get current arena
    add    ax, [ds:memarena.size]                     ; Add arena size
    inc    ax                                         ; Increment past end of alloc
    mov    ds, ax                                     ; Set new arena
    pop    ax                                         ; Restore AX
                                                      ; Drop into check signature
;-------------------------------------------------------
; Memory Check Signature
; Input:
;      DS = Arena
; Output:
;      Carry Set = Not Valid
;      Carry Clear = Valid
;-------------------------------------------------------
memchecksignature:
    cmp    [ds:memarena.signature], byte 'M'            ; Check if signature M
    jz     memchecksignaturevalid                       ;   if yes then valid
    cmp    [ds:memarena.signature], byte 'Z'            ; Check if signature Z
    jz     memchecksignaturevalid                       ;   if yes then valid
    stc                                                 ; Set carry not valid
    ret                                                 ; Return from subroutine
memchecksignaturevalid:
    clc                                                 ; Clear carry valid
    ret                                                 ; Return from subroutine
		

Service

In int.asm we will add a service call to allocate. Inside of inthook add:

    cmp    ah, 048h                                     ; Service 48h: alloc
    jnz    inthookdone48
    call   memalloc
    jmp    inthookdone
inthookdone48:
		

int.asm

;=======================================================
; Service - int.asm
; -----------------------------------------------------
; Written in NASM
; Written by Marcus Kelly
;=======================================================

;-------------------------------------------------------
; Interrupt Hook
;-------------------------------------------------------
inthook:
    cmp    ah, 002h                                     ; Service 02h: stdout
    jnz    inthookdone02
    call   stdout
    jmp    inthookdone
inthookdone02:
    cmp    ah, 008h                                     ; Service 08h: stdin
    jnz    inthookdone08
    call   stdin
    jmp    inthookdone
inthookdone08:
    cmp    ah, 009h                                     ; Service 09h: strout
    jnz    inthookdone09
    call   strout
    jmp    inthookdone
inthookdone09:
    cmp    ah, 00Ah                                     ; Service 0Ah: strin
    jnz    inthookdone0a
    call   strin
    jmp    inthookdone
inthookdone0a:
    cmp    ah, 025h                                     ; Service 25h: setint
    jnz    inthookdone25
    call   intset
    jmp    inthookdone
inthookdone25:
    cmp    ah, 048h                                     ; Service 48h: alloc
    jnz    inthookdone48
    call   memalloc
    jmp    inthookdone
inthookdone48:
inthookdone:
    iret

;-------------------------------------------------------
; Interrupt Set
; Input:
;       AL = Interrupt Number
;       DS = Segment of Interrupt
;       DX = Offset of Interrupt
; Output:
;       None
;-------------------------------------------------------
intset:
    push   ax                                           ; Save registers
    push   cx
    push   di
    push   es
    xor    ah, ah                                       ; DI = INT << 2
    mov    cl, 2                                        ; Essentially multiply by 4
    shl    ax, cl
    mov    di, ax                                       ; di is the address of our int
    xor    ax, ax                                       ; ES = 0 The int table is at 00000h
    mov    es, ax
    mov    ax, dx                                       ; Store offset
    stosw
    mov    ax, ds                                       ; Store segment
    stosw
    pop    es                                           ; Restore registers
    pop    di
    pop    cx
    pop    ax
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Interrupt Initialize
; Input:
;       None
; Output:
;       DX, AX are destroyped
;-------------------------------------------------------
intinit:
    mov    dx, inthook                                  ; Offset to our service routine
                                                        ;    DS should already be the
                                                        ;    correct segment
    mov    al, 021h                                     ; Service 21h
    call   intset                                       ; Set Interrupt 21h
    ret                                                 ; Return from subroutine
		

IO

In io.asm we will add hex word in, hex nibble in, and is hex routines as follows:

;-------------------------------------------------------
; Hex Word In
; Input:
;       ds:si = pointer to string
; Output:
;       ax = value
;       ds:si = pointer to string past hex word
;-------------------------------------------------------
hexwordin:
    push   cx                                           ; Save registers
    push   bx
    xor    bx, bx                                       ; Zero our end value
    xor    cx, cx                                       ; Zero our counter
hexwordinnext:	
    lodsb                                               ; Get character
    call   ishex                                        ; Make sure it is a hex number
    jc     hexwordindone
    shl    bx, 1                                        ; Rotate end value
    shl    bx, 1                                        ;   first pass is pointless
    shl    bx, 1                                        ;   there is no value yet
    shl    bx, 1
    call   hexnibblein                                  ; Convert from ascii to binary
    or     bl, al                                       ; Merge it with end value
    inc    cx                                           ; Increment counter
    jmp    hexwordinnext                                ; Next character
hexwordindone:
    dec    si                                           ; Adjust our string pointer
    mov    ax, bx                                       ; Final value in ax
    pop    bx                                           ; Restore BX
    cmp    cx, 4                                        ; Check less than 4 digits
    jg     hexwordinfail                                ;    if not then fail
    cmp    cx, 0                                        ; Check more than 0 digits
    je     hexwordinfail                                ;    if not then fail
    pop    cx                                           ; Restore CX
    clc                                                 ; Carry clear for success
    ret                                                 ; Return from subroutine
hexwordinfail:
    pop    cx                                           ; Restore CX
    stc                                                 ; Carry set for fail
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Hex Nibble In
; Input:
;       al = Ascii
; Output:
;       al = Binary
;-------------------------------------------------------
hexnibblein:
    call   toup                                         ; Convert to upper case
    sub    al, '0'                                      ; Subtract character '0'
    cmp    al, 00Ah                                     ; If above 9
    jl     hexnibbleindone
    sub    al, 007h                                     ; Subtract difference between '0' and 'A'
hexnibbleindone:
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Is Hex
; Input:
;       al = Ascii
; Output:
;       Carry Set = No
;       Carry Clear = Yes
;-------------------------------------------------------
ishex:
    cmp    al, '0'                                      ; If less than '0'
    jl     ishexno                                      ;   Not hex
    cmp    al, '9'                                      ; If between '0' and '9'
    jle    ishexyes                                     ;   Is hex
    cmp    al, 'A'                                      ; If !'0'-'9' & < 'A'
    jl     ishexno                                      ;   Not hex
    cmp    al, 'F'                                      ; If !'0'-'9' & 'A'-'F'
    jle    ishexyes                                      ;   Is hex
    cmp    al, 'a'                                      ; If !'0'-'9' & < 'a'
    jl     ishexno                                      ;   Not hex
    cmp    al, 'f'                                      ; If !'0'-'9' & !'a'-'f'
    jg     ishexno                                      ;   Not hex
ishexyes:
    clc                                                 ; Carry clear for yes
    ret                                                 ; Return from subroutine
ishexno:
    stc                                                 ; Carry set for no
    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

;-------------------------------------------------------
; Hex Word Output
; Input:
;       AX = Value
; Output:
;       None
;-------------------------------------------------------
hexwordout:
    push   ax                                           ; Save AX
    xchg   ah, al                                       ; Exchange high and low bytes
    call   hexbyteout                                   ; Call hex byte out
    pop    ax                                           ; Restore AX
                                                        ; Drop into hexbyteout for low byte
;-------------------------------------------------------
; Hex Byte Output
; Input:
;       AL = Value
; Output:
;       None
;-------------------------------------------------------
hexbyteout:
    push   ax                                           ; Save registers
    push   cx
    mov    cl, 4                                        ; Shift in upper nibble
    shr    al, cl
    call   hexnibbleout                                 ; Output upper nibble
    pop    cx                                           ; Restore registers
    pop    ax
                                                        ; Drop into hexnibbleout for low nibble
;-------------------------------------------------------
; Hex Nibble Output
; Input:
;       AL[0:3] = Value
; Output:
;       None
;-------------------------------------------------------
hexnibbleout:
    push   ax                                           ; Save registers
    push   dx
    and    al, 00Fh                                     ; Clear upper bits
    cmp    al, 00Ah                                     ; Check greater than "9"
    jl     hexnibbleout2                                ;  if greater add 7
    add    al, 007h
hexnibbleout2:
    add    al, '0'                                      ; Add '0' for number to print
    mov    dl, al                                       ; Move to dl for stdout
    call   stdout
    pop    dx                                           ; Restore registers
    pop    ax
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Hex Word In
; Input:
;       ds:si = pointer to string
; Output:
;       ax = value
;       ds:si = pointer to string past hex word
;-------------------------------------------------------
hexwordin:
    push   cx                                           ; Save registers
    push   bx
    xor    bx, bx                                       ; Zero our end value
    xor    cx, cx                                       ; Zero our counter
hexwordinnext:	
    lodsb                                               ; Get character
    call   ishex                                        ; Make sure it is a hex number
    jc     hexwordindone
    shl    bx, 1                                        ; Rotate end value
    shl    bx, 1                                        ;   first pass is pointless
    shl    bx, 1                                        ;   there is no value yet
    shl    bx, 1
    call   hexnibblein                                  ; Convert from ascii to binary
    or     bl, al                                       ; Merge it with end value
    inc    cx                                           ; Increment counter
    jmp    hexwordinnext                                ; Next character
hexwordindone:
    dec    si                                           ; Adjust our string pointer
    mov    ax, bx                                       ; Final value in ax
    pop    bx                                           ; Restore BX
    cmp    cx, 4                                        ; Check less than 4 digits
    jg     hexwordinfail                                ;    if not then fail
    cmp    cx, 0                                        ; Check more than 0 digits
    je     hexwordinfail                                ;    if not then fail
    pop    cx                                           ; Restore CX
    clc                                                 ; Carry clear for success
    ret                                                 ; Return from subroutine
hexwordinfail:
    pop    cx                                           ; Restore CX
    stc                                                 ; Carry set for fail
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Hex Nibble In
; Input:
;       al = Ascii
; Output:
;       al = Binary
;-------------------------------------------------------
hexnibblein:
    call   toup                                         ; Convert to upper case
    sub    al, '0'                                      ; Subtract character '0'
    cmp    al, 00Ah                                     ; If above 9
    jl     hexnibbleindone
    sub    al, 007h                                     ; Subtract difference between '0' and 'A'
hexnibbleindone:
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Is Hex
; Input:
;       al = Ascii
; Output:
;       Carry Set = No
;       Carry Clear = Yes
;-------------------------------------------------------
ishex:
    cmp    al, '0'                                      ; If less than '0'
    jl     ishexno                                      ;   Not hex
    cmp    al, '9'                                      ; If between '0' and '9'
    jle    ishexyes                                     ;   Is hex
    cmp    al, 'A'                                      ; If !'0'-'9' & < 'A'
    jl     ishexno                                      ;   Not hex
    cmp    al, 'F'                                      ; If !'0'-'9' & 'A'-'F'
    jle    ishexyes                                      ;   Is hex
    cmp    al, 'a'                                      ; If !'0'-'9' & < 'a'
    jl     ishexno                                      ;   Not hex
    cmp    al, 'f'                                      ; If !'0'-'9' & !'a'-'f'
    jg     ishexno                                      ;   Not hex
ishexyes:
    clc                                                 ; Carry clear for yes
    ret                                                 ; Return from subroutine
ishexno:
    stc                                                 ; Carry set for no
    ret                                                 ; Return from subroutine
		

New Command

We will start by adding the string for our new command:

allocstr:       db      "ALLOC", 0
		

Next we will add the code for our new command:

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

Now we will add the check for our new command. inside of main add:

    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:
		

Finally change:

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

to

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


%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

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

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

> alloc 500

1000

> mcb

0FFF M 1000 0500
1500 Z 0000 6B00

>
		
Memory: Initialize | Index | Memory: Free

Support This Project On Patreon

Copyright © 2021 Marcus Kelly