FreeDOS/test/ldosboot/testpl.asm

868 lines
16 KiB
NASM

%if 0
Loader test payload
by C. Masloch, 2017
Usage of the works is permitted provided that this
instrument is retained with the works, so that any entity
that uses the works is notified of this instrument.
DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
%endif
%include "lmacros2.mac"
numdef LARGE, 1
numdef PADDING, 0
struc BS
bsJump: resb 3
bsOEM: resb 8
bsBPB:
endstruc
struc EBPB ; BPB sec
bpbBytesPerSector: resw 1 ; offset 00h 0Bh
bpbSectorsPerCluster: resb 1 ; offset 02h 0Dh
bpbReservedSectors: resw 1 ; offset 03h 0Eh
bpbNumFATs: resb 1 ; offset 05h 10h
bpbNumRootDirEnts: resw 1 ; offset 06h 11h -- 0 for FAT32
bpbTotalSectors: resw 1 ; offset 08h 13h
bpbMediaID: resb 1 ; offset 0Ah 15h
bpbSectorsPerFAT: resw 1 ; offset 0Bh 16h -- 0 for FAT32
bpbCHSSectors: resw 1 ; offset 0Dh 18h
bpbCHSHeads: resw 1 ; offset 0Fh 1Ah
bpbHiddenSectors: resd 1 ; offset 11h 1Ch
bpbTotalSectorsLarge: resd 1 ; offset 15h 20h
bpbNew: ; offset 19h 24h
ebpbSectorsPerFATLarge: resd 1 ; offset 19h 24h
ebpbFSFlags: resw 1 ; offset 1Dh 28h
ebpbFSVersion: resw 1 ; offset 1Fh 2Ah
ebpbRootCluster: resd 1 ; offset 21h 2Ch
ebpbFSINFOSector: resw 1 ; offset 25h 30h
ebpbBackupSector: resw 1 ; offset 27h 32h
ebpbReserved: resb 12 ; offset 29h 34h
ebpbNew: ; offset 35h 40h
endstruc
struc BPBN ; ofs B16 S16 B32 S32
bpbnBootUnit: resb 1 ; 00h 19h 24h 35h 40h
resb 1 ; 01h 1Ah 25h 36h 41h
bpbnExtBPBSignature: resb 1 ; 02h 1Bh 26h 37h 42h -- 29h for valid BPBN
bpbnSerialNumber: resd 1 ; 03h 1Ch 27h 38h 43h
bpbnVolumeLabel: resb 11 ; 07h 20h 2Bh 3Ch 47h
bpbnFilesystemID: resb 8 ; 12h 2Bh 36h 47h 52h
endstruc ; 1Ah 33h 3Eh 4Fh 5Ah
struc LOADSTACKVARS, -10h
lsvFirstCluster: resd 1
lsvFATSector: resd 1
lsvFATSeg: resw 1
lsvLoadSeg: resw 1
lsvDataStart: resd 1
endstruc
lsvclSignature equ "CL"
lsvclBufferLength equ 256
struc LOADDATA, LOADSTACKVARS - 10h
ldMemoryTop: resw 1
ldLoadTop: resw 1
ldSectorSeg: resw 1
ldFATType: resb 1
ldHasLBA: resb 1
ldClusterSize: resw 1
ldParaPerSector:resw 1
ldLoadingSeg: ; word
lsvCommandLine: ; word
.start: equ $ - lsvclBufferLength
.signature: resw 1
ldLoadUntilSeg: ; word
lsvExtra: ; word
.partition: resb 1 ; byte
.flags: resb 1 ; byte
endstruc
lsvefNoDataStart equ 1
lsvefPartitionNumber equ 2
struc LOADCMDLINE, LOADDATA - lsvclBufferLength
ldCommandLine:
.start: resb lsvclBufferLength
endstruc
%ifndef _MAP
%elifempty _MAP
%else ; defined non-empty, str or non-str
[map all _MAP]
%endif
cpu 8086
org 0
payload:
; The device header is of a fixed format.
; For our purposes, the 4-byte code for
; each the strategy entry and the
; interrupt entry is part of this format.
; (DOS may read the attributes or entrypoint
; offsets before calling either, so the
; inicomp stage needs to recreate in its
; entrypoints part exactly what we have here.)
device_header:
.next:
fill 2, -1, jmp strict short j_zero_entrypoint
dw -1
.attributes:
dw 8000h ; character device
.strategy:
dw .strategy_entry ; -> strategy entry
.interrupt:
dw .interrupt_entry ; -> interrupt entry
.name:
fill 8, 32, db "TESTPL$$" ; character device name
.strategy_entry:
fill 4, 90h, jmp device_entrypoint
.interrupt_entry:
fill 4, 90h, retf
j_zero_entrypoint:
jmp zero_entrypoint
nop
align 32, nop
kernel_entrypoint:
; cs:ip = load seg : 32 here
%if ($ - $$) != 32
%error Wrong kernel mode entrypoint
%endif
; S0 +28
pushf ; +26
push ax ; +24
push cs ; +22
call .push_ip
.push_ip:
pop ax
sub ax, .push_ip - kernel_entrypoint
push ax ; +20 IP
common_entrypoint:
push es ; +18 ES
push bx ; +16 BX
cmp word [cs:signature], 2638h
je sig1_valid
jmp sig_invalid
align 64, nop
dos_exe_entrypoint:
; cs:ip = PSP : 256 + 64 here
;
; Code must be position independent enough.
%if ($ - $$) != 64
%error Wrong EXE mode entrypoint
%endif
; S0 +28
pushf ; +26
push ax ; +24
push cs ; +22
call .push_ip
.push_ip:
pop ax
sub ax, .push_ip - dos_exe_entrypoint
push ax ; +20 IP
dos_com_entrypoint:
mov byte [cs:100h + loadmode], 1
mov ax, cs
add ax, 10h ; simulate kernel loading
push ax
jump_common_entrypoint:
mov ax, common_entrypoint
push ax
retf ; jump to cs + 10h : common_entrypoint
zero_entrypoint:
; S0 +28
pushf ; +26
push ax ; +24
push cs ; +22
call .push_ip
.push_ip:
pop ax
sub ax, .push_ip
push ax ; +20 IP
cmp word [cs:0], 20CDh
jne @F
cmp ax, 100h
je dos_com_entrypoint
@@:
push cx
mov cl, 4
shr ax, cl
mov cx, cs
add ax, cx
pop cx
push ax
jmp jump_common_entrypoint
device_entrypoint:
pushf
push ax
push cs
call .push_ip
.push_ip:
pop ax
sub ax, .push_ip - device_header.strategy_entry
push ax
mov byte [cs:loadmode], 2
mov word [cs:dev_sp], sp
mov word [cs:dev_exit], dev_exit.1
jmp common_entrypoint
sig_invalid:
call error
db "Signature invalid.", 0
sig1_valid:
push ds
push bx
mov bx, cs
add bx, (signature2 -$$+0) >> 4
mov ds, bx
cmp word [(signature2 -$$+0) & 0Fh], 2638h
pop bx
pop ds
jne sig_invalid
jmp sig_valid
msg:
.error: db "Test payload error: ", 0
.test: db "Test payload loaded.", 13, 10, 0
.psp_and_size_before: asciz "PSP at "
.psp_and_size_between: asciz "h, size of memory block is "
.psp_and_size_after: asciz "h paragraphs.",13,10
.cmdline.kern.none: asciz "No kernel command line given!",13,10
.cmdline_before.kern: asciz "Kernel command line = ",'"'
.cmdline_before.app: asciz "Application command line = ",'"'
.cmdline_before.device: asciz "Device command line = ",'"'
.cmdline_after: asciz '"',13,10
align 4
.foundname:
times 8+1+3+1 db 0
; buffer for base name (8) + dot (1) + ext (3) + NUL (1)
align 2
.foundname_none:
asciz "(None)"
.foundname_none_size: equ $ - .foundname_none
align 2
.names:
dw .name_first, 0
dw .name_second, 0
dw .name_third, 0
dw .name_fourth, 0
dw 0
.name_first: asciz "1st name"
.name_second: asciz "2nd name"
.name_third: asciz "3rd name"
.name_fourth: asciz "4th name"
.name_before: asciz ": "
.name_quote: asciz '"'
.name_after: asciz 13,10
align 4
dev_request_header:
dd 0
loadmode: dw 0 ; 0 = loaded as boot payload,
; 1 = loaded as DOS application,
; 2 = loaded as DOS device driver
dev_sp: dw 0
dev_exit: dw 0
.1:
mov sp, word [cs:dev_sp]
jmp .common_1
.2: mov sp, word [cs:dev_sp]
jmp .common_2
.common_2:
pop ax ; ss
pop ds
pop ax ; sp
pop bp
pop di
pop si
pop dx
pop cx
.common_1:
pop bx ; bx
pop es ; es
pop ax ; (IP)
pop ax ; (CS)
pop ax ; ax
mov word [es:bx + 3], 8103h
; error, done, error code: unknown command
popf ; flags
retf ; far return to DOS
error:
push cs
pop ds
mov si, msg.error
call disp_msg
pop si
call disp_msg
test byte [cs:loadmode], 2
jz .dos_or_bios
jmp near [cs:dev_exit]
.dos_or_bios:
test byte [cs:loadmode], 1
jz .bios
mov ax, 4C01h
int 21h
.bios:
xor ax, ax
int 16h
int 19h
disp_msg_asciz:
push ds
push si
push ax
push cs
pop ds
mov si, dx
call disp_msg
pop ax
pop si
pop ds
retn
disp_msg:
@@:
lodsb
test al, al
jz @F
call disp_al
jmp short @B
disp_al:
push ax
push bx
push dx
push bp
test byte [cs:loadmode], 1 | 2
jz .bios
mov dl, al
mov ah, 02h
int 21h
jmp .common
.bios:
mov ah, 0Eh
mov bx, 7
int 10h
.common:
pop bp
pop dx
pop bx
pop ax
@@:
retn
disp_msg_length:
push cx
jcxz .ret
.loop:
lodsb
call disp_al
loop .loop
.ret:
pop cx
retn
sig_valid:
; S0 +28
; pushf ; +26
; push ax ; +24
; push cs ; +22
; call .push_ip
;.push_ip:
; pop ax
; sub ax, .push_ip
; push ax ; +20 IP
; push es ; +18
; push bx ; +16
sti
cld
push cx ; +14
push dx ; +12
push si ; +10
push di ; +8
push bp ; +6
mov ax, sp
add ax, 22
push ax ; +4 SP
push ds ; +2
push ss ; +0
test byte [cs:loadmode], 2
jz @F
mov word [cs:dev_sp], sp
mov word [cs:dev_exit], dev_exit.2
mov word [cs:dev_request_header], bx
mov word [cs:dev_request_header + 2], es
cmp byte [es:bx + 2], 0 ; command code 0 (init) ?
jne dev_exit.2 ; else immediately return -->
mov byte [es:bx + 13], 0 ; number of units = 0
mov word [es:bx + 14 + 2], cs
and word [es:bx + 14], 0 ; -> after end of memory to allocate
or word [cs:device_header.next], -1
; fill in offset of device header link
@@:
mov si, sp
push ss
pop ds
mov di, table
push cs
pop es
loop_table:
mov bx, [es:di + 0]
mov al, 32
call disp_al
mov ax, [es:di + 2]
call disp_al
xchg al, ah
call disp_al
cmp bx, -1
je @F
mov al, '='
call disp_al
mov ax, [si + bx]
call disp_ax_hex
@@:
add di, 4
cmp di, table.end
jb loop_table
listnames:
test byte [cs:loadmode], 1 | 2
jnz .skip
mov bx, msg.names
push ss
pop ds
lea si, [bp + bsBPB + ebpbNew + BPBN_size]
mov cx, (512 - (bsBPB + ebpbNew + BPBN_size)) - 2
; -2 = AA55h sig
cmp word [bp + bsBPB + bpbSectorsPerFAT], 0
je @F
mov cx, (512 + (ebpbNew - bpbNew) - (bsBPB + ebpbNew + BPBN_size)) - 2
@@:
.nextname:
call findname
lahf
mov dx, [cs:bx]
call disp_msg_asciz
mov dx, msg.name_before
call disp_msg_asciz
sahf
jc @F ; skip quote if no name -->
mov dx, msg.name_quote
call disp_msg_asciz
@@:
mov dx, msg.foundname
call disp_msg_asciz
sahf
jc @F ; skip quote if no name -->
mov dx, msg.name_quote
call disp_msg_asciz
@@:
mov dx, msg.name_after
call disp_msg_asciz
sahf
mov ax, 0
jc @F ; set to zero if no name -->
lea ax, [si - 11] ; -> name in buffer
@@:
mov word [cs:bx + 2], ax ; -> name in buffer, or 0
add bx, 4
cmp word [cs:bx], 0
jne .nextname
.skip:
push cs
pop ds
mov si, msg.test
call disp_msg
test byte [cs:loadmode], 1
jz .skip_psp_dos
mov si, msg.psp_and_size_before
call disp_msg
mov ah, 51h
int 21h
mov ax, bx
call disp_ax_hex
mov si, msg.psp_and_size_between
call disp_msg
dec bx
mov es, bx
mov ax, word [es:3]
call disp_ax_hex
mov si, msg.psp_and_size_after
call disp_msg
mov si, msg.cmdline_before.app
call disp_msg
mov ah, 51h
int 21h
mov ds, bx
mov si, 81h
xor cx, cx
mov cl, byte [si - 1]
call disp_msg_length
push cs
pop ds
mov si, msg.cmdline_after
call disp_msg
jmp .after_cmdline
.skip_psp_dos:
test byte [cs:loadmode], 2
jz .skip_device
mov si, msg.cmdline_before.device
call disp_msg
les bx, [cs:dev_request_header]
les di, [es:bx + 18]
push es
pop ds
mov si, di
; Writing MS-DOS Device Drivers, second edition, page 349
; specifies the following as to the command line termination:
; "Note that the DEVICE= command string is terminated by an
; Ah when there are no arguments. When there are arguments,
; the string is terminated with the following sequence:
; 0h, Dh, Ah."
db __TEST_IMM8
@@:
inc di
cmp byte [di], 0
je @F
cmp byte [di], 13
je @F
cmp byte [di], 10
jne @B
@@: ; di -> at terminator
sub di, si
mov cx, di
call disp_msg_length
push cs
pop ds
mov si, msg.cmdline_after
call disp_msg
jmp .after_cmdline
.skip_device:
mov si, msg.cmdline.kern.none
cmp word [bp + ldCommandLine], 0FF00h
je .no_kernel_cmdline
mov si, msg.cmdline_before.kern
call disp_msg
push ss
pop ds
lea si, [bp + ldCommandLine]
call disp_msg
push cs
pop ds
mov si, msg.cmdline_after
.no_kernel_cmdline:
call disp_msg
.after_cmdline:
int3
test byte [cs:loadmode], 2
jz .dos_or_bios
jmp near [cs:dev_exit]
.dos_or_bios:
test byte [cs:loadmode], 1
jz .bios
mov ax, 4C00h
int 21h
.bios:
xor ax, ax
int 16h
int 19h
disp_ax_hex: ; ax
xchg al,ah
call disp_al_hex ; display former ah
xchg al,ah ; and fall trough for al
disp_al_hex: ; al
push cx
mov cl,4
ror al,cl
call disp_al_lownibble_hex ; display former high-nibble
rol al,cl
pop cx
; and fall trough for low-nibble
disp_al_lownibble_hex:
push ax ; save ax for call return
and al,00001111b ; high nibble must be zero
add al,'0' ; if number is 0-9, now it's the correct character
cmp al,'9'
jna .decimalnum ; if we get decimal number with this, ok -->
add al,7 ; otherwise, add 7 and we are inside our alphabet
.decimalnum:
call disp_al
pop ax
retn
; INP: ds:si -> first byte to check for name
; cx = number of bytes left
; OUT: (8+1+3+1)bytes[es:msg.foundname] = found name,
; converted to 8.3 ASCIZ format,
; "(None)" if none
; CY if no filename found,
; si = INP:si + INP:cx
; cx = 0
; NC if filename found,
; ds:si -> byte behind the name, thus ds:(si-11)-> name
; cx = number of bytes left
; CHG: di, ax
findname:
.:
cmp cx, 11 ; enough for another name ?
jb .none ; no -->
; (cx == 0 jumps here too)
.check:
push cx
push si
mov cx, 11
lodsb
mov ah, al ; check for same char in all 11 places
cmp al, 32 ; first character must not be blank
je .check_fail ; if it is -->
; cmp al, 5 ; first character may be 05h to indicate 0E5h
; je .check_pass
db __TEST_IMM8 ; (skip lodsb)
.check_loop_same:
lodsb
cmp ah, al
jne .check_loop_differs
call .check_character
jc .check_fail
loop .check_loop_same
; if we arrive here, all characters (while valid) are the
; same character repeated 11 times. we disallow this in case
; that the padding character is an allowed one (eg '&' 26h).
.check_fail:
pop si
pop cx
dec cx ; lessen the counter
inc si ; -> next position to check
jmp .
.check_character:
cmp al, 32
jb .check_character_fail
cmp al, 127
; je .check_character_fail
jae .check_character_fail
; note: with all characters >= 128 allowed,
; we get false positives in our sectors.
cmp al, '.'
je .check_character_fail
cmp al, '/'
je .check_character_fail
cmp al, '\'
je .check_character_fail
cmp al, 'a'
jb .check_character_pass
cmp al, 'z'
ja .check_character_pass
.check_character_fail:
stc
retn
.check_character_pass:
clc
retn
.check_loop:
lodsb
.check_loop_differs:
call .check_character
jc .check_fail
.check_pass:
loop .check_loop
pop ax ; (discard si)
sub si, 11 ; -> at name
call convert_name_to_asciz
; si -> behind name
pop cx
sub cx, 11 ; lessen the counter
clc
retn
.none:
add si, cx
mov di, msg.foundname
push si
push ds
push cs
pop ds
mov si, msg.foundname_none
mov cx, (msg.foundname_none_size + 1) >> 1
rep movsw
pop ds
pop si
xor cx, cx
stc
retn
; INP: ds:si -> 11-byte blank-padded name
; es:msg.foundname -> (8+1+3+1)-byte buffer
; OUT: ds:si -> behind 11-byte blank-padded name
; es:msg.foundname filled
; CHG: cx, di, ax
convert_name_to_asciz:
mov di, msg.foundname
mov cx, 8
rep movsb ; copy over base name, si -> extension
cmp byte [es:di - 8], 05h ; is it 05h ?
jne @F ; no -->
mov byte [es:di - 8], 0E5h ; yes, convert to 0E5h
@@:
db __TEST_IMM8 ; (skip dec)
@@:
dec di ; decrement -> at previous trailing blank
cmp byte [es:di - 1], 32 ; trailing blank ?
je @B ; yes -->
mov al, '.'
stosb ; store dot (if needed)
mov cl, 3
rep movsb ; copy over extension, si -> behind name
db __TEST_IMM8 ; (skip dec)
@@:
dec di ; decrement -> at previous trailing blank
cmp byte [es:di - 1], 32 ; trailing blank ?
je @B ; yes -->
cmp byte [es:di - 1], '.' ; trailing dot ? (only occurs if all-blank ext)
jne @F ; no -->
dec di ; -> at the dot
@@:
mov al, 0
stosb ; store filename terminator
retn
align 4
table:
dw +0, "SS"
dw +6, "BP"
dw +4, "SP"
dw +22, "CS"
dw +20, "IP"
dw +26, "FL"
db -1, -1, 13,10
dw +2, "DS"
dw +10, "SI"
dw +18, "ES"
dw +8, "DI"
db -1, -1, 13,10
dw +24, "AX"
dw +16, "BX"
dw +14, "CX"
dw +12, "DX"
db -1, -1, 13,10
dw +28, "S0"
dw +30, "S1"
dw +32, "S2"
dw +34, "S3"
dw +36, "S4"
dw +38, "S5"
dw +40, "S6"
dw +42, "S7"
db -1, -1, 13,10
dw +44, "S8"
dw +46, "S9"
dw +48, "SA"
dw +50, "SB"
dw +52, "SC"
dw +54, "SD"
dw +56, "SE"
dw +58, "SF"
db -1, -1, 13,10
.end:
signature:
dw 2638h
align 16, db 0
%if _LARGE
times 64 * 1024 db 0
%endif
signature2:
dw 2638h
%if _PADDING
times _PADDING - ($ - $$) db 0
%endif