Last active
December 19, 2025 22:13
-
-
Save LightningStalker/32b86c751114bafd6fb38ec744f29f0f to your computer and use it in GitHub Desktop.
"wc" - wordcount in asm for DOS, 8086 +
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name wc | |
| title WC.COM--word count | |
| ;--- assemble: uasm -bin -Fo WC.COM WC.ASM | |
| display_char macro character | |
| mov dl, character | |
| mov ah, 02h | |
| int 21h | |
| endm | |
| display macro string | |
| mov dx, offset string | |
| mov ah, 09h | |
| int 21h | |
| endm | |
| open_handle macro path, access | |
| mov dx, offset path | |
| mov al, access | |
| mov ah, 3dh | |
| int 21h | |
| endm | |
| read_handle macro handle, buffer, bytes | |
| mov bx, handle | |
| mov dx, offset buffer | |
| mov cx, bytes | |
| mov ah, 3fh | |
| int 21h | |
| endm | |
| .model tiny | |
| DEBUG equ 1 | |
| CR equ 13 ; carriage return | |
| LF equ 10 ; linefeed char | |
| BUFSIZE equ 256 | |
| ARGCOUNT equ 80h ; DOS area args count | |
| FILENAME equ 82h ; ... argv[] | |
| MAXARGS equ 77h | |
| .code | |
| org 100h | |
| start: | |
| mov cx, MAXARGS ; zero terminate args | |
| mov di, FILENAME - 1 ; argv[] block | |
| mov al, CR ; we gotta look | |
| repne scasb | |
| dec di ; went past | |
| mov byte ptr [di], 0 ; maybe di - 1 is faster? | |
| open_handle FILENAME, 0 | |
| mov handle, ax | |
| jc errorOpen ; open error | |
| xor dx, dx ; inWord = 0 | |
| mov bp, dx ; chars = 0 | |
| push dx ; align stack | |
| readStream: | |
| read_handle handle, buffer, BUFSIZE | |
| jc errorRead ; error reading | |
| ; wc bp = lines, bx, mem = words, mem = chars | |
| pop dx ; inWord = stack | |
| mov si, offset buffer ; load at beginning | |
| push ax ; save bytes read | |
| mov cx, ax ; counter = nbytes read | |
| add [charcount], ax ; add to 32-bit char count var | |
| adc [charcount + 2], 0 ; ... | |
| mov ah, ' ' ; white space we look for | |
| xor bx, bx ; words = 0 | |
| countWords: | |
| dec cx ; next byte | |
| js EOB ; could be the end? | |
| lodsb ; load a byte from buffer to al | |
| cmp al, ah ; is a control char | |
| jbe whitespace ; or whitespace? | |
| test dl, dl ; already inside the word? | |
| jnz countWords ; still inside | |
| not dl ; whitespace/word boundary | |
| inc bx ; words += 1 | |
| jmp countWords ; go get the next | |
| whitespace: | |
| jne notSpace ; not 20h | |
| xor dl, dl ; end of word | |
| jmp countWords ; ^next char^ | |
| notSpace: ; other whitespace or control ch | |
| xor dl, dl ; dl = 0 = still not the word | |
| cmp al, LF ; end | |
| je EOL ; of line? | |
| jmp countWords ; next byte | |
| EOL: ; end the line | |
| inc bp ; add to count | |
| jmp countWords | |
| EOB: ; end the buffer | |
| add [wordcount], bx ; add to word count | |
| adc [wordcount + 2], 0 ; ... 32-bit var | |
| pop ax ; get bytes read from above | |
| cmp ax, BUFSIZE ; smaller than buffer? | |
| push dx ; stack = inWord | |
| jae readStream | |
| IF DEBUG | |
| nop | |
| ENDIF | |
| ; displayCounts | |
| xor dx, dx ; dx = 0 | |
| mov ax, bp ; bp = lines | |
| call optdecdw ; display | |
| display_char ' ' ; space between | |
| mov ax, [wordcount] ; mem = words | |
| mov dx, [wordcount + 2] ; ... | |
| call optdecdw ; display | |
| display_char ' ' ; space again | |
| mov ax, [charcount] ; mem = chars | |
| mov dx, [charcount + 2] ; ... | |
| call optdecdw ; disp | |
| display crlf | |
| ; goodbye | |
| mov ax, 4c00h | |
| int 21h | |
| errorOpen: | |
| display errOpenMsg | |
| display crlf | |
| jmp badbye | |
| errorRead: | |
| display errReadMsg | |
| display crlf | |
| badbye: | |
| mov ax, 4c01h | |
| int 21h | |
| IF DEBUG | |
| nop ; debugs for the remove | |
| nop | |
| ENDIF | |
| ;--- 8086asm library code begin here | |
| ; written by hyan23 | |
| ; 2016.10.24 | |
| ; output the dword as the unsigned decimal string | |
| ; in: dx:ax | |
| ; ret: none | |
| optdecdw: ; dword | |
| push ax | |
| push bx | |
| push cx | |
| push dx | |
| xor cx, cx ; count | |
| ; - Project Crew - patch to handle in case of the zero | |
| mov bx, cx ; 0 | |
| or bx, dx ; testing | |
| or bx, ax ; ... | |
| jnz optddw1 ; dx:ax > 0, decompose number | |
| push cx ; 0 found, align stack | |
| inc cx ; 1 char | |
| jmp optddw2 ; print him | |
| ; - end patch | |
| optddw0: ; /= 10 | |
| cmp dx, ax | |
| jne optddw1 | |
| test ax, ax | |
| jz optddw2 | |
| optddw1: | |
| push cx | |
| mov cx, 10 | |
| call div32 ; arith.inc | |
| pop cx | |
| add cx, 1 | |
| push bx | |
| jmp optddw0 | |
| optddw2: ; print digit | |
| pop ax | |
| add ax, 30h | |
| mov dl, al | |
| mov ah, 02h | |
| int 21h | |
| loop optddw2 | |
| pop dx | |
| pop cx | |
| pop bx | |
| pop ax | |
| ret | |
| IF DEBUG | |
| nop | |
| nop | |
| ENDIF | |
| ; 00:dx / cx = q1 ... r1 | |
| ; r1:ax / cx = q2 ... r2 | |
| ; q1:q2 ... r2 | |
| ; in: dx:ax / cx | |
| ; ret: dx:ax ... bx | |
| div32: ; 32-bit division | |
| mov bx, dx | |
| ; bx:ax / cx | |
| ; bx:ax ... dx | |
| xchg ax, bx | |
| xor dx, dx | |
| div cx | |
| xchg ax, bx | |
| div cx | |
| push dx | |
| mov dx, bx | |
| pop bx | |
| ret | |
| ; ====================================== vars: | |
| wordcount dw 0, 0 ; word count | |
| charcount dw 0, 0 ; characters count | |
| ; strings: | |
| crlf db 13, 10, '$' ; DOS line end | |
| buffer db [BUFSIZE + 1] dup (?) ; you're gonna be our string | |
| handle dw ? ; open handle | |
| errOpenMsg db "File open error$" ; error msgs | |
| errReadMsg db "File read error$" ; ... | |
| end start |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment