blob: 3be94940eac2d599d99032318e8418ba362d72c8 [file] [log] [blame] [raw]
/*
* hmloadstart.S -- A DOS utility for loading a file into high memory.
* Copyright (C) 2006,2007 Tinybit(tinybit@tom.com)
* Copyright (C) 2006,2007 John Cobb (Queen Mary, University of London)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
The original source code was written by John Cobb and can be found at:
http://sysdocs.stu.qmul.ac.uk/sysdocs/Comment/GrubForDOS/code/xmsel.asm
Transformed to AT&T syntax by Tinybit <tinybit@tom.com>.
The original copyright notice can be found at:
http://sysdocs.stu.qmul.ac.uk/sysdocs/Comment/GrubForDOS/code/warranty
and here is an exact copy of the "warranty" file in August 16, 2007:
The material is available under the usual free software rules.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* This program is used to generate the hmload.com file.
*
* Use the following shell command to generate the file:
*
* cp hmloadstart hmload.com
*
*/
/* John Cobb's Note:
* It takes two parameters -f filename -a address
* Address is in megabytes. It assumes that himem.sys
* or equivalent XMS provider is running.
*/
/* Use the following command to compile and build:
*
* gcc -o hmloadstart.exec -nostdlib -I. -I.. -I../stage1 -Wl,-N -Wl,-s hmloadstart.S
*
*/
#define ABS(x) (x - _start + 0x100)
#define xmsh (bss_begin_address + 0)
#define xmsa (bss_begin_address + 256)
#define xmsl (bss_begin_address + 256 + 512)
#define buffer (bss_begin_address + 256 + 512 + 128)
#define thestack (bss_begin_address + 256 + 512 + 128 + 4096)
#define lastword (bss_begin_address + 256 + 512 + 128 + 4096 + 4096)
#define xmsh (bss_begin_address + 0)
#define xmsh (bss_begin_address + 0)
#define xmsh (bss_begin_address + 0)
.text
.globl _start
_start:
/* this is real mode dos code */
.code16
# this code loads a file at a given address using the 32 bit XMS extensions.
# it makes a pretty wild assumption which is that if it
# asks for the biggest XMS memory block that block will contain the
# the address required. (on all machines I tried I got back two
# blocks, one small one and one large one.
# Obviously more sophisticated code could work round the possibility
# that you space you need straddles more than one block.
# The other assumption is that the data once loaded will stay at the
# same address, which of course it need not. A solution to that might be
# to exit terminate and stay loaded so as to hold onto the resources
//org 100h
//section .text
//start:
# first cut program size down to that desired
# (.com prgrams get all of memory to start)
movw %cs, %ax
movw %ax, %es # set ES to CS
movw $ABS(lastword), %bx # desired end of program
movw %bx, %sp # set stack pointer
addw $15, %bx # round to nearest page (ok so long as not too big!)
shrw $4, %bx # pages
movb $0x4A, %ah
int $0x21 # cut program down to size
xorw %ax, %ax
movw %ax, ABS(hand) # not ensure handle zero
movw %ax, ABS(file)
movw %ax, ABS(addr)
movw $1, ABS(xmsa20init)
movw $ABS(hallo), %dx
call chars # initial part of message about parameters
xorw %cx, %cx
cld
movb 0x80, %cl
movw $0x81, %si
movw $ABS(buffer), %di
repz movsb # copy the parameter block to a convenient place
movb $'$', (%di)
movw $ABS(buffer), %dx
call msg # echo the parameters
movb 0x80, %al
movw $ABS(errparam), %dx
testb %al, %al
jnz startparams
paramerr:
call msg
movw $ABS(use), %dx
call msg
movw $ABS(func), %dx
call msg
jmp exiterr
nohyphen:
movw $ABS(needhyphen), %dx
jmp paramerr
noparamletter:
movw $ABS(misparam), %dx
notparam:
movb %ah, ABS(notknown)
movw $ABS(notknown), %dx
jmp paramerr
novalue:
movb %ah, ABS(misvalue+11)
movw $ABS(misvalue), %dx
jmp paramerr
nofile:
movw $ABS(misfile), %dx
jmp paramerr
noaddr:
movw $ABS(misaddr), %dx
jmp paramerr
startparams:
xorw %cx, %cx
movb 0x80, %cl # count in cx
movw $0x81, %di # point to start of buffer
cld
parameters:
movb $' ', %al
repe scasb # skip spaces nb pointer and count are left after
jcxz endparams # the character that causes the termination!
movb -1(%di), %ah # pick up the character that caused termination
cmpb $'-', %ah
jne nohyphen
jcxz noparamletter
movb (%di), %ah # the parameter identifier
incw %di
decw %cx
jcxz novalue
movb $' ', %al # skip spaces after the identifier
repe scasb
incw %cx
decw %di
orb $0x20, %ah # case insensitive
cmpb $'f', %ah
je .isfile
cmpb $'a', %ah
je .isaddr
jmp notparam
.isfile:
movw %di, ABS(file)
jmp .nextp
.isaddr:
movw %di, ABS(addr)
.nextp:
movb $' ', %al # look for space
repne scasb
cmpb $' ', -1(%di) # did we actually find a space?
jne .lastp # if not must have been last and terminated by count
movb $0, -1(%di)
jcxz endparams
jmp parameters
.lastp:
movb $0, (%di)
# check we got everything
endparams:
movw ABS(addr), %cx
jcxz noaddr
movw ABS(file), %cx
jcxz nofile
xorl %eax, %eax
movl $10, %ecx
movw ABS(addr), %si # address of addressvalue
xorl %ebx, %ebx
.dec1:
movb (%si), %bl
subb $'0', %bl
jl .dec2
cmpb $10, %bl
jge .dec2
mull %ecx
addl %ebx, %eax
incw %si
jmp .dec1
.dec2:
shll $20, %eax #; shift to megabytes
movl %eax, ABS(address)
movw ABS(file), %dx
xorw %cx, %cx
movw $0x3d00, %ax #; open the file
int $0x21
jnc opened
movw $ABS(erropen), %dx
movw ABS(file), %si
jmp errstr
opened:
movw %ax, ABS(hand)
movw $ABS(act1), %dx
call chars
movw ABS(file), %di
movb $0, %al
movw $256, %cx
repne scasb
movb $'$', (%di)
movw ABS(file), %dx
call chars
movw $ABS(act2), %dx
call chars
movw $ABS(address+3), %si
call hexl
call eol
# see if we have xms available
movw $0, ABS(xmsfull)
movw $0x4300, %ax
int $0x2F
cmpb $0x80, %al
je .xmsok
movw $ABS(noxmsmsg), %dx
call msg
jmp exiterr
.xmsok:
movw $0x4310, %ax
pushw %es
pushw %ds
int $0x2F
popw %ds
movw %es, ABS(xmsptr+2)
movw %bx, ABS(xmsptr)
popw %es
movw $ABS(xmsProc), %dx
call chars
movw $ABS(xmsptr+3), %si
call hexl
call eol
# xms version etc
movb $0, %ah
call xms
movw %ax, ABS(num)
movw %bx, ABS(num+2)
pushw %dx
movw $ABS(xmsver), %dx
call chars
movw $ABS(num+1), %si
call hexw
movw $ABS(xmsrev), %dx
call chars
movw $ABS(num+3), %si
call hexw
popw %cx # value that was in dx after the xms call
movw $ABS(xmsHMAno), %dx
jcxz .next
movw $ABS(xmsHMAyes), %dx
.next:
call msg
call checka20 # check status of the a20 line
jnc .ok
jmp exiterr
.ok:
movw %ax, ABS(xmsa20init) # save initial status of line
# a20 not needed in this version
# call xmsenable # locally enable a20
# jnc .gota20
# jmp xmserr
#.gota20:
#
# aquire xms memory blocks
# i am going to assume that most of xms is available as one large block
# and that will encompass the desired adress obviously I should do a sanity
# check but for now lets just stick straws in the hair
# extended xms spec found at
# http://freedos.sourceforge.net/freedos/news/press/1991-xms30.html
# aquire all full sized xms memory blocks available
movw $0, ABS(nxmsh) # zero count
.getxms:
movb $0x88, %ah
call xms # query largest xms block size
testb %bl, %bl
jz .gotms # success if BL zero
movw $ABS(xmsnomem), %dx
call msg
jmp .donemem
.gotms:
movl %eax, ABS(num) # save block size
movw $ABS(xmslargest), %dx
call chars
movw $ABS(num+3), %si
call hexl
movw $ABS(inkbmsg), %dx
call chars
# movw ABS(num), %dx
# xorw ABS(fullxmsblock), %dx
# jnz .donemem
movl ABS(num), %edx
movb $0x89, %ah
call xms # grab the block
testw %ax, %ax
jz .donemem
movw ABS(nxmsh), %si # current handle index
movb $0, ABS(xmsl)(%si) # zero the lock indicator
shlw $1, %si
movw %dx, ABS(xmsh)(%si) # store handle
pushw %dx
movw $ABS(xmsgotblock), %dx
call chars
popw %dx
movb $0x0C, %ah
call xms
testw %ax, %ax
jz .incind
movw ABS(nxmsh), %si # current handle index
movb $1, ABS(xmsl)(%si) # set the lock indicator
shlw $2, %si
movw %bx, ABS(xmsa)(%si)
movw %dx, ABS(xmsa+2)(%si)
pushw %si
movw $ABS(xmslockedblock), %dx
call chars
popw %si
addw $ABS(xmsa+3), %si
call hexl
# movw $4, %cx
# call hexstr
.incind:
call eol
movw ABS(nxmsh), %si # current handle index
incw %si
movw %si, ABS(nxmsh)
jmp .getxms
.donemem:
call eol
movw $ABS(xmsblocksgot), %dx
call chars
movw $ABS(nxmsh+1), %si
call hexw
call eol
# start the load
movl ABS(address), %eax
subl ABS(xmsa), %eax # make relative to the first xms segment
copyin:
pushl %eax
movb $0x3f, %ah #; read file
movw ABS(hand), %bx
movw $4096, %cx
movw $ABS(buffer), %dx
int $0x21
jc eof
testw %ax, %ax
jz eof
movw $ABS(buffer), %bx
# setup the xms move structure
movzwl %ax, %eax
movl %eax, ABS(xmsmvlen)
movw $0, ABS(xmsmvshand)
movw %bx, ABS(xmsmvsoff)
movw %ds, ABS(xmsmvsseg)
popl %eax
movl %eax, ABS(xmsmvdoff)
addl ABS(xmsmvlen), %eax
movw ABS(xmsh), %dx
movw %dx, ABS(xmsmvdhand)
movw $ABS(xmsmove), %si
pushl %eax
movb $0x0B, %ah
call xms
popl %eax
jmp copyin
eof:
popl %eax
movw $ABS(done), %dx
call msg
# for now release the xms again
# nb this is questionable as we don't want it to move but...
.relxms:
movw ABS(nxmsh), %si # index of last handle
testw %si, %si
jz .xmsfreed
decw %si
movw %si, ABS(nxmsh) # decrement for next time
movb ABS(xmsl)(%si), %ah # check lock status
testb %ah, %ah
jz .nolock
shlw $1, %si
movw ABS(xmsh)(%si), %dx
movb $0x0D, %ah
call xms # unlock block
.nolock:
movw ABS(nxmsh), %si # index of last handle
shlw $1, %si
movw ABS(xmsh)(%si), %dx
movb $0x0A, %ah
call xms # free block
jmp .relxms
.xmsfreed:
movw ABS(xmsa20init), %ax
testw %ax, %ax
jnz .wason
# call xmsdisable # locally disable xms
# jnc .losta20
# jmp exiterr
#.losta20:
.wason: # or possibly we never got it in the first place
movw ABS(hand), %bx
movb $0x3E, %ah # close file
int $0x21
movw $0x4C00, %ax # return success
int $0x21
exit:
movw $0x4C00, %ax # exit with 0 result code
int $0x21
errstr:
pushw %si
call chars
popw %si
movw $256, %cx
movw $ABS(buffer), %di
cld
.str1:
lodsb
testb %al, %al
jz .str2
stosb
loop .str1
.str2:
movb $'$', (%di)
movw $ABS(buffer), %dx
err:
call msg
exiterr:
movw ABS(hand), %bx
testw %bx, %bx
jz .nofile
movb $0x3e, %ah # close file
int $0x21
.nofile:
movw $0x4c01, %ax # return failure
int $0x21
msg:
movb $0x09, %ah
int $0x21
eol:
movw $ABS(eolmsg), %dx
movb $0x09, %ah
int $0x21
ret
chars:
movb $0x09, %ah
int $0x21
ret
hexstr:
cld
jmp hexout
hexl:
movw $4, %cx
jmp hexnum
hexw:
movw $2, %cx
jmp hexnum
hexb:
movw $1, %cx
hexnum:
std
hexout:
# expect si pointing at string or at most sig byte of num
movw $ABS(hex), %bx
lodsb
movb %al, %ah
shrb $4, %al
# andb $0x0F, %al
xlat
movb %al, ABS(tbuff+1)
pushw %cx
pushw %si
movw $ABS(tbuff), %dx
movb $0x09, %ah
int $0x21
popw %si
popw %cx
loop hexout
ret
checka20:
# check status of a20 line
movb $7, %ah
call xms
testb %bl, %bl
jnz .noa20
movw $ABS(xmsa20off), %dx
pushw %ax
testw %ax, %ax
jz .next1
movw $ABS(xmsa20on), %dx
.next1:
call msg
movb $0, %bl
popw %ax
clc
ret
.noa20:
pushw %bx
movw $ABS(xmsnoa20), %dx
call chars
popw %bx
pushw %bx
call xmserr
popw %bx
stc
ret
xmserr:
movb %bl, ABS(num)
movw $ABS(xmserrcode), %dx
call chars
movw $ABS(num), %si
call hexb
call eol
ret
xms:
pushw %ds
lcall *ABS(xmsptr)
popw %ds
ret
xmsenable:
movb $5, %ah
call xms
testw %ax, %ax
jnz .gota
movw $ABS(xmslocenfai), %dx
pushw %bx
call chars
popw %bx
call xmserr
stc
ret
.gota:
movw $ABS(xmslocenable), %dx
call msg
call checka20
clc
ret
xmsdisable:
movb $6, %ah
call xms
testw %ax, %ax
jnz .losta
movw $ABS(xmslocdisfai), %dx
pushw %bx
call chars
popw %bx
call xmserr
stc
ret
.losta:
movw $ABS(xmslocdisable), %dx
call msg
clc
ret
//section .data
hallo:
.ascii "Parameters: $"
//; file: db 'C:\temp\mdump',0
erropen:
.ascii "Could not open file: $"
errparam:
.ascii "No Parameters!$"
use:
.ascii " Use: -f filename -a address (in megabytes)$"
func:
.ascii "Loads file to address$"
needhyphen:
.ascii " parameters must start with -$"
notknown:
.ascii "x is not a recognised parameter$"
misparam:
.ascii "Parameter type missing after -$"
misvalue:
.ascii "Parameter -x has no value!$"
misfile:
.ascii "-f (filename) parameter missing$"
misaddr:
.ascii "Address parameter missing$"
act1:
.ascii "Loading file: $"
act2:
.ascii " to 0x$"
done:
.ascii "file loaded$"
.align 2
file:
.word 0
addr:
.word 0
hand:
.word 0
eolmsg:
.ascii "\r\n$"
inkbmsg:
.ascii " KB,$"
xmsnomem:
.ascii "No free XMS memory$"
xmslargest:
.ascii "Largest XMS block 0x$"
xmsgotblock:
.ascii " aquired,$"
xmslockedblock:
.ascii " locked at 0x$"
xmsblocksgot:
.ascii "number of full sized XMS blocks 0x$"
noxmsmsg:
.ascii "XMS interface not available$"
xmsnohmsg:
.ascii "XMS get handle failed$"
xmsfree:
.ascii "Free XMS$"
xmslarge:
.ascii "largest XMS$"
xmsstat:
.ascii "XMS Status$"
xmsnoa20:
.ascii "couldn't read a20 state$"
xmserrcode:
.ascii " err=0x$"
xmsa20off:
.ascii "A20 line off$"
xmsa20on:
.ascii "A20 line on$"
xmsver:
.ascii "XMS Version 0x$"
xmsrev:
.ascii " XMS Revision 0x$"
xmsHMAyes:
.ascii " HMA Exists$"
xmsHMAno:
.ascii " No HMA$"
xmsProc:
.ascii "XMS procedure address 0x$"
hex:
.ascii "0123456789ABCDEF" # String of hex numbers
tbuff:
.ascii "xx$"
.align 2
xmsa20init:
.word 0
xmslocenfai:
.ascii "Failed local enable of A20$"
xmslocdisfai:
.ascii "Failed local disable of A20$"
xmslocenable:
.ascii "A 20 locally enabled$"
xmslocdisable:
.ascii "A 20 locally disabled$"
dump:
.byte 0,1,2,3
//section .bss
.align 4
num:
.byte 0,0,0,0
xmsptr:
.word 0,0
xmsnext:
.word 0
xmsfull:
.word 0
address:
.long 0
xmsmove: # structure
xmsmvlen:
.long 0
xmsmvshand:
.word 0
xmsmvsoff: # note if shand non zero then 32 bit ofsset and no seg
.word 0
xmsmvsseg:
.word 0
xmsmvdhand:
.word 0
xmsmvdoff:
.word 0
xmsmvdseg:
.word 0
# end of xms move structure
nxmsh:
.word 0
.align 16
bss_begin_address:
/*
xmsh:
.space 256
xmsa:
.space 512
xmsl:
.space 128
buffer:
.space 4096
thestack:
.space 4096
lastword:
.word 0
*/