From 1dd05ebe61c95985b16d170bf2d3f081a02dfd7d Mon Sep 17 00:00:00 2001 From: Joseph Hunkeler Date: Tue, 28 Nov 2017 16:49:24 -0500 Subject: Initial commit --- .gitignore | 2 + LICENSE.txt | 22 +++++ Makefile | 19 ++++ ascii.asm | 38 ++++++++ boot.asm | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++ console.asm | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ constants.asm | 10 +++ disk.asm | 85 ++++++++++++++++++ kernel.asm | 77 ++++++++++++++++ keyboard.asm | 82 +++++++++++++++++ make.bat | 9 ++ scancodes.asm | 9 ++ stdio.asm | 28 ++++++ string.asm | 174 +++++++++++++++++++++++++++++++++++ 14 files changed, 1085 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 ascii.asm create mode 100644 boot.asm create mode 100644 console.asm create mode 100644 constants.asm create mode 100644 disk.asm create mode 100644 kernel.asm create mode 100644 keyboard.asm create mode 100644 make.bat create mode 100644 scancodes.asm create mode 100644 stdio.asm create mode 100644 string.asm 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 -- cgit