Do enough of the boot stub to produce an EXE header and enter DPMI protected

mode.
This commit is contained in:
David Given 2022-08-08 22:03:12 +02:00
parent 3716d49cd9
commit 1e0961c679

View file

@ -12,141 +12,263 @@
.sect .text .sect .text
.use16 .use16
exe_header:
.data2 0x5a4d ! magic number
.data2 0 ! number of bytes in last loadable page
.data2 exe_text_paras ! size of .exe, in pages
.data2 0 ! number of relocation entries
.data2 0 ! start of loadable area, in 16-byte paragraphs
.data2 exe_ram_paras ! required RAM size, in 16-byte paragraphs
.data2 0 ! maximum RAM siz, in 16-byte paragraphse
.data2 0 ! initial SS, relative to program
.data2 exe_stack ! initial SP
.data2 0 ! checksum (ignored)
.data2 exe_start ! initial IP
.data2 0 ! initial CS, relative to program
.data2 0 ! offset of relocation table
.data2 0 ! overlay number
dpmi_edi = 0x00
dpmi_esi = 0x04
dpmi_ebp = 0x08
dpmi_ebx = 0x10
dpmi_edx = 0x14
dpmi_ecx = 0x18
dpmi_eax = 0x1c
dpmi_flags = 0x20
dpmi_es = 0x22
dpmi_ds = 0x24
dpmi_fs = 0x26
dpmi_gs = 0x28
dpmi_ip = 0x2a
dpmi_cs = 0x2c
dpmi_sp = 0x2e
dpmi_ss = 0x30
dpmi_ps = 0x32 ! WORD: protected register segment
dpmi_rs = 0x34 ! WORD: real register segment
dpmi_psp = 0x36 ! WORD: PSP segment
dpmi_switch = 0x38 ! DWORD: far pointer of pmode switch routine
.seek 0x3c
exe_start:
! On entry, DS=ES=PSP. Make DS=CS, so we're running in tiny mode.
push cs
pop ds
mov (dpmi_rs), ds
mov (dpmi_psp), es
! Initialise DPMI.
mov ax, 0x1687
int 0x2f
or ax, ax
jnz no_dpmi
mov (dpmi_switch+0), di ! write back PMODE switch routine
mov (dpmi_switch+2), es
or si, si ! do we need a DPMI private area?
jz 1f
mov bx, si
movb ah, 0x48
int 0x21 ! allocate memory from DOS
mov es, ax ! data area segment -> es
1:
! Switch to protected mode.
mov ax, 1 ! 32-bit app
callf (dpmi_switch)
jc no_pmode
mov (dpmi_edx), go_msg
mov ax, (dpmi_rs)
mov (dpmi_ds), ax
mov (dpmi_eax+1), 9
call int21
mov ax, 0x4c00
int 0x21
! Simulate DOS interrupt bx.
int21:
mov bx, 0x21
callint:
xor ax, ax
mov (dpmi_ss), ax ! zero stack: DPMI host allocates one.
mov (dpmi_sp), ax
push ds
pop es
mov di, ax
mov ax, 0x300
int 0x31 ! simulate DOS interrupt
push cs
pop ds
ret
no_pmode: ! Could not switch to protected mode.
mov dx, no_pmode_msg
jmp exit_with_error
no_dpmi:
mov dx, no_dpmi_msg
! fall through
! Displays the message in dx and exits.
exit_with_error:
movb ah, 9
int 0x21 ! print $-terminated string
mov ax, 0x4cff
int 0x21 ! terminate with error code al
.use32
start_32bit:
go_msg:
.ascii "Go!$"
no_pmode_msg:
.ascii "Couldn't switch to protected mode$"
no_dpmi_msg:
.ascii "No DPMI$"
exe_top:
exe_stack = exe_top + 512
exe_text_paras = [exe_top - exe_header + 511] / 512
exe_ram_paras = [exe_stack - exe_top + 15] / 16
#define STACK_BUFFER 128 /* number of bytes to leave for stack */ #define STACK_BUFFER 128 /* number of bytes to leave for stack */
begtext: begtext:
! Make sure we are running under MS-DOS 2 or above. ! Make sure we are running under MS-DOS 2 or above.
! !
! While at it, also remember the actual DOS version, so that we know ! While at it, also remember the actual DOS version, so that we know
! whether DOS gives us the program's name in the environment ! whether DOS gives us the program's name in the environment
! segment. (DOS 3+ does; DOS 2.x does not.) ! segment. (DOS 3+ does; DOS 2.x does not.)
movb ah, 0x30 movb ah, 0x30
int 0x21 int 0x21
cbw cbw
cmpb al, 2 cmpb al, 2
xchg bp, ax xchg bp, ax
jnc ok_sys jnc ok_sys
mov dx, bad_sys_msg mov dx, bad_sys_msg
dos_msg: dos_msg:
movb ah, 9 ret
int 0x21
ret
ok_sys: ok_sys:
! Resize the program's memory control block (MCB) to cover only the ! Resize the program's memory control block (MCB) to cover only the
! program's near code and data space. Use the starting sp value as ! program's near code and data space. Use the starting sp value as
! a guide to how much memory we can grab. Abort on any failure. ! a guide to how much memory we can grab. Abort on any failure.
! !
! As a side effect, this also frees up any memory allocated to our ! As a side effect, this also frees up any memory allocated to our
! program beyond 64 KiB. (The freed memory can possibly be used by ! program beyond 64 KiB. (The freed memory can possibly be used by
! e.g. child processes, in the future.) ! e.g. child processes, in the future.)
! !
! Also check that we have some space between the BSS end and the ! Also check that we have some space between the BSS end and the
! starting sp. ! starting sp.
cmp sp, endbss+STACK_BUFFER cmp sp, endbss+STACK_BUFFER
jb no_room jb no_room
movb ah, 0x4a movb ah, 0x4a
mov bx, sp mov bx, sp
movb cl, 4 movb cl, 4
shr bx, cl shr bx, cl
inc bx inc bx
int 0x21 int 0x21
jc no_room jc no_room
! Clear BSS. ! Clear BSS.
mov di, begbss mov di, begbss
mov cx, endbss+1 mov cx, endbss+1
sub cx, di sub cx, di
shr cx, 1 shr cx, 1
xor ax, ax xor ax, ax
cld cld
rep stosw rep stosw
! Get the size of the environment variables plus (if present) the ! Get the size of the environment variables plus (if present) the
! program name. Also count the number of environment variables. ! program name. Also count the number of environment variables.
xor di, di xor di, di
mov es, 0x002C(di) mov es, 0x002C(di)
! ax = 0 from above ! ax = 0 from above
cwd ! dx = count of env. vars. cwd ! dx = count of env. vars.
! cx = 0 from above ! cx = 0 from above
dec cx ! cx = max. str. bytes to scan dec cx ! cx = max. str. bytes to scan
scasb ! handle special case of empty env. scasb ! handle special case of empty env.
jz is_empty_env jz is_empty_env
size_env: size_env:
inc dx inc dx
repnz scasb repnz scasb
scasb scasb
jnz size_env jnz size_env
is_empty_env: is_empty_env:
cmp bp, 2 cmp bp, 2
jz no_argv0 jz no_argv0
scasw scasw
repnz scasb repnz scasb
no_argv0: no_argv0:
! Copy out the environment variables and (possibly) program name ! Copy out the environment variables and (possibly) program name
! onto the stack. ! onto the stack.
mov si, di mov si, di
dec si dec si
std std
copy_env: copy_env:
and si, -2 and si, -2
eseg lodsw eseg lodsw
push ax push ax
jnz copy_env jnz copy_env
mov cx, sp mov cx, sp
! Reset DF and es properly. ! Reset DF and es properly.
cld cld
push ss push ss
pop es pop es
! Reserve space for argc and the argv and envp pointers on the ! Reserve space for argc and the argv and envp pointers on the
! stack. These will be passed to __m_a_i_n later. ! stack. These will be passed to __m_a_i_n later.
sub sp, 6 sub sp, 6
mov ax, sp mov ax, sp
! Build up argc, argv[], and envp[]. ! Build up argc, argv[], and envp[].
push ax ! output buffer for argc, argv, envp push ax ! output buffer for argc, argv, envp
push bp ! MS-DOS version push bp ! MS-DOS version
push cx ! env. string data push cx ! env. string data
push dx ! count of env. vars. push dx ! count of env. vars.
mov ax, 0x0080 mov ax, 0x0080
push ax ! raw command line push ax ! raw command line
call __sys_initmain call __sys_initmain
add sp, 10 add sp, 10
! Bail out if something went wrong. ! Bail out if something went wrong.
test ax, ax test ax, ax
jnz no_room jnz no_room
! argc, argv, and envp are now at the stack top. Now go. ! argc, argv, and envp are now at the stack top. Now go.
call __m_a_i_n call __m_a_i_n
add sp, 6 add sp, 6
push ax push ax
call _exit call _exit
no_room: no_room:
mov dx, no_room_msg mov dx, no_room_msg
call dos_msg call dos_msg
movb al, -1 movb al, -1
jmp al_exit jmp al_exit
! Exit. ! Exit.
.define __exit .define __exit
.extern __exit .extern __exit
.define EXIT .define EXIT
.extern EXIT .extern EXIT
__exit: __exit:
EXIT: EXIT:
pop bx pop bx
pop ax pop ax
al_exit: al_exit:
movb ah, 0x4c movb ah, 0x4c
int 0x21 int 0x21
! Define symbols at the beginning of our various segments, so that we can find ! Define symbols at the beginning of our various segments, so that we can find
! them. (Except .text, which has already been done.) ! them. (Except .text, which has already been done.)
@ -168,3 +290,6 @@ no_room_msg: .ascii 'No room$'
.comm .trppc, 4 .comm .trppc, 4
.comm .ignmask, 4 .comm .ignmask, 4
.comm _errno, 4 .comm _errno, 4
! vim: ts=4 sw=4 et ft=asm