; fn_tour.asm -- exercises the important INT 21h functions, one labelled line of output each. ; Run it under DOS (the harness uses DOSBox) with stdin redirected from IN.TXT. bits 16 org 0x100 ; ---- shrink our memory block: a .COM owns all conventional memory (AH=4Ah) ---- mov bx, 1000h ; keep 64 KB = 1000h paragraphs mov ah, 4Ah ; ES = our PSP segment already int 21h ; ---- AH=30h get DOS version ---- mov dx, l_ver call puts mov ah, 30h int 21h ; AL = major, AH = minor push ax xor ah, ah call putdec mov dl, '.' mov ah, 02h int 21h pop ax mov al, ah xor ah, ah call putdec call crlf ; ---- AH=2Ah / AH=2Ch date and time (values change every run, so we only range-check) ---- mov dx, l_date call puts mov ah, 2Ah ; CX = year, DH = month, DL = day int 21h cmp cx, 1980 jb .baddate cmp dh, 12 ja .baddate mov dx, s_ok jmp .dateout .baddate: mov dx, s_bad .dateout: call puts mov ah, 2Ch ; CH = hour, CL = minute, DH = second int 21h mov dx, s_ok2 cmp ch, 23 jbe .timeok mov dx, s_bad .timeok: call puts call crlf ; ---- AH=02h / AH=06h character output ---- mov dx, l_chr call puts mov dl, 'A' mov ah, 02h int 21h mov dl, 'B' mov ah, 06h ; direct console output (no ^C check) int 21h call crlf ; ---- AH=01h then AH=0Ah stdin: one echoed char, then a buffered line ---- mov dx, l_in call puts mov ah, 01h ; AL = char read (and echoed) int 21h mov [ch1], al mov dx, buf mov ah, 0Ah ; buf[0] = max, buf[1] = count read, buf[2..] = text int 21h call crlf mov dx, l_got call puts mov al, [ch1] call puthex8 ; the character AH=01h returned mov dx, l_cnt call puts xor ah, ah mov al, [buf+1] ; number of characters AH=0Ah stored (CR not counted) mov bx, ax call putdec mov byte [buf+2+bx], '$' mov dx, l_txt call puts mov dx, buf+2 call puts call crlf ; ---- AH=25h / AH=35h set and get an interrupt vector ---- mov dx, l_vec call puts mov ax, 2560h mov dx, dummy_isr int 21h mov ax, 3560h int 21h ; ES:BX = vector 60h mov dx, s_bad cmp bx, dummy_isr jne .vecout mov ax, es mov cx, cs cmp ax, cx jne .vecout mov dx, s_ok .vecout: call puts call crlf ; ---- AH=48h / AH=49h allocate and free memory; failure returns CF=1, AX=8, BX=largest ---- mov dx, l_mem call puts mov bx, 0100h ; 100h paragraphs = 4 KB mov ah, 48h int 21h jc .memfail mov es, ax ; ES = segment of the new block mov ah, 49h int 21h mov dx, s_ok jmp .memout .memfail: mov dx, s_bad .memout: call puts call crlf mov bx, 0FFFFh ; ask for 1 MB: must fail mov ah, 48h int 21h mov [err], ax ; save the results before printing clobbers them mov [big], bx mov byte [cfch], '0' jnc .memcf mov byte [cfch], '1' .memcf: mov dx, l_memfail call puts mov dl, [cfch] mov ah, 02h int 21h mov dx, l_ax call puts mov ax, [err] ; 0008h = insufficient memory call puthex16 mov dx, l_big call puts mov ax, [big] ; BX = largest block available, in paragraphs call puthex16 call crlf ; ---- file I/O: AH=3Ch create, 40h write, 3Eh close, 3Dh open, 3Fh read, 42h lseek ---- mov dx, l_file call puts mov dx, fname xor cx, cx ; normal attributes mov ah, 3Ch int 21h jc .fileerr mov bx, ax ; BX = handle mov dx, payload mov cx, payload_len mov ah, 40h int 21h mov ah, 3Eh int 21h mov dx, fname mov ax, 3D00h ; open read-only int 21h jc .fileerr mov bx, ax mov dx, rdbuf mov cx, 64 mov ah, 3Fh int 21h ; AX = bytes actually read call putdec ; bytes actually read mov dx, l_read call puts mov ax, 4202h ; LSEEK from end, offset 0 xor cx, cx xor dx, dx int 21h ; DX:AX = file size mov [fsize], ax mov dx, l_seek call puts mov ax, [fsize] call putdec mov ah, 3Eh int 21h call crlf jmp .fileok .fileerr: mov dx, s_bad call puts call crlf .fileok: ; ---- AH=56h rename, AH=4Eh findfirst (DTA defaults to PSP:0080h), AH=41h delete ---- mov dx, l_ff call puts mov dx, fname mov di, fname2 push ds pop es mov ah, 56h int 21h mov dx, fname2 xor cx, cx mov ah, 4Eh int 21h jc .ffbad mov dx, 80h+1Eh ; DTA+1Eh = ASCIZ name found mov si, dx ; copy it to a '$'-terminated buffer so AH=09h can print it mov di, ffname .cp: lodsb test al, al jz .cpend stosb jmp .cp .cpend: mov al, '$' stosb mov dx, ffname call puts mov dx, l_size call puts mov ax, [80h+1Ah] ; DTA+1Ah = file size (low word) call putdec jmp .ffend .ffbad: mov dx, s_bad call puts .ffend: call crlf mov dx, l_del call puts mov dx, fname2 mov ah, 41h ; delete int 21h mov dx, fname2 mov ax, 3D00h ; open it again: must fail int 21h mov [err], ax ; save AX and CF before printing clobbers them mov byte [cfch], '0' jnc .cfout mov byte [cfch], '1' .cfout: mov dl, [cfch] mov ah, 02h int 21h mov dx, l_ax call puts mov ax, [err] call puthex16 call crlf ; ---- AH=39h mkdir, AH=3Ah rmdir, AH=19h current drive, AH=47h current directory ---- mov dx, l_dir call puts mov dx, dname mov ah, 39h int 21h mov dx, dname mov ah, 3Ah int 21h mov dx, s_ok jnc .dirout mov dx, s_bad .dirout: call puts mov dx, l_drv call puts mov ah, 19h ; AL = 0 for A:, 1 for B:, 2 for C: int 21h xor ah, ah call putdec call crlf ; ---- AH=4Bh EXEC a child, AH=4Dh read its return code ---- mov dx, l_exec call puts mov ax, cs mov [pb+4], ax ; command-tail segment mov [pb+8], ax ; FCB1 segment mov [pb+12], ax ; FCB2 segment mov dx, child mov bx, pb mov ax, 4B00h ; load and execute int 21h jc .execbad mov ah, 4Dh ; AL = child's return code, AH = termination type int 21h push ax mov dx, l_rc call puts pop ax push ax xor ah, ah call putdec mov dx, l_tt call puts pop ax mov al, ah xor ah, ah call putdec call crlf jmp .done .execbad: mov dx, s_bad call puts call crlf .done: mov ax, 4C00h ; AH=4Ch: exit, return code 0 int 21h dummy_isr: iret l_ver db 'AH=30h DOS version : $' l_date db 'AH=2Ah/2Ch date+time ranges : $' l_chr db 'AH=02h + AH=06h : $' l_in db 'AH=01h + AH=0Ah (stdin) : echo -> $' l_got db ' AH=01h got 0x$' l_txt db ', text = $' l_cnt db ' ; AH=0Ah count = $' l_vec db 'AH=25h/35h vector 60h : $' l_mem db 'AH=48h/49h alloc+free 4 KB : $' l_memfail db 'AH=48h ask for 1 MB (fails) : CF=$' l_big db ' ; largest block (paragraphs) = $' l_file db 'AH=3Ch/40h/3Dh/3Fh/42h : $' l_read db ' bytes read ; $' l_ff db 'AH=56h + AH=4Eh findfirst : $' l_size db ' size = $' l_del db 'AH=41h delete, reopen fails : CF=$' l_seek db 'LSEEK(end) says size = $' l_ax db ' AX=$' l_dir db 'AH=39h/3Ah mkdir+rmdir : $' l_drv db ' ; AH=19h drive = $' l_exec db 'AH=4Bh exec child : $' l_rc db 'AH=4Dh return code = $' l_tt db ' type = $' s_ok db 'ok$' s_ok2 db ' ok$' s_bad db 'FAILED$' fname db 'T.TXT', 0 fname2 db 'U.TXT', 0 dname db 'SUBD', 0 child db 'HELLO21.COM', 0 payload db 'DOS file I/O' payload_len equ $ - payload ch1 db 0 err dw 0 fsize dw 0 big dw 0 cfch db 0 buf db 20, 0 times 22 db 0 ffname times 16 db 0 rdbuf times 64 db 0 ; EXEC parameter block: env seg (0 = inherit), cmd tail ptr, FCB1 ptr, FCB2 ptr pb dw 0, tail, 0, fcb1, 0, fcb2, 0 tail db 0, 0Dh fcb1 times 16 db 0 fcb2 times 16 db 0 %include "common.inc"