diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | LICENSE.txt | 22 | ||||
-rw-r--r-- | Makefile | 19 | ||||
-rw-r--r-- | ascii.asm | 38 | ||||
-rw-r--r-- | boot.asm | 246 | ||||
-rw-r--r-- | console.asm | 284 | ||||
-rw-r--r-- | constants.asm | 10 | ||||
-rw-r--r-- | disk.asm | 85 | ||||
-rw-r--r-- | kernel.asm | 77 | ||||
-rw-r--r-- | keyboard.asm | 82 | ||||
-rw-r--r-- | make.bat | 9 | ||||
-rw-r--r-- | scancodes.asm | 9 | ||||
-rw-r--r-- | stdio.asm | 28 | ||||
-rw-r--r-- | string.asm | 174 |
14 files changed, 1085 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cf5001 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.bin +*.img diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..ff33a29 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2017 Joseph Hunkeler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6aa7a1d --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +QEMU=qemu-system-i386 + +all: system.img + +system.img: boot.bin kernel.bin + cat $^ > $@ + +boot.bin: boot.asm + nasm -f bin -o $@ $< + +kernel.bin: kernel.asm + nasm -f bin -o $@ $< + +run: system.img + $(QEMU) -m 16 -fda $< + +clean: + rm *.bin *.img +.PHONY: clean diff --git a/ascii.asm b/ascii.asm new file mode 100644 index 0000000..2616042 --- /dev/null +++ b/ascii.asm @@ -0,0 +1,38 @@ +%ifndef _ASCII_ASM +%define _ASCII_ASM + +NUL equ 00h ; null +SOH equ 01h ; start of heading +STX equ 02h ; start of text +ETX equ 03h ; end of text +EOT equ 04h ; end of transmission +ENQ equ 05h ; enquiry +ACK equ 06h ; acknowledge +BEL equ 07h ; bell (audible) +BS equ 08h ; backspace +TAB equ 09h ; horizontal tab +LF equ 0Ah ; line feed +VT equ 0Bh ; vertical tab +FF equ 0Ch ; form feed +CR equ 0Dh ; carriage return +SHO equ 0Eh ; shift out +SHI equ 0Fh ; shift in +DLE equ 10h ; data link escape +DC1 equ 11h ; device control 1 +DC2 equ 12h ; device control 2 +DC3 equ 13h ; device control 3 +DC4 equ 14h ; device control 4 +NAK equ 15h ; negative acknowledge +SYN equ 16h ; synchronous idle +ETB equ 17h ; end of transmission block +CAN equ 18h ; cancel +EM equ 19h ; end of medium +SUBST equ 1Ah ; substitute +ESC equ 1Bh ; escape +FSEP equ 1Ch ; file separator +GSEP equ 1Dh ; group separator +RSEP equ 1Eh ; record separator +USEP equ 1Fh ; unit separator +SPC equ 20h ; space + +%endif diff --git a/boot.asm b/boot.asm new file mode 100644 index 0000000..d6ddfa1 --- /dev/null +++ b/boot.asm @@ -0,0 +1,246 @@ +bits 16 + +jmp start + +CR equ 0Dh +LF equ 0Ah +K_CS_ADDR equ 007eh + +start: + mov ax, 07c0h + mov ds, ax ; set data segment + mov ax, 07e0h + mov ss, ax ; set stack segment + mov sp, 2000h ; 8192k + + push bp ; set up stack frame + mov bp, sp + sub sp, 2 ; local storage + + mov [drive0], dl ; save first detected drive + + call cls ; clear screen + + push 0 ; + call setcursor ; set cursor position + + push banner ; + call puts ; print version + add sp, 2 ; clean up + + push word [drive0] + call disk_reset + add sp, 2 + + xor cx, cx + mov ax, K_CS_ADDR + mov es, ax + + push msg_loading + call puts + add sp, 2 + + mov bx, 0 + mov di, 2 ; start at sector +.loader: + mov al, 1 ; read one sector + mov cx, di ; track/cyl | sector number + mov dh, 0 ; head number + mov dl, [drive0] ; drive number + call disk_read + + push '.' + call putc + add sp, 2 + + add bx, 200h ; increment address by 512 bytes + inc di ; increment sector read count + cmp di, 16 ; 8K (i'll make this smarter later) + jle .loader ; keep reading + + push msg_done + call puts + add sp, 2 + + add sp, 2 ; remove local storage + mov sp, bp + pop bp + + mov dx, [drive0] ; the kernel will need the boot drive number + jmp K_CS_ADDR:0000h ; jump to kernel address + + cli ; disable interrupts + jmp $ ; hang + +panic: + ; Hang system with supplied error message + push bp + mov bp, sp + + push error_msg_panic ; i.e. 'PANIC:' + call puts + add sp, 2 + + push word [bp + 4] ; address of error string buffer + call puts ; print error + add sp, 2 + + cli ; disable interrupts + jmp $ ; hang (no return) + ; stack is dead + + +disk_reset: + push bp + mov bp, sp + pusha + + mov ah, 00h ; reset disk + mov dl, [bp + 4] ; disk number + int 13h ; BIOS disk service + jnc .success + + push error_msg_disk_reset + call panic + +.success: + popa + mov sp, bp + pop bp + ret + + +disk_read: + push bp + mov bp, sp + + push di + mov di, 3 ; retry counter +.readloop: + push ax + push bx + push cx + + mov ah, 02h ; BIOS - read disk sectors + int 13h ; BIOS disk service + + jnc .success + + push dx + call disk_reset + add sp, 2 + + pop cx + pop bx + pop ax + + dec di + jnz .readloop + + push error_msg_disk_read + call panic + add sp, 2 + +.success: + pop di + mov sp, bp + pop bp + ret + + +cls: + push bp + mov bp, sp + pusha + + mov ah, 07h ; BIOS - scroll down + mov al, 00h ; lines to scroll (0 == entire screen) + mov bx, 0700h ; color white/black + ; & video page zero + mov cx, 0 + mov dh, 24 ; rows + mov dl, 79 ; cols + int 10h ; BIOS video service + popa + + mov sp, bp + pop bp + ret + + +setcursor: + push bp + mov bp, sp + pusha + + mov ah, 02h ; BIOS - set cursor position + mov bh, 0 ; video page zero + mov dx, [bp + 4] ; address of new cursor value + int 10h ; BIOS video service + + popa + mov sp, bp + pop bp + ret + + +putc: + ; Write single character at cursor position + push bp + mov bp, sp + pusha + + mov ah, 0eh ; BIOS - teletype + mov al, [bp + 4] ; character + mov bx, 0 ; video page zero + int 10h ; BIOS video service + + popa + mov sp, bp + pop bp + ret + + +puts: + ; Write string buffer at cursor position + push bp + mov bp, sp + pusha + + mov si, [bp + 4] ; address of string buffer + mov bx, 0000h ; + mov ah, 0eh ; BIOS - teletype + +.loop: + lodsb ; load byte at [si] into al + or al, 0 ; 0 | 0 = 0 (detect null terminator) + je .end + int 10h ; BIOS video service + jmp .loop +.end: + popa + mov sp, bp + pop bp + ret + + + +; data +drive0: dw 0 +banner: db "MINOS Bootloader", CR, LF, 0 + +; General messages +msg_loading: db "Loading", 0 +msg_done: db "done!", CR, LF, 0 +msg_disk_reset: db "Drive reset successful.", CR, LF, 0 +msg_disk_read: db "Sector read successful.", CR, LF, 0 + +; Error messages +error_msg_panic: db "PANIC: ", 0 +error_msg_disk_reset: db "Drive reset failed!", CR, LF, 0 +error_msg_disk_read: db "Drive read failed!", CR, LF, 0 + +; boot signature +times 510-($-$$) db 0 +dw 0xAA55 + diff --git a/console.asm b/console.asm new file mode 100644 index 0000000..f992705 --- /dev/null +++ b/console.asm @@ -0,0 +1,284 @@ +%ifndef _CONSOLE_ASM +%define _CONSOLE_ASM + +%include "constants.asm" +%include "stdio.asm" +%include "string.asm" +%include "keyboard.asm" + +MAX_ROWS equ 25 +MAX_COLS equ 80 + +putc: + ; Write single character at cursor position + push bp + mov bp, sp + pusha + + cmp al, 20h + jl .non_graphical + + mov ah, 0ah ; BIOS - write character + mov bx, 00h ; video page zero + mov cx, 01h ; repeat character N times + int 10h ; BIOS video service + +.non_graphical: + push ax + call console_driver + add sp, 2 + + popa + mov sp, bp + pop bp + ret + + +console_scroll_up: + cmp dh, MAX_ROWS - 1 + jne .no_action + + mov dh, MAX_ROWS - 2 + push dx + call setcursor + add sp, 2 + + ; scroll window up: + mov ah, 06h ; scroll up function id. + mov al, 1 ; lines to scroll. + mov bx, 0700h ; attribute for new lines. + mov cl, 0 ; upper col. + mov ch, 0 ; upper row. + mov dl, MAX_COLS ; lower col. + mov dh, MAX_ROWS ; lower row. + int 10h +.no_action: + ret + + +console_driver: + push bp + mov bp, sp + + call console_cursor_getpos + mov dh, [cursor_row] + mov dl, [cursor_col] + + mov ax, [bp + 4] + +.do_fn: +.do_scancode: + cmp al, 00h ; when AL is 00h, check scan-code + jne .do_ascii + + cmp ah, SC_ARROW_LEFT + je .handle_sc_arrow_left + + cmp ah, SC_ARROW_RIGHT + je .handle_sc_arrow_right + + +.handle_sc_arrow_left: + dec dl + jmp .return + +.handle_sc_arrow_right: + inc dl + jmp .return + +.do_ascii: + ; ASCII control block + cmp al, SPC + jae .handle_SPC + + cmp al, TAB + je .handle_TAB + + cmp al, BS + je .handle_BS + + cmp al, CR + je .handle_CR + + cmp al, LF + je .handle_LF + + ; etc... + jmp .return + + +.handle_SPC: + inc dl + cmp dl, MAX_COLS + jge .handle_CR + jmp .return + +.handle_TAB: + add dl, 4 + jmp .return + +.handle_BS: + dec dl + cmp dl, 0 + jl .skip_bs + + push dx + call setcursor + add sp, 2 + +.skip_bs: + mov ah, 0ah + mov al, 20h + mov bx, 00h + mov cx, 1 + int 10h + jmp .return_noupdate + +.handle_CR: + mov dl, 0 ; set column zero + ; fall through + +.handle_LF: + inc dh ; increment row + jmp .return + + +.return: + push dx + call setcursor + add sp, 2 + + +.return_noupdate: + call console_scroll_up + mov sp, bp + pop bp + ret + + +cls: + push bp + mov bp, sp + pusha + + mov ah, 07h ; BIOS - scroll down + mov al, 00h ; lines to scroll (0 == entire screen) + mov bx, 0700h ; color white/black + ; & video page zero + mov cx, 0 + mov dh, 24 ; rows + mov dl, 79 ; cols + int 10h ; BIOS video service + popa + + mov sp, bp + pop bp + ret + +console_cursor_getpos: + push bp + mov bp, sp + pusha + + mov ah, 03h ; BIOS - query cursor position and size + mov bh, 00h ; video page zero + int 10h + + mov [cursor_sl_start], byte ch ; record data + mov [cursor_sl_end], byte cl + mov [cursor_row], byte dh + mov [cursor_col], byte dl + + popa + mov sp, bp + pop bp + ret + +console_cursor_read: + push bp + mov bp, sp + push ax + push bx + + mov ah, 08h ; BIOS - read character/attr at cursor + mov bh, 00h ; video page zero + int 10h + + mov [cursor_attr], byte ah + mov [cursor_char], byte al + + pop bx + pop ax + mov sp, bp + pop bp + ret + +console_cursor_read_last: + push bp + mov bp, sp + push dx + + call console_cursor_getpos + mov dh, [cursor_row] + mov dl, [cursor_col] + + cmp dh, 0 ; is this column zero? + je .finalize + + sub dl, 1 ; previous column + js .prev_row ; column went negative + jmp .finalize + +.prev_row: + cmp dh, 0 ; is this row zero? + je .return + + sub dh, 1 ; go up one row + add dl, 80 ; return to last column of row (-1 + 80 = 79) + +.finalize: + push dx + call setcursor + call console_cursor_getpos + call console_cursor_read + + ; restore original cursor position + mov dh, [cursor_row] + mov dl, [cursor_col] + push dx + call setcursor +.return: + add sp, 4 + pop dx + mov sp, bp + pop bp + ret + + +setcursor: + push bp + mov bp, sp + pusha + + mov ah, 02h ; BIOS - set cursor position + mov bh, 0 ; video page zero + mov dx, [bp + 4] ; address of new cursor value + int 10h ; BIOS video service + + popa + mov sp, bp + pop bp + ret + + +; data +cursor_sl_start: db 0 +cursor_sl_end: db 0 +cursor_row: db 0 +cursor_col: db 0 +cursor_row_vram: dw 0 +cursor_col_vram: dw 0 +cursor_vram: dw 0 +cursor_attr: db 0 +cursor_char: db 0 +%endif diff --git a/constants.asm b/constants.asm new file mode 100644 index 0000000..56c2496 --- /dev/null +++ b/constants.asm @@ -0,0 +1,10 @@ +%ifndef _CONSTANTS_ASM +%define _CONSTANTS_ASM + +LENGTH_ROW equ 0A0h ; NOTE: length in bytes (80 * 2 = 160) + +%include "ascii.asm" ; ASCII control codes +%include "scancodes.asm" ; Keyboard scancodes + +%endif + diff --git a/disk.asm b/disk.asm new file mode 100644 index 0000000..3e43c48 --- /dev/null +++ b/disk.asm @@ -0,0 +1,85 @@ +%ifndef _DISK_ASM +%define _DISK_ASM + +disk_lba_chs: + push bp + mov bp, sp + + mov ax, word [bp - 2] ; LBA + + mov sp, bp + pop bp + ret + +disk_reset: + push bp + mov bp, sp + pusha + + mov ah, 00h ; reset disk + mov dl, [bp + 4] ; disk number + int 13h ; BIOS disk service + jnc .success + + push error_msg_disk_reset + call panic + +.success: + push msg_disk_reset + call puts + add sp, 2 + + popa + mov sp, bp + pop bp + ret + + +disk_read: + push bp + mov bp, sp + + push di + mov di, 3 ; retry counter +.readloop: + push ax + push bx + push cx + + mov ah, 02h ; BIOS - read disk sectors + int 13h ; BIOS disk service + + jnc .success + + push dx + call disk_reset + add sp, 2 + + pop cx + pop bx + pop ax + + dec di + jnz .readloop + + push error_msg_disk_read + call panic + add sp, 2 + +.success: + pop di + mov sp, bp + pop bp + ret + + +; data +drive0: dw 0 + +msg_disk_reset: db "Drive reset successful.", CR, LF, 0 +msg_disk_read: db "Sector read successful.", CR, LF, 0 + +error_msg_disk_reset: db "Drive reset failed!", CR, LF, 0 +error_msg_disk_read: db "Drive read failed!", CR, LF, 0 + +%endif ; _DISK_ASM diff --git a/kernel.asm b/kernel.asm new file mode 100644 index 0000000..f613460 --- /dev/null +++ b/kernel.asm @@ -0,0 +1,77 @@ +bits 16 + +jmp kmain + +%include "constants.asm" +%include "string.asm" +%include "disk.asm" +%include "console.asm" +%include "stdio.asm" + + +kmain: + cli ; disable interrupts + mov ax, cs ; get code segment (i.e. far jump address in bootloader) + mov ds, ax ; set data segment + mov es, ax ; set extra segment + mov ax, 06000h + mov ss, ax ; set stack segment + mov sp, 0ffffh ; set stack pointer (~64k) + sti ; enable interrupts + + mov [drive0], dx ; store bootloader's drive number + + ; reset general purpose registers + xor ax, ax + xor bx, bx + xor cx, cx + xor dx, dx + xor di, di + xor si, si + xor bp, bp + + + push banner + call puts + +.mainloop: + call kbd_read + call putc + + jmp .mainloop + + cli + jmp $ + + +panic: + ; Hang system with supplied error message + push bp + mov bp, sp + + push error_msg_panic ; i.e. 'PANIC:' + call puts + add sp, 2 + + push word [bp + 4] ; address of error string buffer + call puts ; print error + add sp, 2 + + cli ; disable interrupts + jmp $ ; hang (no return) + ; stack is dead + + +; data +kernel_address: dd 0 ; format DS:ADDR +banner: db "+========================+", CR, LF + db "| Welcome to MINOS 0.0.1 |", CR, LF + db "+========================+", CR, LF + db CR, LF, 0 + +; Error messages +error_msg_panic: db "PANIC: ", 0 + + +times 512 * 16 db 0 +dw 0xefbe diff --git a/keyboard.asm b/keyboard.asm new file mode 100644 index 0000000..444a6cf --- /dev/null +++ b/keyboard.asm @@ -0,0 +1,82 @@ +%ifndef _KEYBOARD_ASM +%define _KEYBOARD_ASM + +; REFERENCE(s): +; http://stanislavs.org/helppc/int_16-3.html + +kbd_read: + mov ah, 00h ; BIOS - read key (blocking) + int 16h ; BIOS keyboard service + mov [kbd_last_key], ax ; Record keypress + ; ah = scancode + ; al = ascii code + ret + + +kbd_read_async: + mov ah, 01h ; BIOS - read key (non-blocking) + int 16h ; BIOS keyboard service + mov [kbd_last_key], ax ; Record keypress + ; ah = scancode + ; al = ascii code + ret + + +kbd_status_shift: + mov ah, 02h ; BIOS - keyboard status + ; al = flags + ; + ; bit fields: + ; 7 = insert active + ; 6 = caps-lock active + ; 5 = num-lock active + ; 4 = scroll-lock active + ; 3 = ALT depresssed + ; 2 = CTRL depressed + ; 1 = left shift depressed + ; 0 = right shift depressed + mov [kbd_status_flags], byte al + int 16h ; BIOS keyboard service + + ret + + +kbd_set_rate: + push bp + mov bp, sp + + mov ah, 03h ; BIOS - keyboard service rate/delay + mov al, 05h ; CONTROL + ; 00 - set typematic rate + ; 01 - increase delay + ; 02 - decrease rate by 0.5 + ; 04 - disable typematic characters + ; 05 - set typematic rate & delay (used here) + + mov bh, byte [bp + 4] ; REPEAT (per second) + ; 00 - 30.0 01 - 26.7 02 - 24.0 03 - 21.8 + ; 04 - 20.0 05 - 18.5 06 - 17.1 07 - 16.0 + ; 08 - 15.0 09 - 13.3 0A - 12.0 0B - 10.9 + ; 0C - 10.0 0D - 9.2 0E - 8.6 0F - 8.0 + ; 10 - 7.5 11 - 6.7 12 - 6.0 13 - 5.5 + ; 14 - 5.0 15 - 4.6 16 - 4.3 17 - 4.0 + ; 18 - 3.7 19 - 3.3 1A - 3.0 1B - 2.7 + ; 1C - 2.5 1D - 2.3 1E - 2.1 1F - 2.0 + + mov bl, byte [bp + 6] ; DELAY + ; 00 - 250ms + ; 01 - 500ms + ; 02 - 750ms + ; 03 - 1000ms + + int 16h ; BIOS keyboard service + + mov sp, bp + pop bp + ret + + +; data +kbd_last_key: dw 0 +kbd_status_flags: db 0 +%endif diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..48ef0e7 --- /dev/null +++ b/make.bat @@ -0,0 +1,9 @@ +del system.img *.bin + +nasm -f bin -o boot.bin boot.asm +if %errorlevel% neq 0 exit /b %errorlevel% +nasm -f bin -o kernel.bin kernel.asm +if %errorlevel% neq 0 exit /b %errorlevel% + +type boot.bin kernel.bin > system.img +qemu-system-i386 system.img diff --git a/scancodes.asm b/scancodes.asm new file mode 100644 index 0000000..3a33183 --- /dev/null +++ b/scancodes.asm @@ -0,0 +1,9 @@ +%ifndef _SCANCODES_ASM +%define _SCANCODES_ASM + +SC_ARROW_UP equ 48h +SC_ARROW_DOWN equ 50h +SC_ARROW_LEFT equ 4Bh +SC_ARROW_RIGHT equ 4Dh + +%endif diff --git a/stdio.asm b/stdio.asm new file mode 100644 index 0000000..e6f7290 --- /dev/null +++ b/stdio.asm @@ -0,0 +1,28 @@ +%ifndef _STDIO_ASM +%define _STDIO_ASM + +%include "console.asm" + +puts: + ; Write string buffer at cursor position + push bp + mov bp, sp + pusha + + mov si, [bp + 4] ; address of string buffer + mov bx, 0000h ; + mov ah, 0eh ; BIOS - teletype + +.loop: + lodsb ; load byte at [si] into al + or al, 0 ; 0 | 0 = 0 (detect null terminator) + je .end + int 10h ; BIOS video service + jmp .loop +.end: + popa + mov sp, bp + pop bp + ret + +%endif diff --git a/string.asm b/string.asm new file mode 100644 index 0000000..4000afb --- /dev/null +++ b/string.asm @@ -0,0 +1,174 @@ +%ifndef _STRING_ASM +%define _STRING_ASM + +; stack direction +; --------------- +; + = external +; - = local +; +; ^ must be getting old D: +; + +; calling conventions +; push COUNT +; push SOURCE +; push DESTINATION +; call FUNCTION +; add sp, 6 (^ in the case of three WORDS) +; +; In C, for instance, this translate to: +; FUNCTION (DESTINATION, SOURCE, COUNT); + +memset: + ; Set memory with byte value + push bp ; setup stack frame + mov bp, sp ; ... + push si + push di + push cx + + mov di, [bp + 4] ; destination address + mov ax, [bp + 6] ; requested byte value + mov cx, [bp + 8] ; count + + cld ; will decrement address ES:DI + rep stosb ; while cx > 0 + ; store byte AL in ES:DI + + pop cx + pop di + pop si + mov sp, bp + pop bp + ret + + +memcpy: + push bp ; setup stack frame + mov bp, sp ; ... + + mov di, [bp + 4] ; destination buffer + mov si, [bp + 6] ; source buffer + mov cx, [bp + 8] ; count of characters to move + + cld + rep movsb + + mov sp, bp + pop bp + ret + + +strlen: + ; Determine length of null terminated string + ; NOTE: 64k limit imposed + push bp ; setup stack frame + mov bp, sp ; ... + push cx + push si + + xor cx, cx ; cx is counter + xor ax, ax ; ax is return value + mov si, [bp + 2] ; string address +.loop: + lodsb ; load byte from ES:SI into AL + cmp al, 0 ; zero? + je .return ; if so, we're done + inc cx ; if not, keep going + jc .crash ; if we roll over CX the carry flag will be set (that's bad) + + jmp .loop + +.crash: + stc ; force carry flag on failure + +.return: + clc + mov ax, cx + pop si + pop cx + mov sp, bp + pop bp + ret + + +strnchr: + ; Find first occurence of character in a string + push bp ; setup stack frame + mov bp, sp ; ... + push cx + push dx + push si + + xor ax, ax ; ax is return value + mov si, [bp + 4] ; string address + mov dx, [bp + 6] ; needle character + mov cx, [bp + 8] ; counter +.loop: + lodsb ; load byte at si + cmp al, dl ; same as needle? + je .return ; if true: return + + dec cx ; decrement counter + jne .loop ; counter zero? + +.return: + mov ax, cx ; return index of character + pop si + pop dx + pop cx + mov sp, bp + pop bp + ret + + +strncmp: + ; Determine difference between two strings + push bp ; setup stack frame + mov bp, sp ; ... + push bx ; save registers + push dx + push si + push di + + mov di, [bp + 4] ; string2 address + mov si, [bp + 6] ; string1 address + mov cx, [bp + 8] ; limit to compare + +.loop: + mov bx, [si] ; string1 + mov dx, [di] ; string2 + inc si ; next address, string1 + inc di ; next address, string2 + dec cx ; decrease byte counter + + cmp bx, dx ; compare bytes + jne .diff ; Just die if not-equal + + cmp cx, 0 ; If equal, check for null termination + jne .loop + +.diff: + cmp bx, dx + jg .s1_larger + jl .s1_smaller + je .return + +.s1_smaller: + mov ax, -1 + jmp .return + +.s1_larger: + mov ax, cx + +.return: + pop di ; cleanup stack + pop si + pop dx + pop bx + mov sp, bp + pop bp + ret + + +%endif ; _STRING_ASM |