Skip to content

Instantly share code, notes, and snippets.

@thentenaar
Last active August 31, 2021 09:41
Show Gist options
  • Save thentenaar/7050651 to your computer and use it in GitHub Desktop.
Save thentenaar/7050651 to your computer and use it in GitHub Desktop.
DOS 6+ Fix for PC/GEOS 1.x (and an example of an extremely simply file patcher.)
;
; DOS 6+ Fix for PC/GEOS 1.x
; (and an example of an extremely simply file patcher)
;
; Copyright (C) 2013 Tim Hentenaar, http://hentenaar.com
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice, this
; list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright notice,
; this list of conditions and the following disclaimer in the documentation
; and/or other materials provided with the distribution.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
; ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;
; Assemble with TASM:
; tasm geos6fix.asm
; tlink geos6fix.obj
;
; For an explanation of what this patch does, see:
; http://hentenaar.com/geos1-and-dos6
;
.model tiny
.stack 16
.data
target db 'KERNEL.EXE',0
patch_count db 11
;
; The format of these patches is as follows:
;
; Offset Length Description
; -----------------------------------------------------------
; 0 4 File offset to patch
; 4 1 Number of bytes to patch
; 5 1 Verify? 1=Yes, 0=No
; 6 * Existing Bytes (for verification) or Patch
; * * Patch
;
patches:
; Append the dos_version_check subroutine -> seg004:1CDB
dd 11C0Bh
db 38
db 0 ; Don't verify (since we write past the end of the file.)
db 50h ; push ax
db 0A1h, 0ABh, 05h ; mov ax, word ptr [dos_version]
db 3Ch, 06h ; cmp al, 6
db 7Ch, 06h ; jl $+8
db 0C7h, 06h, 0ABh, 05h, 05h, 00h ; mov word ptr [dos_version], 5
db 58h ; pop ax
db 0E9h, 44h, 0F0h ; jmp init_drive_module
db 0C3h ; ret
dw 9090h ; 2x nop (padding)
dw 0 ; 1CF0
dw 0 ; 1CF2
db 13 dup(0) ; Segment padding bytes (til end of paragraph @ 1CFF)
; seg004:0220 call init_drive_module -> call dos_version_check
dd 10151h
db 2
db 1
db 0Eh, 0Bh
db 0B8h, 1Ah
; seg004:048E mov cs:tmp_address.offset, si (Correct offset to 1CF0)
dd 103C1h
db 2
db 1
db 0E0h, 1Ch
db 0F0h, 1Ch
; seg004:0493 mov cs:temp_address.segment, ds (Correct offset to 1CF2)
dd 103C6h
db 2
db 1
db 0E2h, 1Ch
db 0F2h, 1Ch
; seg004:04C3 lds si, dword ptr cs:temp_address (Correct offset to 1CF0)
dd 103F6h
db 2
db 1
db 0E0h, 1Ch
db 0F0h, 1Ch
; seg004:0543 lds si, dword ptr cs:temp_address (Correct offset to 1CF0)
dd 10476h
db 2
db 1
db 0E0h, 1Ch
db 0F0h, 1Ch
; seg004:0913 mov dx, offset geos_ini_buffer (Correct offset to 1CF0)
dd 10844h
db 2
db 1
db 0E0h, 1Ch
db 0F0h, 1Ch
; seg004:0928 mov byte ptr cs:geos_ini_buffer[bx], 1Ah (Correct offset to 1CF0)
dd 1085Bh
db 2
db 1
db 0E0h, 1Ch
db 0F0h, 1Ch
; seg004:099D mov ax, offset geos_ini_buffer (Correct offset to 1CF0)
dd 108CEh
db 2
db 1
db 0E0h, 1Ch
db 0F0h, 1Ch
; Fix the routine that relocates seg004 so that it includes
; the new subroutine. This size should be paragraph aligned.
;
; seg004:0046 mov cx, SEG004_SIZE -> mov cx, SEG004_SIZE + 16
dd 0FF67h
db 2
db 1
db 0E0h, 1Ch
db 0F0h, 1Ch
; Adjust the number of bytes at the end of the last page in the
; EXE header to account for the new subroutine / padding bytes.
dd 02h
db 2
db 1
db 20h, 00h
db 30h, 00h
; Messages
status db 'Applying Patch: '
run dw 0
db '/'
total dw 0
db '...', 0Dh, 0Ah, '$'
open_failed db 'Open failed', 0Dh, 0Ah, '$'
seek_failed db 'Seek failed', 0Dh, 0Ah, '$'
read_failed db 'Read failed', 0Dh, 0Ah, '$'
verify_failed db 'Verify failed', 0Dh, 0Ah, '$'
write_failed db 'Write failed', 0Dh, 0Ah, '$'
patch_successful db 'Patch successful', 0Dh, 0Ah, '$'
patch_too_big db 'Patch too big', 0Dh, 0Ah, '$'
too_many_patches db 'Too many patches', 0Dh, 0Ah, '$'
MSG_OPEN_FAILED equ 0
MSG_SEEK_FAILED equ 1
MSG_READ_FAILED equ 2
MSG_VERIFY_FAILED equ 3
MSG_WRITE_FAILED equ 4
MSG_PATCH_SUCCESSFUL equ 5
MSG_PATCH_TOO_BIG equ 6
MSG_TOO_MANY_PATCHES equ 7
MSG_TABLE_SIZE equ 8
msg_table:
dw offset open_failed
dw offset seek_failed
dw offset read_failed
dw offset verify_failed
dw offset write_failed
dw offset patch_successful
dw offset patch_too_big
dw offset too_many_patches
; I/O buffer
MAX_PATCH_SIZE equ 50
buffer db 50 dup(?)
.code
.8086
;
; Convert a binary number <= 99 to its ASCII representation
;
; Input:
; AL: Number to convert
;
; Output:
; AX: Ascii representation of number
;
itoa:
push bx
push dx
; Check if the number is >= 99
xor ah, ah
cmp al, 99
jge .number_too_large
; Convert the number in al to ASCII by dividing by 10
; and adding 30h.
mov bl, 10
div bl
add ax, 3030h
jmp .itoa_return
.number_too_large:
mov ax, 3939h
.itoa_return:
pop dx
pop bx
ret
;
; Verify the bytes we're about to overwrite
; with our patch byte by byte.
;
; Input:
; BX - File Handle
; CX - Count
;
; Clobbers: AX, CX, DX, DI
;
verify_patch:
; Read the bytes
mov dx, offset buffer
mov ax, 3F00h
int 21h
cmp ax, cx
je .verify
mov dx, MSG_READ_FAILED
call print_message_and_exit
.verify:
; Verify (byte-by-byte)
push cx
mov di, dx
.verify_loop:
lodsb
cmp al, [di]
jne .verify_failed
inc di
loop .verify_loop
.verify_done:
pop cx
mov ax, cx
ret
.verify_failed:
mov dx, MSG_VERIFY_FAILED
call print_message_and_exit
;
; Write the patch
;
; Input:
; AX: Number of bytes to write
; BX: File Handle
; CX/DX: File offset to patch
; SI: Pointer to bytes to write
;
write_patch:
; Seek back to where we want to patch
push ax
mov ax, 4200h
int 21h
jnc .write_patch
mov dx, MSG_SEEK_FAILED
call print_message_and_exit
.write_patch:
; Now, write our patch
pop cx
mov dx, si
mov ax, 4000h
int 21h
jc .write_failed
cmp ax, cx
jne .write_failed
; Loop
add si, ax
ret
.write_failed:
mov dx, MSG_WRITE_FAILED
call print_message_and_exit
;
; Print a message and exit
;
; Input:
; BX: File Handle
; DX: Index of message in message_table
;
; Doesn't return
;
print_message_and_exit:
push bx
mov bx, dx
xor bh, bh
; Make sure it doesn't overrun the table
cmp bl, MSG_TABLE_SIZE
jge .close_and_exit
; Print the message
shl bl, 1
mov dx, word ptr msg_table[bx]
mov ax, 0900h
int 21h
.close_and_exit:
pop bx
test bx, bx
jz .do_exit
; Close the file
mov ax, 3E00h
int 21h
.do_exit:
mov ax, 4C00h
int 21h
;
; The main routine
;
start:
; Ensure ds points to our data segment (.data)
mov ax, @data
mov ds, ax
; Prepare our status message (and check our patch count)
xor ax, ax
mov si, offset patches
mov cl, [patch_count]
mov al, cl
cmp al, 100
jl .open_file
mov dx, MSG_TOO_MANY_PATCHES
call print_message_and_exit
.open_file:
call itoa
mov di, offset total
mov [di], ax
; Open the target file (r/w)
mov ax, 3D02h
mov dx, offset target
int 21h
mov bx, ax
jnc .patch_loop
mov dx, MSG_OPEN_FAILED
call print_message_and_exit
.patch_loop:
cld
push cx
; Print the status message
mov al, [patch_count]
sub al, cl
inc al
call itoa
mov [run], ax
mov dx, offset status
mov ax, 0900h
int 21h
; Load the offset
lodsw
mov dx, ax
lodsw
mov cx, ax
push cx
push dx
; Seek
mov ax, 4200h
int 21h
jnc .check_patch_size
mov dx, MSG_SEEK_FAILED
call print_message_and_exit
.check_patch_size:
; Load the count
lodsb
xor cx, cx
mov cl, al
; Make sure it's not too big
cmp cl, MAX_PATCH_SIZE
jle .check_verify
mov dx, MSG_PATCH_TOO_BIG
call print_message_and_exit
.check_verify:
; Check if we want verification
lodsb
test al, al
mov ax, cx
jz .do_write
call verify_patch
.do_write:
pop dx
pop cx
call write_patch
pop cx
loop .patch_loop
mov dx, MSG_PATCH_SUCCESSFUL
call print_message_and_exit
end start
; vi:set ts=8 sw=8 expandtab sta ft=tasm:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment