; > asmsyntax=nasm psp_size equ 100h buf_size equ 100h clt_off equ 80h struc clt .len resb 1 .data resb 127 endstruc struc far_addr .off: resw 1 .seg: resw 1 endstruc cpu 8086 org psp_size section .code ; set up stack and back up stack segment cli mov sp, stack_off mov bp, sp mov [stack_seg], ss sti ; quit if DOS version is less than 2.x mov ah, 30h int 21h cmp al, 2 jl int20h ; read interrupt 10h routine mov ax, 3510h int 21h mov [prev_int10h_routine + far_addr.off], bx mov [prev_int10h_routine + far_addr.seg], es ; install new interrupt 10h routine mov dx, int10h_routine mov ah, 25h ; AL still 10h int 21h ; restore es for exec call mov ax, ds mov es, ax ; complete the exec params (AX is still DS) mov [exec_params.clt + far_addr.seg], ax mov [exec_params.fcb1 + far_addr.seg], ax mov [exec_params.fcb2 + far_addr.seg], ax ; resize memory block mov bx, (psp_size + code_size + data_size + bss_size + 15) / 16 mov ah, 4ah int 21h jc error ; open setup file using handle (read-only) mov ax, 3d00h mov dx, setup_file int 21h jc error ; read setup file content mov dx, buf mov cx, 256 mov bx, ax mov ah, 3fh int 21h jc error ; closes file mov ah, 3eh ; handle still in BX int 21h jc error ; ignores first line of setup file mov di, buf mov cx, buf_size mov al, 0ah repne scasb ; copies to prog_name until it finds a space (20h) mov si, prog_name xchg di, si copy_prog_name: lodsb cmp al, 20h je copy_prog_name_done stosb jmp copy_prog_name copy_prog_name_done: ; sets the trailing 0 xor al, al stosb ; copies to clt until it finds a newline (0dh) mov di, clt_off + clt.data xor ch, ch copy_clt: lodsb stosb ; must write the newline in the clt inc ch cmp al, 0dh je copy_clt_done jmp copy_clt copy_clt_done: mov [clt_off + clt.len], ch ; invokes the program specified in the setup file mov dx, prog_name mov bx, exec_params mov ax, 4b00h int 21h ; restore stack pointer and segment cli mov sp, stack_off mov ss, [stack_seg] sti ; restore old interrupt 10h routine mov dx, [prev_int10h_routine + far_addr.off] mov ds, [prev_int10h_routine + far_addr.seg] mov ax, 2510h int 21h ; gets subprocess return code mov ah, 4dh int 21h ; exits to DOS mov ah, 4ch ; AL holds the return value of the subprocess int 21h error: ; exits to DOS with an error code of 1 mov ax, 4c01h int 21h int20h: ; exits to DOS (only if the DOS version is 1.x) int 20h int10h_routine: ; only intercept calls to get video state cmp ah, 0fh jne .done ; save used registers (to the game stack!) push bp push ds ; the LOAD.EXE executable puts its own CS in the stack mov bp, sp mov ax, [bp + 6] ; changes a byte in the DS segment so that validation will be skipped sub ax, 1ea2h mov ds, ax inc byte [2ach] ; restore registers mov ah, 0fh pop ds pop bp .done: ; resume regular interrupt service routine jmp far [cs:prev_int10h_routine] code_size equ $ - $$ section .data align=1 setup_file db 'SETUP.DAT', 0 exec_params: .envseg dw 0 .clt istruc far_addr at far_addr.off, dw clt_off at far_addr.seg, dw 0 iend .fcb1 istruc far_addr at far_addr.off, dw 5ch at far_addr.seg, dw 0 iend .fcb2 istruc far_addr at far_addr.off, dw 6ch at far_addr.seg, dw 0 iend data_size equ $ - $$ section .bss align=1 stack_seg resw 1 prev_int10h_routine resb far_addr_size prog_name resb 13 buf resb buf_size stack_off: ; points to the end of the buffer bss_size equ $ - $$