No idea.
> I am interested in disassembling boot sectors and BIOSes. DEBUG isn't
> good for that.
I think there are two important factors here:
1) how frequently you want to do this
2) the trade off between how much time you want to invest in learning how to
use a powerful assisted disassembler or debugger versus the time and effort
needed to use simpler tools.
I do this infrequently and usually do it the "hard" way. I usually use
these programs:
1) NASM assembler
2) NASM's NDISASM disassembler
3) my own hex dump program (you could use DJGPP's OD)
4) a DOS version of VI with EX support
5) MS-DOS EDIT
6) MS-DOS FC/B
7) DJGPP port of LESS
I use NDISASM to disassemble the entire file as -b16 then as -b32 saving the
output without regard to whether the disassembly is 100% correct or not.
I've found that only a few small instruction sequences typically near data
or transitioning from 16-bit to 32-bit will be incorrect. Next I use the EX
support of the VI editor to strip off the hex in front of the assembly for
both files. Otherwise, I use MS-DOS EDIT for all the editing. Then I hex
dump the program saving the output. The hex dump gives offsets for data,
text, branches, and shows the text data. For bootsectors and .com's, you
know the code starts as 16-bit. I then start cutting out the disassembly of
the data and text and replacing it with the correct text or data. I usually
add a comment that has the proper offsets of each. If there is 32-bit code,
I then patch in as much of it as I can. I then do offset disassemblies to
generate correct sequences for the areas where disassembly wasn't correct
and patch them in. After those are patched into the source it should be
complete except for branches. Next I add labels at the proper offsets for
the branches and convert the branch addresses to labels. This can be
tedious, but just use L with the address. If it's got lots of jmp's, I may
use J with an address for the jumps. Once the source is complete, assemble
with NASM, and hex dump the new version. Then compare the new and original
hex dumps. NASM compiles some sequences slightly different than say MASM,
usually branches. Rework until dumps match or only equivalent instruction
sequence differences exist. Anyway, I've done this brute force method a few
times now with good results. It probably took me an hour and a half for a
512 byte bootsector the first time. After maybe ten times, it took about
fifteen, maybe twenty, minutes the other day. The largest I've done is 4k
.com and 3k device driver this way. The .com was simple. The device driver
took longer since it used structures and other DOS device driver stuff I
wasn't familiar with. It would take longer to do a 64k BIOS this way, but
just depends on your determination and time constraints.
Rod Pemberton
> Debug is fine for DOS boot sectors.
Thanks, that's very good to know. The boot sector I want to look at
is actually one produced by Lilo, produced on a RH Linux system and
apparently produced using as86 and ld86. Linux doesn't seem to have
any tools for disassembly of the 8086 binary files that are produced,
but I have FREEDOS installed on another partition on the same machine,
so I figured I'd try to use something from that platform. Is there a
way to save a transcript of the DEBUG session?
I'm aware of dosemu under Linux but it doesn't work very well on my
system.
I did a Google search of the disassemblers you mentioned and was led
to a Wikipedia page that also mentions Norman Ramsay's New Jersey Toolkit,
which he uses to construct disassemblers for various machines. The source
code comes in the form of a literate program and I'm a big fan of literate
programming. So, I've downloaded that and will try to make sense of it.
That might let me stay in Linux and do this.
Of course, the boot sector that is produced by Lilo derives from source code
that is freely available and which I have a copy of. But I'd like to compare
the final product with the sources, hence the need to look at the boot sector.
Use DEBUG (below) or post a hex dump of the boot sector and ask someone here
or on alt.lang.asm to post a disassembly.
For Linux, you should be able to do something like this to generate a hex
dump:
dd if=/dev/hda of=boot.bin bs=512 count=1
od -A x -t x1 boot.bin > boot.dmp
For DOS:
A) using John Fine's partcopy:
partcopy -h0 0 200 boot.bin
(hex dump of boot.bin)
- DJGPP has a port of OD. Use same line above.
- load file into DEBUG and dump
B) dump using DEBUG - raw sector from disk
l 7c00 2 0 2
d 7c00 7dff
q
> Is there a way to save a transcript of the DEBUG session?
A) type in commands above blind:
DEBUG > asdf
B) create input command file with commands above
DEBUG < command.txt > output.txt
Rod Pemberton
> Thanks, that's very good to know. The boot sector I want to look at
> is actually one produced by Lilo, produced on a RH Linux system and
> apparently produced using as86 and ld86. Linux doesn't seem to have
> any tools for disassembly of the 8086 binary files that are produced,
> but I have FREEDOS installed on another partition on the same machine,
> so I figured I'd try to use something from that platform. Is there a
> way to save a transcript of the DEBUG session?
Wouldn't it more appropriate to look at the original source code
than to disassemble the binary?
!
! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
! modified by Drew Eckhardt
! modified by Bruce Evans (bde)
! modified by John Coffman for LILO disk.b diagnostic (27-Mar-2000)
! (09-Mar-2001)
!
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! itself out of the way to address 0x90000, and jumps there.
!
! bde - should not jump blindly, there may be systems with only 512K low
! memory. Use int 0x12 to get the top of memory, etc.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts.
!
! NOTE! currently system is at most (8*65536-4096) bytes long. This should
! be no problem, even in the future. I want to keep it simple. This 508 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix (and especially now that the kernel is
! compressed :-)
!
! The loader has been made as simple as possible, and continuous
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole tracks at a time whenever possible.
;#include <linux/config.h> /* for CONFIG_ROOT_RDONLY */
;#include <asm/boot.h>
.text
SIZEBYTES = SIZEDISKB+511 ! round disk.b size for division
SETUPSECS = SIZEBYTES/512 ! size of disk.b in sectors
BOOTSEG = 0x07C0 ! original address of boot-sector
INITSEG = 0x9000 ! we move boot here - out of the way
SETUPSEG = INITSEG+0x20 ! setup starts here
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
MAX_SETUPSECS = 31 ! same as lilo.h
STK_SIZE = MAX_SETUPSECS*512+512 ! 0x4000
#ifndef SIZEKRNL
SYSSIZE = 0x8000 ! system size: number of 16-byte clicks
! to be loaded
#else
SIZEKRNLP = SIZEKRNL+15
SYSSIZE = SIZEKRNLP/16
#endif
ASK_VGA = 0xfffd
! ROOT_DEV & SWAP_DEV are now written by "build".
ROOT_DEV = 0
SWAP_DEV = 0
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef CONFIG_ROOT_RDONLY
#define CONFIG_ROOT_RDONLY 1
#endif
! ld86 requires an entry symbol. This may as well be the usual one.
.globl _main
_main:
mov ax,#BOOTSEG
mov ds,ax
int 0x12
sub ax,#STK_SIZE/1024
shl ax,#10-4
mov bx,#INITSEG
cmp ax,bx
jb *+3
xchg ax,bx
mov es,ax
mov cx,#256
sub si,si
sub di,di
cld
rep
movsw
mov di,#STK_SIZE-12 ! 0x4000 is arbitrary value >= length of
! bootsect + length of setup + room for stack
! 12 is disk parm size
! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
! wouldn't have to worry about this if we checked the top of memory. Also
! my BIOS can be configured to put the wini drive tables in high memory
! instead of in the vector table. The old stack might have clobbered the
! drive table.
mov ds,ax
mov ss,ax ! put stack at INITSEG:0x4000-12.
mov sp,di
; jmpi go,INITSEG
push es
push #go
retf
! ax and es already contain INITSEG
go:
add ax,#0x20 ! seg offset to SETUPSEG
mov setupseg,ax
/*
* Many BIOS's default disk parameter tables will not
* recognize multi-sector reads beyond the maximum sector number
* specified in the default diskette parameter tables - this may
* mean 7 sectors in some cases.
*
* Since single sector reads are slow and out of the question,
* we must take care of this by creating new parameter tables
* (for the first disk) in RAM. We will set the maximum sector
* count to 36 - the most we will encounter on an ED 2.88.
*
* High doesn't hurt. Low does.
*
* Segments are as follows: ds=es=ss=cs - INITSEG,
* fs = 0, gs is unused.
*/
! cx contains 0 from rep movsw above
mov bx,#0x78 ! fs:bx is parameter table address
push ds
push cx ! contains 0 from rep movsw above
pop ds
lds si,(bx) ! ds:si is source
mov cl,#6 ! copy 12 bytes
cld
push di
rep
movsw
pop di
pop ds
movb 4(di),*36 ! patch sector count
;;; seg fs
push ds
push #0
pop ds
mov (bx),di
;;; seg fs
mov 2(bx),es
pop ds
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
! Also cx is 0 from rep movsw above.
load_setup:
xor ah,ah ! reset FDC
xor dl,dl
int 0x13
xor dx, dx ! drive 0, head 0
mov cl,#0x02 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ah,#0x02 ! service 2, nr of sectors
mov al,setup_sects ! (assume all on head 0, track 0)
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
push ax ! dump error code
call print_nl
mov bp, sp
call print_hex
pop ax
jmp load_setup
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
#if 0
! bde - the Phoenix BIOS manual says function 0x08 only works for fixed
! disks. It doesn't work for one of my BIOS's (1987 Award). It was
! fatal not to check the error code.
xor dl,dl
mov ah,#0x08 ! AH=8 is get drive parameters
int 0x13
xor ch,ch
#else
! It seems that there is no BIOS call to get the number of sectors. Guess
! 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
! 15 if sector 15 can be read. Otherwise guess 9.
mov si,#disksizes ! table of sizes to try
probe_loop:
lodsb
cbw ! extend to word
mov sectors, ax
cmp si,#disksizes+4
jae got_sectors ! if all else fails, try 9
xchg ax, cx ! cx = track and sector
xor dx, dx ! drive 0, head 0
xor bl, bl
mov bh,setup_sects
inc bh
shl bh,#1 ! address after setup (es = cs)
mov ax,#0x0201 ! service 2, 1 sector
int 0x13
jc probe_loop ! try next value
#endif
got_sectors:
! Print some inane message
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#9
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! ok, we've written the message, now
! we want to load the system (at 0x10000)
#if 0
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
#else
push #SYSSEG
pop es
#endif
#ifdef SIZEKRNL
call read_it
#endif
call kill_motor
call print_nl
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8),
! depending on the number of sectors we pretend to know we have.
#if 0
;; seg cs
mov ax,root_dev
or ax,ax
jne root_defined
;; seg cs
mov bx,sectors
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
cmp bx,#15
je root_defined
mov al,#0x1c ! /dev/PS0 - 1.44Mb
cmp bx,#18
je root_defined
mov al,#0x20 ! /dev/fd0H2880 - 2.88Mb
cmp bx,#36
je root_defined
mov al,#0 ! /dev/fd0 - autodetect
root_defined:
;; seg cs
mov root_dev,ax
#endif
! after that (everything loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
jmpi 0,SETUPSEG
setupseg = *-2
! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in: es - starting address segment (normally 0x1000)
!
sread: .word 0 ! sectors read of current track
head: .word 0 ! current head
track: .word 0 ! current track
read_it:
mov al,setup_sects
inc al
mov sread,al
mov ax,es
test ax,#0x0fff
die: jne die ! es must be at 64kB boundary
xor bx,bx ! bx is starting address within segment
rp_read:
#ifdef __BIG_KERNEL__
#define CALL_HIGHLOAD_KLUDGE .word 0x1eff,0x220 ! call far * bootsect_kludge
! NOTE: as86 can't assemble this
CALL_HIGHLOAD_KLUDGE ! this is within setup.S
#else
mov ax,es
sub ax,#SYSSEG
#endif
cmp ax,syssize ! have we loaded all yet?
jbe ok1_read
ret
ok1_read:
mov ax,sectors
sub ax,sread
mov cx,ax
shl cx,#9
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,#9
ok2_read:
call read_track
mov cx,ax
add ax,sread
cmp ax,sectors
jne ok3_read
mov ax,#1
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,#9
add bx,cx
jnc rp_read
mov ax,es
add ah,#0x10
mov es,ax
xor bx,bx
jmp rp_read
read_track:
pusha
pusha
mov ax, #0xe2e ! loading... message 2e = .
mov bx, #7
int 0x10
popa
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
and dx,#0x0100
mov ah,#2
push dx ! save for error dump
push cx
push bx
push ax
int 0x13
jc bad_rt
add sp, #8
popa
ret
bad_rt: push ax ! save error code
call print_all ! ah = error, al = read
xor ah,ah
xor dl,dl
int 0x13
add sp, #10
popa
jmp read_track
/*
* print_all is for debugging purposes.
* It will print out all of the registers. The assumption is that this is
* called from a routine, with a stack frame like
* dx
* cx
* bx
* ax
* error
* ret <- sp
*
*/
print_all:
mov cx, #5 ! error code + 4 registers
mov bp, sp
print_loop:
push cx ! save count left
call print_nl ! nl for readability
cmp cl, #5
jae no_reg ! see if register name is needed
mov ax, #0xe05 + 'A - 1
sub al, cl
int 0x10
mov al, #'X
int 0x10
mov al, #':
int 0x10
no_reg:
add bp, #2 ! next register
call print_hex ! print it
pop cx
loop print_loop
ret
print_nl:
mov ax, #0xe0d ! CR
int 0x10
mov al, #0xa ! LF
int 0x10
ret
/*
* print_hex is for debugging purposes, and prints the word
* pointed to by ss:bp in hexadecimal.
*/
print_hex:
mov cx, #4 ! 4 hex digits
mov dx, (bp) ! load word into dx
print_digit:
rol dx, #4 ! rotate so that lowest 4 bits are used
mov ax, #0xe0f ! ah = request, al = mask for nybble
and al, dl
#if 0
add al, #0x90 ! convert al to ASCII hex (four instructions)
daa
adc al, #0x40
daa
#else
daa ! shorter conversion routine
add al,#0xF0
adc al,#0x40 ! is now a hex char 0..9A..F
#endif
int 0x10
loop print_digit
ret
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
push dx
mov dx,#0x3f2
xor al,al
out dx,al ; outb
pop dx
ret
sectors:
.word 0
disksizes:
.byte 36,18,15,9
msg1:
.byte 13,10
.ascii "Loading"
.byte 0
.org 497
setup_sects:
.byte SETUPSECS
root_flags:
.word CONFIG_ROOT_RDONLY
syssize:
.word SYSSIZE
swap_dev:
.word SWAP_DEV
ram_size:
.word RAMDISK
vid_mode:
.word SVGA_MODE
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
LInux LOader (LILO) program code, documentation, and auxiliary programs are
Copyright 1992-1998 Werner Almesberger.
Copyright 1999-2005 John Coffman.
All rights reserved.
License
=======
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* 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.
* Neither the names of the author(s) nor the names of other contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
Disclaimer
==========
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 AUTHORS 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.
(Note: The above license is copied from the BSD license at:
http://www.opensource.org/licenses/bsd-license.html,
substituting the appropriate references in the template.)
(end)
Ehhh... What's wrong with "objdump -D -m i386 -b binary -Mdata16
-Mcode16 bootsector.dd"?
Right,
MartinS
A method I've used over the years is to put the debug-commands in a file and
redirect them into debug :
debug filename < commandfile > outputfile
While not being interactive anymore it makes it easy to back-track wrong
debug-commands and generate a new output-file. Just make sure that the
commandfile ends with a "q" and an empty line.
Ofcourse, MS never thought of upgrading DEBUG.EXE (probably allso
deliberatily), so it only understands 8086 code. But than again, that is
quite enough for most all boot-sectors.
Although I allso use other disassemblers DEBUG has got a feature I find
quite handy : the ability to execute some code and than leave the further
code open for scrutenizing. Very handy when, for example, the program uses
a sort of self-unpacker.
Regards,
Rudy Wieser
$ objdump -D bootsect.o
objdump: bootsect.o: File format not recognized
$ file bootsect.o
bootsect.o: Linux-8086 object file
> Allan Adler wrote:
>
> > Thanks, that's very good to know. The boot sector I want to look at
> > is actually one produced by Lilo, produced on a RH Linux system and
> > apparently produced using as86 and ld86. Linux doesn't seem to have
> > any tools for disassembly of the 8086 binary files that are produced,
> > but I have FREEDOS installed on another partition on the same machine,
> > so I figured I'd try to use something from that platform. Is there a
> > way to save a transcript of the DEBUG session?
>
> Wouldn't it more appropriate to look at the original source code
> than to disassemble the binary?
I am looking at the original source code. Yes, the assembled code eventually
winds up in memory at certain locations. But there are several source
files and the precise way they wind up on the boot sector isn't obvious
to me. I want to try to recognize the various parts of the source in
the disassembled boot sector.
> http://lilo.go.dyndns.org/
[Almost 1000 lines of quoted code deleted]
Just for future reference, should I follow Kleeman's example and post
very long quotations of code and dumps or should I find or put them
on a website and post the URL? On most newsgroups, I would expect that
such long quotations are discouraged, but maybe comp.os.mdsos.programmer
is different.
> Terence <tbwr...@cantv.net> writes:
>> Debug is fine for DOS boot sectors.
>
> Thanks, that's very good to know. The boot sector I want to look at
> is actually one produced by Lilo, produced on a RH Linux system and
> apparently produced using as86 and ld86. Linux doesn't seem to have
> any tools for disassembly of the 8086 binary files that are produced,
> but I have FREEDOS installed on another partition on the same machine,
> so I figured I'd try to use something from that platform. Is there a
> way to save a transcript of the DEBUG session?
Perhaps GRDB will be suitable. It's like an improved DEBUG with an
option to log the session and can be set to not interpret 386+
instructions. Source code is available.
http://members.tripod.com/~ladsoft/grdb.htm
Nice to know someone actually had the time to do it
Thanks, I'll try it (GRDB).
I could have used this last year for some keyboard/language switching
code I needed to fix.
.
> $ objdump -D bootsect.o
> objdump: bootsect.o: File format not recognized
And why don't you try the command I wrote? There's a reason for those
switches...
Right,
MartinS
You said earlier that you might try to disassemble a BIOS, or part thereof.
I posted these links to a.l.a. the other day. They may be useful to you
when you get around to that. Just keep in mind that the code for BIOSes
below is obsolete and will be both similar and different from a modern BIOS.
BIOS "modification" website:
http://www.geocities.com/mamanzip/articles.html
Ralf Brown's interrupt list documents function calls, including BIOS:
http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html
http://www.ctyme.com/rbrown.htm
http://www.delorie.com/djgpp/doc/rbinter/
An XT and AT BIOS code is here as bios-asm.zip and pcatbios.zip:
http://www.singlix.com/trdos/specs.html
An XT and AT BIOS code is here as pcbios.asm and pcatbios.zip:
(pcatbios.zip above is the same):
http://highgate.comm.sfu.ca/~rcini/classiccmp/systems.htm
Boch's rombios.c lists all the important BIOS locations, such as entry
points and data structures, in the "ROM BIOS compatability entry points"
section:
http://bochs.sourceforge.net/cgi-bin/lxr/source/bios/rombios.c
Rod Pemberton
> Allan Adler <a...@nestle.csail.mit.edu> read selectively and wrote:
> > Martin Str|mberg <a...@brother.ludd.ltu.se> writes:
> >> Ehhh... What's wrong with "objdump -D -m i386 -b binary -Mdata16
> >> -Mcode16 bootsector.dd"?
>
> > $ objdump -D bootsect.o
> > objdump: bootsect.o: File format not recognized
>
> And why don't you try the command I wrote? There's a reason for those
> switches...
Three reasons:
(1) I wasn't sure what the file bootsector.dd was.
(2) I had used objdump earlier and gotten complaints that the file
format was unknown.
(3) The option -M isn't documented in the man page for objdump on my system.
Likewise, the man page doesn't mention "binary" as an option for -b.
So, I thought you were wrong and tried to point out (2).
However, after reading your followup message, I tried harder and, as root,
executed
dd if=/dev/hda1 of=bootsect.dd bs=512 count=1
and then executed your recommended command and it worked. Here is an incipit
of the roughly 216 line output:
bootsector.dd: file format binary
objdump: bootsector.dd: no symbols
Disassembly of section .data:
00000000 <.data>:
0: fa cli
1: eb 7c jmp 0x7f
3: 6c insb (%dx),%es:(%edi)
4: 62 61 4c bound %esp,0x4c(%ecx)
7: 49 dec %ecx
8: 4c dec %esp
9: 4f dec %edi
a: 01 00 add %eax,(%eax)
c: 15 04 5a 00 00 adc $0x5a04,%eax
11: 00 00 add %al,(%eax)
13: 00 71 6e add %dh,0x6e(%ecx)
16: f5 cmc
17: 3b 68 56 cmp 0x56(%eax),%ebp
1a: c0 7f 01 69 sarb $0x69,0x1(%edi)
1e: 56 push %esi
1f: c0 7f 01 67 sarb $0x67,0x1(%edi)
23: 56 push %esi
24: c0 7f 01 01 sarb $0x1,0x1(%edi)
28: 44 inc %esp
29: 5a pop %edx
2a: 6b 56 c0 7f imul $0x7f,0xffffffc0(%esi),%edx
2e: 01 6c 56 c0 add %ebp,0xffffffc0(%esi,%edx,2)
32: 7f 01 jg 0x35
34: 62 3b bound %edi,(%ebx)
36: c0 7f 01 63 sarb $0x63,0x1(%edi)
3a: 3b c0 cmp %eax,%eax
3c: 7f 01 jg 0x3f
So, thanks for your suggestion and for insisting on it. Since the man
page doesn't go into enough detail, what should I have read to have been
able to figure out for myself how to use objdump for this problem?
It's ok that this output looks like gas code, since it's not hard to imagine
how to go from one assembly language to another. On the other hand, this
seems entirely unrelated to the code in bootsector.S that Kleeman posted.
That might mean some offset is needed before disassembly is initiated.
On another machine, I used ndisasm under Linux on the bootsector of that
machine, after using dd to obtain it. The results also look unfamiliar.
> You said earlier that you might try to disassemble a BIOS, or part thereof.
Yes, the machine I plan to experiment with is a very old machine, which I
consider expendable and feel free to experiment on. It has an
AMD-K6 P2 166.591 MB CPU and VIA VT82585 chipset. I don't know anything
about the BIOS, but I doubt that its source code is available. I want to
know everything about this old machine before it falls apart. Examining
the BIOS is certainly on the list of things to do, if it is actually doable.
> I posted these links to a.l.a. the other day. They may be useful to you
> when you get around to that. Just keep in mind that the code for BIOSes
> below is obsolete and will be both similar and different from a modern BIOS.
Thanks, I'll look at them. Obsolete BIOS is what I have. My main source of
information about interrupts is Gilluwe's The Undocumented PC, about 10
years old.
Gnu tools usually have their documentation in info.
Anyway, the man page on the system I tried does say "You list the
formats available with the -i option." (That system's info page is a
man page, which is weird.)
For my (original) post, I found the relevant options with "objdump
--help".
> how to go from one assembly language to another. On the other hand, this
> seems entirely unrelated to the code in bootsector.S that Kleeman posted.
> That might mean some offset is needed before disassembly is initiated.
Nope. You want /dev/hda for the MBR, I guess.
Right,
MartinS
Regards,
Michael
> > Perhaps GRDB will be suitable. It's like an improved DEBUG with an
> > option to log the session and can be set to not interpret 386+
> > instructions. Source code is available.http://members.tripod.com/~ladsoft/grdb.htm
>
> Nice to know someone actually had the time to do it
More at: http://www.bttr-software.de/links/#debug
--
Robert Riebisch
Bitte NUR in der Newsgroup antworten!
Please reply to the Newsgroup ONLY!
> (3) The option -M isn't documented in the man page for objdump on my system.
> Likewise, the man page doesn't mention "binary" as an option for -b.
Actually, all the information is in the man page (which has the same
information as the info page). It just took some getting used to.
> However, after reading your followup message, I tried harder and, as root,
> executed
> dd if=/dev/hda1 of=bootsect.dd bs=512 count=1
> and then executed your recommended command and it worked. Here is an incipit
> of the roughly 216 line output:
> bootsector.dd: file format binary
>
> objdump: bootsector.dd: no symbols
> Disassembly of section .data:
>
> 00000000 <.data>:
> 0: fa cli
> 1: eb 7c jmp 0x7f
> 3: 6c insb (%dx),%es:(%edi)
> 4: 62 61 4c bound %esp,0x4c(%ecx)
> 7: 49 dec %ecx
> 8: 4c dec %esp
> 9: 4f dec %edi
> a: 01 00 add %eax,(%eax)
> c: 15 04 5a 00 00 adc $0x5a04,%eax
> 11: 00 00 add %al,(%eax)
> 13: 00 71 6e add %dh,0x6e(%ecx)
[snip]
This was from a machine different from the one I plan to study. After
clearing interrupts, the above code jumps unconditionally to 0x7f, which
might be anything. It's not a call, so I can't assume it will return. In
particular, all of what appear to be instructions following the jump might
really be data.
> I am interested in disassembling boot sectors and BIOSes. DEBUG isn't
> good for that. Frank Gilluwe wrote a program called Sourcer that is
> better for this, but it doesn't seem to be available. Also, it isn't
> free. Is there anything comparable running under FREEDOS?
Suppose I am in FREEDOS and want to run DEBUG on the BIOS. How do I
tell DEBUG where the BIOS is?
> Suppose I am in FREEDOS and want to run DEBUG on the BIOS. How do I
> tell DEBUG where the BIOS is?
Two different methods :
One : just tell DEBUG where to start the disassembling by providing CS as
well as IP after the "u" command : u f000:1234
Two : change the CS and IP registers (using "r cs <enter> value" and "r ip
<enter> value") after which you give an "r" command. (register-display. For
some reason its needed). After that you can simply use "u" to start
disassembly.
Suggestion/hint : DEBUG has some Help included under the "?" command.
Reards,
Rudy Wieser
Thanks. I guess I wasn't clear in stating my question. I know a little about
how to use DEBUG. What I don't know what address I have to give it for the
BIOS. Are you saying it will always be located at f000:1234? If not, how
would I find out what it is on my machine?
Yes, thats pretty-much what I'm saying, although the address I mentioned was
just an example.
The BIOS has to be at a fixed location, otherwise there would be no way for
the processor find and execute its code at startup (the BIOS-code is the
first thing the processor will see after power has been supplied to it)
Also worth to know is that the machine has got *multiple* BIOS-es. The
Video-card has one on the card itself, and the Hard-disk allso has one (in
earlier days the Harddisk controller was a seperate card, just like the
Video card currently can be). A network-card can allso contain its own BIOS
(so that certain machine-configurations can boot for a network and do not
need a local (hard-)disk)
Although I do not exactly know the adress-ranges of the seperate BIOS-es I
think that a quick Google will be able to tell you that.
Though one interresting addres is F000:FFF0, which is the hardware-reset
address of where the processor will go to after being powered-up, or when
its reset-line is pulled down.
Regards,
Rudy Wieser
> Allan Adler <a...@nestle.csail.mit.edu> schreef in berichtnieuws
> y931wbf...@nestle.csail.mit.edu...
> <snip>
> > Thanks. I guess I wasn't clear in stating my question. I know a little
> > about how to use DEBUG. What I don't know what address I have
> > to give it for the BIOS. Are you saying it will always be located at
> > f000:1234?
>
> Yes, thats pretty-much what I'm saying, although the address I mentioned was
> just an example.
I think we are confusing each other due to varying interpretations of
intended sequences of quantifiers. Natural languages are most unsuitable
for this kind of communication, so let's try mathematics. In this reply,
it sounds as though you are saying the following:
(1) Let M be a machine. Then the location of the BIOS of M is a function
of M. Denote that function F(M).
(2) F is not a constant function on the set of all machines.
If that is what you mean, then the problem of discovering F(M) for a given
machine M remains, but see below.
> Also worth to know is that the machine has got *multiple* BIOS-es. The
> Video-card has one on the card itself, and the Hard-disk allso has one (in
> earlier days the Harddisk controller was a seperate card, just like the
> Video card currently can be). A network-card can allso contain its own BIOS
> (so that certain machine-configurations can boot for a network and do not
> need a local (hard-)disk)
Yes, that is an important complication.
> Although I do not exactly know the adress-ranges of the seperate BIOS-es I
> think that a quick Google will be able to tell you that.
I did try before my last posting and just tried again. If you know a search
string that does produce the right answers, please let me know.
Since it depends on the machine, it seems that the information would be
in the documentation of the machine, if it is anywhere.
> Though one interresting addres is F000:FFF0, which is the hardware-reset
> address of where the processor will go to after being powered-up, or when
> its reset-line is pulled down.
This suggests that what I should do is the following:
DEBUG
d F000:FFF0
and read in F000:FFF0 the address of the place in the BIOS where the CPU
jumps to next. If so, it won't necessarily tell me the range occupied by
the BIOS, but it will get me started in disassembling the BIOS.
What is the total amount of memory that a BIOS (with all its component BIOSes)
on a given machine will normally occupy? That will give some impression
of how much time will have to be spent in the debugger.
> (1) Let M be a machine. Then the location of the BIOS of M is a function
> of M. Denote that function F(M).
> (2) F is not a constant function on the set of all machines.
>
> If that is what you mean, then the problem of discovering F(M) for a given
> machine M remains, but see below.
Nope. The available space and location of the mentioned BIOS-es are
constants. It does not depend on the machine (although some BIOS-es do not
need to be present. Think of the network-card)
[Snip]
> > Although I do not exactly know the adress-ranges of the seperate BIOS-es
I
> > think that a quick Google will be able to tell you that.
>
> I did try before my last posting and just tried again. If you know a
search
> string that does produce the right answers, please let me know.
I just did a Google for "dos bios memory", and got a usable one in
http://www.frontiernet.net/~fys/rombios.htm
> > Though one interresting addres is F000:FFF0, which is the hardware-reset
> > address of where the processor will go to after being powered-up, or
when
> > its reset-line is pulled down.
>
> This suggests that what I should do is the following:
> DEBUG
> d F000:FFF0
Allmost. Its code that is there, so the command should be "u".
> What is the total amount of memory that a BIOS (with all its component
BIOSes)
> on a given machine will normally occupy? That will give some impression
> of how much time will have to be spent in the debugger.
It depends. But the normal system-BIOS ranges from F000:0000 to F000:FFFF
(64 KB). It does however not mean that all that space actually contains
code and related data.
Regards,
Rudy Wieser
> What is the total amount of memory that a BIOS
Minimum 64k (PC XT, f0000-fffffh). Typical 128k (PC AT, e0000-fffffh).
Could be upto 244k (see DELL link below). Absolute maximum (without special
electronic "tricks") is 384k, i.e., 0a0000h to 0fffffh.
> (with all its component BIOSes)
PC's have a system BIOS. The system BIOS is called BIOS. Technically PC's
also have a Video BIOS, a harddisk BIOS, an optional network card BIOS, etc.
Originally, these were all on external cards which is why they are separate
from the system BIOS. It can be confusing to refer to these other
("component") BIOSes as BIOS. Typically, people will call them "Video ROM"
or "HD ROM", etc. Anyway, I think the two links below might help.
This link has an upper memory area map which summarizes for what each 16K
memory block between 640K and 1M are typically used:
http://www.tvdsb.on.ca/banting/cicp/hardware/PCGuide/ref/ram/uma-c.html
This link has an upper memory area map for some older, but real DELL PC's.
It should give you an idea about some of variations a real PC might have:
http://support.ap.dell.com/support/edocs/dta/_hannbl/00000020.htm
Rod Pemberton
> PC's have a system BIOS. The system BIOS is called BIOS. Technically PC's
> also have a Video BIOS, a harddisk BIOS, an optional network card BIOS, etc.
> Originally, these were all on external cards which is why they are separate
> from the system BIOS. It can be confusing to refer to these other
> ("component") BIOSes as BIOS. Typically, people will call them "Video ROM"
> or "HD ROM", etc. Anyway, I think the two links below might help.
Thanks. The first link helps a lot. My browser can't handle the second
one for some reason, but I'll look at it on a library computer.
To locate the optional ROMs, you can scan 0xc0000 to 0xf0000 on 2k
boundaries for an 0xAA55 signature (0x55 0xAA - like the MBR). You'll also
have the (non-optional) system ROM at 0xf0000.
Rod Pemberton
> To locate the optional ROMs, you can scan 0xc0000 to 0xf0000 on 2k
> boundaries for an 0xAA55 signature (0x55 0xAA - like the MBR). You'll also
> have the (non-optional) system ROM at 0xf0000.
Thanks, that's a very helpful suggestion.
If I know exactly where a card is plugged in (e.g. which PCI slot), is
that information visible anywhere in the BIOS for the card? If so, one can
also scan for that. It may not be at a particular place in the BIOS but it
might have a particular way of being represented in the machine code.
I googled
pci bus assembly language
and was lead to
http://www.rdrop.com/~cary/html/device_driver.html
which has some programs that might be relevant. Then I remembered that
under Linux there is information about the PCI bus in the proc filesystem,
specifically in /proc/pci, which has entries such as:
PCI devices found:
Bus 0, device 0, function 0:
Host bridge: Intel Corporation 440BX/ZX - 82443BX/ZX Host bridge (rev 3).
Master Capable. Latency=32.
Prefetchable 32 bit memory at 0xf4000000 [0xf7ffffff].
Bus 0, device 1, function 0:
PCI bridge: Intel Corporation 440BX/ZX - 82443BX/ZX AGP bridge (rev 3).
Master Capable. Latency=32. Min Gnt=140.
I'm not sure whether these memory addresses are ROM BIOS addresses.
For the following:
Bus 1, device 0, function 0: VGA compatible controller:
Neomagic Corporation NM2360 [MagicMedia 256ZX] (rev 0).
IRQ 11. Master Capable. Latency=32. Min Gnt=16.Max Lat=255.
Prefetchable 32 bit memory at 0xfa000000 [0xfbffffff].
the address given (I don't know why one address is in brackets and I'm
ignoring it) doesn't seem to be consistent with the VGA addresses in
upper memory given on the page:
http://www.tvdsb.on.ca/banting/cicp/hardware/PCGuide/ref/ram/uma-c.html
The following device looks like a sound card, but I'm not sure.
Bus 1, device 0, function 1: Multimedia audio controller:
Neomagic Corporation NM2360 [MagicMedia 256ZX Audio] (rev 0).
IRQ 5. Prefetchable 32 bit memory at 0xf9800000 [0xf9ffffff].
Non-prefetchable 32 bit memory at 0xfda00000 [0xfdafffff].
The machine I'm interested in doing all this disassembly on (not the
one from which I got the above information) has both Linux and FREEDOS
on it. So, I can also look at /proc/pci on that machine, but I'm not sure
that it is giving me information about the BIOS for the device.
I'll look at the Borland Code from the first site I mentioned and see what
clues it might offer.
Most BIOSes support the DMI (draft 1993 , 2.0 rel. 1996 or so) or SMBIOS
(draft 2.0 1995, 2.2 rel. 1998 or so ) corporate PC management
specifications. These provide specific information on PC hardware. This
link has a number of programs which will display the information.
http://www.nongnu.org/dmidecode/
You can read about, and download, some PC industry BIOS standards here:
http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
Rod Pemberton
> Allan Adler <a...@nestle.csail.mit.edu> writes:
> > If I know exactly where a card is plugged in (e.g. which PCI slot), is
> > that information visible anywhere in the BIOS for the card? If so, one
> > can also scan for that. It may not be at a particular place in the BIOS but
> > it might have a particular way of being represented in the machine code.
> >
>
> Most BIOSes support the DMI (draft 1993 , 2.0 rel. 1996 or so) or SMBIOS
> (draft 2.0 1995, 2.2 rel. 1998 or so ) corporate PC management
> specifications. These provide specific information on PC hardware. This
> link has a number of programs which will display the information.
> http://www.nongnu.org/dmidecode/
>
> You can read about, and download, some PC industry BIOS standards here:
> http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
Thanks for these pointers. I'll try to make sense of them. My browser doesn't
display either correctly for some reason, but I'll try with the library's
computers. I did manage to download a version of libsmbios and chose an old
one for size and greater probability of compatibility with my system. I
tried just typing make but it complained endlessly.
> I am interested in disassembling boot sectors and BIOSes. DEBUG isn't
> good for that. Frank Gilluwe wrote a program called Sourcer that is
> better for this, but it doesn't seem to be available. Also, it isn't
> free. Is there anything comparable running under FREEDOS?
By running programs from the book, The Undocumented PC, I found that the
BIOS on the machine I'm interested in is an Award BIOS from 29-May-97.
I don't know if that helps.
I also haven't forgotten about Rod's suggestions about DMI and SMBIOS, but
I'm not yet capable of following up on them.
You just need a program that saves memory to a file. I don't know of any
off the top of my head. I usually use a program I wrote for DOS. There
should be programs for Linux and Windows which do this since the BIOS is
below 1Mb. Most OSes map this memory physically since they need fixed memory
locations to program devices and call the BIOS or video BIOS.
> Once I have that, I'll write a C program under Linux or use
> dd to write that region to a file. Once that is done, I can start
searching it
> and generally studying it (e.g. disassembling it). Is there any legal
reason
> why I can't also put a copy of the file on a website in case someone here
> thinks he can analyze it, disassemble the various BIOSes, etc.? (BIOSes
are
> often copyrighted).
>
The only issue I'm coming up with at the moment is copyright. IIRC, there
is nothing illegal with disassembly for your own personal use in the USA.
Although unlikely, posting where numerous others have access might expose
you to claims of distributing copyrighted material. You have the right to
modify your own BIOS. The question is: "Does fair use allow posting of
copyrighted material so others can help you modify your own BIOS?" I don't
have an answer to that. But, BIOS modification and hacking sites usually
have disassembled copyrighted material displayed... If you need help,
you're probably be better off via private or (somewhat) anonymous
communications, i.e., join some forum that deals with BIOS disassembly, IRC,
email, etc. Except for the rare use of a telephone and mail, which I won't
give out here, I haven't used a form of private communication for years.
So, although I'd like to help with the disassembly, I have to say please
don't ask me. I know this contradicts my previous offer to disassemble the
bootloader, but according to you, the binary code was the result of GPL'd or
LGPL'd open source material.
> I also haven't forgotten about Rod's suggestions about DMI and SMBIOS, but
> I'm not yet capable of following up on them.
Dmidecode compiles for Linux etc. For DOS, you could try ctbios or ctpci.
The program outputs as German with some English.
Look for ctbios15.zip and ctpci330.zip here:
http://www.heise.de/ct/ftp/ctsi.shtml
You can get altavista to do a poor translation of the page to English to
read about the other programs:
http://babelfish.altavista.com/babelfish/tr
Rod Pemberton
Sorry about the delay. I forgot to post this. This link has a disassembly
of LILO:
http://www.geocities.com/thestarman3/asm/mbr/LILOmbr.htm
It might help to look at it, if you haven't disassembled it already...
Rod Pemberton
> You just need a program that saves memory to a file. I don't know of any
> off the top of my head. I usually use a program I wrote for DOS. There
> should be programs for Linux and Windows which do this since the BIOS is
> below 1Mb. Most OSes map this memory physically since they need fixed memory
> locations to program devices and call the BIOS or video BIOS.
It's probably good for me to try to do it with an assembly language
program, since I want to get better at doing that. I'll think about
this tomorrow.
> Dmidecode compiles for Linux etc. For DOS, you could try ctbios or ctpci.
> The program outputs as German with some English.
> Look for ctbios15.zip and ctpci330.zip here:
> http://www.heise.de/ct/ftp/ctsi.shtml
> You can get altavista to do a poor translation of the page to English to
> read about the other programs:
> http://babelfish.altavista.com/babelfish/tr
I just downloaded ctbios15.zip and ctpci330.zip. Ich kann Deutsch lesen.
Vielen Danke!
> > I also haven't forgotten about Rod's suggestions about DMI and SMBIOS, but
> > I'm not yet capable of following up on them.
>
> Dmidecode compiles for Linux etc. For DOS, you could try ctbios or ctpci.
Nope. With minimal patches I built `dmidecode' using DJGPP a few days
ago. :-)
It'd be nice if someone could get them into the DJGPP archives on
delorie.com.
I did 2.8 and 2.9 easily for both DJGPP and OpenWatcom. I submitted patches
for 2.8 to Jean Delvare on Savannah a while back:
http://savannah.nongnu.org/bugs/?func=detailitem&item_id=16114#comments
He couldn't seem to understand how to integrate them. I checked for a
response from him for 6 months, but nothing. Then, at the beginning of
Sept. 2006, a response from him date 5/13/06 appeared out of nowhere...
What an blankety-blank. I found his response to be disrepectful and
arrogant considering I had done the work for him and went through _much_
trouble trying to find a way to get the information to him. Anyway, maybe
you can get "Khali" to add your changes if you prepare nice simple patches
for the idiot to merge... I thought I had mentioned this on
comp.os.msdos.djgpp, but it appears it was this NG. I've listed what I did
to my copy 2.9 below for comparison. I think there was just a trivial
change to what I listed on Savannah for 2.8.
Rod Pemberton
1) added missing getopt files, from DJGPP's make or many other DJGPP
utils...
getopt.h
getopt.c
getopt1.c
gettext.h
1a) fix bug line 695 of getopt.c
if (opterr) missing braces around if-else
1b) fix bug line 108 of getopt.h no args
extern int getopt (int argc, char *const *argv, const char *optstring);
2) changed global #include <getopt.h> to local "getopt.h" in files:
biosdecode.c
dmiopt.c
ownership.c
vpdopt.c
#if defined( __DJGPP__ ) || defined( __WATCOMC__ )
#include "getopt.h"
#else
#include <getopt.h>
#endif
2a) replace #include <strings.h> with #include <string.h>
and add Watcom section in files:
dmiopt.c
vpdopt.c
//#include <strings.h>
#include <strings.h>
#ifdef __WATCOMC__
#define strcasecmp stricmp
#endif
2b) comment out illegal line in fjkeyinf_decode() in biosdecode.c
// (void) len;
3) changed config.h #ifndef __BEOS__
#if !defined( __BEOS__ ) && !defined( __DJGPP__ ) && !defined( __WATCOMC__ )
4) for 2.8, removed for 2.9 .RP
5) changed util.c #ifndef USE_MMAP
#if !defined( USE_MMAP ) && !defined( __DJGPP__ ) && !defined( __WATCOMC__ )
6) changed util.c mem_chunk
void *mem_chunk(size_t base, size_t len, const char *devmem)
{
#ifndef __DJGPP__
/* original mem_chunk() function goes here */
#else /* DJGPP */
#include <go32.h>
#include <dpmi.h>
#include <sys\nearptr.h>
unsigned long CS_base;
void *p;
if((p=malloc(len))==NULL)
{
perror("malloc");
return NULL;
}
__dpmi_get_segment_base_address(_my_cs(), &CS_base);
__djgpp_nearptr_enable();
memcpy(p,(unsigned char *)base-CS_base,len);
return p;
#endif /* DJGPP */
}
7) add to rules and rules for getopt.o and getopt1.o to makefile
8) convert filenames to 8.3 and #included filenames to 8.3
> It'd be nice if someone could get them into the DJGPP archives on
> delorie.com.
I'm working on that, of course. But it's my first port. So be a little
patient, please.
> I did 2.8 and 2.9 easily for both DJGPP and OpenWatcom. I submitted patches
> for 2.8 to Jean Delvare on Savannah a while back:
> http://savannah.nongnu.org/bugs/?func=detailitem&item_id=16114#comments
Thanks for this link. Maybe I'll find some additional ideas there.
> "Rod Pemberton" <do_no...@nohavenot.cmm> writes:
>
> > You just need a program that saves memory to a file. I don't know of any
> > off the top of my head. I usually use a program I wrote for DOS. There
> > should be programs for Linux and Windows which do this since the BIOS is
> > below 1Mb.Most OSes map this memory physically since they need fixed memory
> > locations to program devices and call the BIOS or video BIOS.
>
> It's probably good for me to try to do it with an assembly language
> program, since I want to get better at doing that. I'll think about
> this tomorrow.
I've made a little progress: I just have to use interrupt 13h with AH=3
to write from memory to the floppy. I know one way to carry this out without
knowing any more than I do now about the use of this interrupt. I'll do
more work on that approach tomorrow. The interrupt will have to be called
repeatedly because the copy isn't allowed to cross 64K boundaries and also
because the number of sectors copied apparently can't exceed 36. It's not
difficult, but I don't want to rush. Also, to check the copy, I'll use
interrupt 13h with AH=4.
Here are some questions that might facilitate a better program than the one
I have in mind: the location ES:BX says where to copy from in memory
and various registers say how many sectors to copy, and on which track
and sector to start. Suppose I tell it to copy m sectors starting with
sector n on track t (1 <= n <= 36, 0 <= t <= 79). There are 36 sectors
per track and 80 tracks on a 3.5" 1.44 MB floppy, which is the floppy I'm
working with. By specification, the first sector it writes to will be sector
n on track t. I would guess (but I don't really know) that the next sector
it writes to will be sector n+1 on track t, at least if n is 35 or less. But
if n is 36, will the next sector written be 1 on track t or will it be sector
1 on track t+1 or will it return an error or what?
I've also made a little progress in anticipating how to study the memory once
it gets copied. In addition to the AA55h's to search for, which are supposed
to be the first two bytes of a ROM, the third byte gives the size of the ROM
and the 4th byte is the first instruction. Knowing the size of each ROM is
very helpful. Also, the sum of all the bytes in the ROM should be 0. My
source for this information is the chapter of The Undocumented PC that talks
about how to design your own adapter card.
?!?! Basically, the same list of changes I posted after my signature...
Didn't read to end?
Rod Pemberton
> > > for 2.8 to Jean Delvare on Savannah a while back:
> > > http://savannah.nongnu.org/bugs/?func=detailitem&item_id=16114#comments
> >
> > Thanks for this link. Maybe I'll find some additional ideas there.
> >
>
> ?!?! Basically, the same list of changes I posted after my signature...
> Didn't read to end?
Yes, I read to end and wondered why you attached redundant stuff. ;-)
> I've made a little progress: I just have to use interrupt 13h with AH=3
> to write from memory to the floppy.
I wrote a boot sector program to copy all of the upper memory area to
the floppy. The floppy was prepared by filling it entirely with 0's
before writing the program to the boot sector. The program seems to
work but also seems not to work. Because of my great inexperience and
fallibility at writing assembler programs, I scavenged some of the
assembly language subroutines from the lilo-22.8 distribution. The
assembler I use is as86 under Linux and also the loader ld86. I used the
C program write.c from Krishnakumar's online article, "How to write your
own toy OS" (with minor modifications, such as filling up the array
boot_buf with 0's before doing anything else) to write the boot sector
program to the boot sector of the floppy.
My boot sector program copies the upper memory area (0xA0000 to 0xFFFFF)
to sectors 1-16 of tracks 1-48 on side 0 (i.e. head = 0) of the floppy.
The code is below. The routine abc (based on part I of Krishnakumar's
article) writes 128 a's, b's and c's to the top of the screen by writing
directly to the screen buffer. The subroutine hello calls the lilo routine
say to write "Hello, World!" to the screen. The routine wout is also from
lilo and writes the contents of AX in a form recognizable as hex digits.
The routine dsk_do_rw is a wrapper for calling int 13h and is also scavenged
from lilo. The various calls to wout let me check what the contents are of
the es register, the return codes from the writes to the floppy and the lower
byte of the flags register. Based on the wout output, it appears that the
program works. From the Linux partition of the same machine, I executed
dd if=/dev/fd0 of=flop bs=512
to capture the floppy and then
od -bc flop > dump
to get a dump of the file flop (I might prefer hex, but octal is ok).
One nice feature of the dump is that if there are a lot of repetitions
of the same line, only the first is shown. From the file dump I can tell
that 48 writes were indeed done and seem to be in the right places on
the floppy (based on a guess as to how dd copies the sectors of the
floppy to the file flop). So, in that sense, the program seems to work.
The sense in which the program doesn't seem to work is that, without
exception, the bytes written are all FFh. It is inconceivable that
the upper memory area consists entirely of FF's since the screen itself
has various characters on it, including the 128 a's, b's and c's, and their
ascii codes should be part of upper memory.
In an earlier version of the program, I accidentally had the line
add ax,#0x400
instead of
add ax,#0x200
and this did result in the last 24 writes producing stuff other than FFh,
but I don't think that counts. The upper memory area is from 0xA0000
to 0xFFFFF.
Here is the boot sector program. If anyone has any idea what is wrong with
the program, please let me know. The assembler is as86 and the linker is
ld86.
entry start
start:
call abc
call hello
call capuma
jmp loop1
capuma:
mov bp,#48
mov ax,#0xa000
mov cx,#0x101
capuma1:
push ax
mov es,ax
push es
pop ax
call wout
mov bx,#0
mov dx,#0
mov ax,#0x310
call dsk_do_rw
call wout
lahf
call wout
pop ax
add ax,#0x200
inc ch
dec bp
jnz capuma1
exit_capuma:
ret
loop1: jmp loop1
> I wrote a boot sector program to copy all of the upper memory area to
> the floppy. The floppy was prepared by filling it entirely with 0's
> before writing the program to the boot sector.
What do you mean by "entirely"? And what tool did you use to do
this? A 1.44MB dos floppy has 18 sectors per track (you mention
below that you are writing 16) and I think dos accesses the disk
on alternate sides/heads before moving to the next track (this is
after all a DOS newsgroup). The track/sector layout you are
using might be opaque to dd?
> The program seems to
> work but also seems not to work. Because of my great inexperience and
> fallibility at writing assembler programs, I scavenged some of the
> assembly language subroutines from the lilo-22.8 distribution. The
> assembler I use is as86 under Linux and also the loader ld86. I used the
> C program write.c from Krishnakumar's online article, "How to write your
> own toy OS" (with minor modifications, such as filling up the array
> boot_buf with 0's before doing anything else) to write the boot sector
> program to the boot sector of the floppy.
>
> My boot sector program copies the upper memory area (0xA0000 to 0xFFFFF)
> to sectors 1-16 of tracks 1-48 on side 0 (i.e. head = 0) of the floppy.
Just so long as dd understands this...
> The code is below. The routine abc (based on part I of Krishnakumar's
> article) writes 128 a's, b's and c's to the top of the screen by writing
> directly to the screen buffer. The subroutine hello calls the lilo routine
> say to write "Hello, World!" to the screen. The routine wout is also from
> lilo and writes the contents of AX in a form recognizable as hex digits.
> The routine dsk_do_rw is a wrapper for calling int 13h and is also scavenged
> from lilo. The various calls to wout let me check what the contents are of
> the es register, the return codes from the writes to the floppy and the lower
> byte of the flags register. Based on the wout output, it appears that the
> program works. From the Linux partition of the same machine, I executed
> dd if=/dev/fd0 of=flop bs=512
> to capture the floppy and then
> od -bc flop > dump
> to get a dump of the file flop (I might prefer hex, but octal is ok).
You could use od -x of course (but I guess you know this :)
> One nice feature of the dump is that if there are a lot of repetitions
> of the same line, only the first is shown. From the file dump I can tell
> that 48 writes were indeed done and seem to be in the right places on
> the floppy (based on a guess as to how dd copies the sectors of the
> floppy to the file flop). So, in that sense, the program seems to work.
>
> The sense in which the program doesn't seem to work is that, without
> exception, the bytes written are all FFh. It is inconceivable that
> the upper memory area consists entirely of FF's since the screen itself
> has various characters on it, including the 128 a's, b's and c's, and their
> ascii codes should be part of upper memory.
All those FFs look to me symptomatic of "error reading disk"...
> In an earlier version of the program, I accidentally had the line
> add ax,#0x400
> instead of
> add ax,#0x200
> and this did result in the last 24 writes producing stuff other than FFh,
> but I don't think that counts.
On the contrary; those last 24 writes would have been of data
starting at 0x00000 as the address wrapped beyond 1MB -- the fact
that you are seeing non-FF bytes there (the first of these writes
would have been the start of the interrupt vector table) suggests
that your program is operating "correctly".
> The upper memory area is from 0xA0000
> to 0xFFFFF.
>
> Here is the boot sector program. If anyone has any idea what is wrong with
> the program, please let me know. The assembler is as86 and the linker is
> ld86.
>
> entry start
> start:
> call abc
> call hello
> call capuma
> jmp loop1
> capuma:
> mov bp,#48 ;write 48 tracks (loop count)
> mov ax,#0xa000 ;starting segment address
> mov cx,#0x101 ;track (cyl) 1, sector 1
> capuma1:
> push ax
> mov es,ax ;ES -> segment address to read
> push es ; this and the 'pop ax' below
> pop ax ; are redundant?
> call wout
> mov bx,#0 ;BX -> offset address to read
> mov dx,#0 ;head 0, drive 0
> mov ax,#0x310 ;write 10h sectors (=2000h bytes)
> call dsk_do_rw
> call wout
> lahf
> call wout
> pop ax
> add ax,#0x200 ;advance 200h paras
> inc ch ;next track (cyl)
> dec bp ;decr loop counter
> jnz capuma1
> exit_capuma:
> ret
> loop1: jmp loop1
The logic and all numbers seem reasonable, though without seeing
what dsk_do_rw actually does it's impossible to say that the code
is correct. Provided that this routine (and 'wout' too) preserve
all the important registers, it should be OK.
Is it important that you record the contents of 0xa0000 to 0xfffff
at boot time? If not it would be a whole lot easier to write a
small prog in (e.g.) C to dump the said contents to a file. Off
the top of my head (hence untried/untested) and with zero error
checking (for brevity):
#define BLOCKSIZ 0x4000
int main(void)
{
unsigned long seg;
int fh = creat("somefile", S_IREAD | S_IWRITE);
for (seg = 0xa000L; seg < 0x10000L; seg += BLOCKSIZ)
{
char far *p = MK_FP((unsigned)seg, 0);
write(fh, p, BLOCKSIZ);
}
close(fh);
return 0;
}
Pete
--
"We have not inherited the earth from our ancestors,
we have borrowed it from our descendants."
> In article <y93d4u7...@nestle.csail.mit.edu>
> a...@nestle.csail.mit.edu "Allan Adler" writes:
>
> > I wrote a boot sector program to copy all of the upper memory area to
> > the floppy. The floppy was prepared by filling it entirely with 0's
> > before writing the program to the boot sector.
>
> What do you mean by "entirely"? And what tool did you use to do
> this?
I wrote a C program in Linux that writes 18 x 80 x 2 x 512 consecutive 0's
to /dev/fd0. Executing
dd if=/dev/fd0 of=flop bs=512
produces a file flop with 18 x 80 x 2 x 512 bytes, all of them null.
> A 1.44MB dos floppy has 18 sectors per track (you mention
> below that you are writing 16) and I think dos accesses the disk
> on alternate sides/heads before moving to the next track (this is
> after all a DOS newsgroup). The track/sector layout you are
> using might be opaque to dd?
I originally wrote 32 sectors per track for 24 tracks and that worked as
well as 16 sectors for 48 tracks. However, precisely because I don't know
how dd understands the sides/heads, tracks and sectors, I decided that
16 sectors, starting at sector 1 with head 0 for tracks 1 through 48
would be safer. Some earlier experiments seem to show that even if one writes,
say, 8 tracks starting at track 15 of track 1 with head 1, the write will
be done and will wrap to the next track but the return code in AH will be 4,
indicating sector not found.
I'm glad to learn that DOS accesses the full track on both sides/heads before
going to the next track. I didn't know that and don't know how Linux does it.
> The boot sector program copies the upper memory area (0xA0000 to 0xFFFFF)
> > to sectors 1-16 of tracks 1-48 on side 0 (i.e. head = 0) of the floppy.
>
> Just so long as dd understands this...
I'm still trying to find out what dd understands. But at the very least it
should send sectors in their entirety. It is also likely that sectors 1-18
on a given side of a given track will be consecutive. It's also likely
that track n side 0 will be copied before track n+1 side 0. And that's all
I'm assuming.
> You could use od -x of course (but I guess you know this :)
I should have known it. Thanks.
> > The sense in which the program doesn't seem to work is that, without
> > exception, the bytes written are all FFh. It is inconceivable that
> > the upper memory area consists entirely of FF's since the screen itself
> > has various characters on it, including the 128 a's, b's and c's, and their
> > ascii codes should be part of upper memory.
>
> All those FFs look to me symptomatic of "error reading disk"...
I'm confused. It is writing to the disk, not reading it. The return code
in AX is 0x0010, which indicates that the write was successful and 0x10
sectors were written. The content of AH after lahf is 46, which shows the
carry flag is 0, another indication that the write was successful.
I don't think there should be any obstacle to reading memory when I've
already demonstrated that I can write to 0xb800:0000, but it is at least
conceivable that there is some problem reading memory.
> > In an earlier version of the program, I accidentally had the line
> > add ax,#0x400
> > instead of
> > add ax,#0x200
> > and this did result in the last 24 writes producing stuff other than FFh,
> > but I don't think that counts.
>
> On the contrary; those last 24 writes would have been of data
> starting at 0x00000 as the address wrapped beyond 1MB -- the fact
> that you are seeing non-FF bytes there (the first of these writes
> would have been the start of the interrupt vector table) suggests
> that your program is operating "correctly".
Thanks for explaining that.
> > Here is the boot sector program. If anyone has any idea what is wrong with
> > the program, please let me know. The assembler is as86 and the linker is
> > ld86.
[snip]
> The logic and all numbers seem reasonable, though without seeing
> what dsk_do_rw actually does it's impossible to say that the code
> is correct. Provided that this routine (and 'wout' too) preserve
> all the important registers, it should be OK.
Thanks very much for looking over my code.
Here is the code for dsk_do_rw and wout.
dsk_do_rw: or ah,#0 ; 0=read, 1=write, 2=read-only test
dsk_wrflag equ *-1 ; byte data area is the immediate
dsk_do_int13:
push bp
mov bp,#5 ;number of tries
dsk_do_int13a: pusha
int 0x13
jnc dsk_io_exit
dec bp ;does not affect the carry
jz dsk_io_exit
xor ax,ax ;reset disk controllers
int 0x13
popa
dec bp
jmp dsk_do_int13a
dsk_io_exit: mov bp,sp ;do not touch any flags
lea sp,(bp+16) ;an ADD would touch flags
pop bp ;do not touch any flags
ret
; write the word in AX in hex
wout: xchg ah,al
call bout
xchg ah,al
call bout
ret
; write the byte in AL in hex
bout: push ax
push cx
push ax
mov cx,#0x204
shr al,cl
bout1: add al,#'0
cmp al,#'9
jbe bout2
add al,#'A-'0-10
bout2: call putc
dec ch
jz bout4
pop ax
and al,#0xF
jmp bout1
bout4: pop cx
pop ax
ret
; write the character in AL in teletype mode
; all registers are preserved
putc: push bx
push ax
mov ah,#14
xor bh,bh
cmp al,#10 ;test for new-line
jne putc2
mov al,#13 ;get <cr>
int 0x10
mov al,#10
putc2: int 0x10
pop ax
pop bx
ret
> Is it important that you record the contents of 0xa0000 to 0xfffff
> at boot time? If not it would be a whole lot easier to write a
> small prog in (e.g.) C to dump the said contents to a file. Off
> the top of my head (hence untried/untested) and with zero error
> checking (for brevity):
Of course ROM won't be affected by anything that the operating systems will
do, so in that sense it won't matter. My goal is to examine all the BIOSes
for the system and all the devices. By capturing the upper memory area to
a file, I can write programs to locate and study them. Gilluwe's chapter on
designing adapters is pretty helpful, although there are a few details I
don't fully understand. I considered writing a C program to look at the
memory area but I am afraid that Linux will either refuse to let me do it
and cause a segmentation fault or else will show me something else and let
me think I'm looking at the memory areas. I would try your C program out
under FREEDOS but I have never succeeded in installing djgpp under FREEDOS
and have never succeeded in installing it as a cross-compiler from Linux to
DOS. I'm interested in accomplishing both of these some time, but at the
moment I find it conceptually simpler and more educational to try to do it
with a boot sector program. Merely having done as much as I've done with
this is far beyond anything I've been able to do before. Krishnakumar's
article changed my life.
I just tried another experiment. I wrote a C program under Linux to write
256 blue D's to sector 1 of track 1 (a blue D is 0x44 0x1F) after zapping
the diskette with 0's. Then I wrote a boot sector program that calls
dsk_do_rw with AH=2 to write that blue D sector to 0xb800:0300. If it
works properly, I should see 256 blue D's on the screen when I boot from
this floppy. Well, I just tried it and it didn't work. Anyway, the basic
idea is that this kind of experiment should make it possible to test whether
I can read from diskette to memory (although I don't really doubt this) and
also, by exploiting writes to the screen buffer to explore the geometry
of the diskette, as seen from /dev/fd0. I'll keep at it. The simplest
thing to do is to write enough blue D's to the diskette so that some of them
do wind up at sector 1 of track 1 of side 0, wherever that is from the
point of view of /dev/fd0. Then I can start cutting back on them until the
blue D's stop showing up. Then I'll know what bytes of /dev/fd0 are occupied
by sector 1, track 1, side 0. I just modified the program so that dx=0x100.
This caused a smiley face to appear in the upper left corner of the screen
at bootup and the speaker to play a note which didn't stop until I rebooted.
Another thing to try with the attempts to capture the upper memory area is
to follow each write to the diskette with a call to dsk_do_rw with AH=4,
which verifies the write.
> pe...@nospam.demon.co.uk writes:
>
> > In article <y93d4u7...@nestle.csail.mit.edu>
> > a...@nestle.csail.mit.edu "Allan Adler" writes:
[..]
> > > The sense in which the program doesn't seem to work is that, without
> > > exception, the bytes written are all FFh. It is inconceivable that
> > > the upper memory area consists entirely of FF's since the screen itself
> > > has various characters on it, including the 128 a's, b's and c's, and their
> > > ascii codes should be part of upper memory.
> >
> > All those FFs look to me symptomatic of "error reading disk"...
>
> I'm confused. It is writing to the disk, not reading it.
Sorry -- brain fade on my part; that should have read "error
reading memory" or "error writing disk".
> The return code
> in AX is 0x0010, which indicates that the write was successful and 0x10
> sectors were written. The content of AH after lahf is 46, which shows the
> carry flag is 0, another indication that the write was successful.
Which sort of narrows the diagnosis...
> I don't think there should be any obstacle to reading memory when I've
> already demonstrated that I can write to 0xb800:0000, but it is at least
> conceivable that there is some problem reading memory.
... and I have no idea what that problem might be! Clutching at
straws, could it be that somehow the various ROMs have not yet
been mapped into their "normal" address space at this point in
the boot sequence and that your prog is therefore reading fresh
air? I have no idea -- perhaps if you ask in comp.lang.asm.x86
someone there could put us both straight!
> > > Here is the boot sector program. If anyone has any idea what is wrong with
> > > the program, please let me know. The assembler is as86 and the linker is
> > > ld86.
> [snip]
>
> > The logic and all numbers seem reasonable, though without seeing
> > what dsk_do_rw actually does it's impossible to say that the code
> > is correct. Provided that this routine (and 'wout' too) preserve
> > all the important registers, it should be OK.
>
> Thanks very much for looking over my code.
A pleasure.
> Here is the code for dsk_do_rw and wout.
>
> dsk_do_rw: or ah,#0 ; 0=read, 1=write, 2=read-only test
This doesn't seem to do much (except perhaps condition the flags)
> dsk_wrflag equ *-1 ; byte data area is the immediate
> dsk_do_int13:
> push bp
> mov bp,#5 ;number of tries
> dsk_do_int13a: pusha
> int 0x13
> jnc dsk_io_exit
> dec bp ;does not affect the carry
> jz dsk_io_exit
> xor ax,ax ;reset disk controllers
> int 0x13
> popa
> dec bp
> jmp dsk_do_int13a
jnz dsk_do_int13a ?
> dsk_io_exit: mov bp,sp ;do not touch any flags
> lea sp,(bp+16) ;an ADD would touch flags
I assume that +16 is correct...
If it helps, I could compile/fix/compile/etc that C prog with one
of the C compilers I have here, and email the exe to you? With
any luck it should run under FreeDOS -- just say the word.
> I just tried another experiment. I wrote a C program under Linux to write
> 256 blue D's to sector 1 of track 1 (a blue D is 0x44 0x1F) after zapping
> the diskette with 0's. Then I wrote a boot sector program that calls
> dsk_do_rw with AH=2 to write that blue D sector to 0xb800:0300. If it
> works properly, I should see 256 blue D's on the screen when I boot from
> this floppy. Well, I just tried it and it didn't work.
:-(
> Anyway, the basic
> idea is that this kind of experiment should make it possible to test whether
> I can read from diskette to memory (although I don't really doubt this) and
> also, by exploiting writes to the screen buffer to explore the geometry
> of the diskette, as seen from /dev/fd0. I'll keep at it. The simplest
> thing to do is to write enough blue D's to the diskette so that some of them
> do wind up at sector 1 of track 1 of side 0, wherever that is from the
> point of view of /dev/fd0. Then I can start cutting back on them until the
> blue D's stop showing up. Then I'll know what bytes of /dev/fd0 are occupied
> by sector 1, track 1, side 0. I just modified the program so that dx=0x100.
> This caused a smiley face to appear in the upper left corner of the screen
> at bootup and the speaker to play a note which didn't stop until I rebooted.
Doesn't sound good...
> Another thing to try with the attempts to capture the upper memory area is
> to follow each write to the diskette with a call to dsk_do_rw with AH=4,
> which verifies the write.
It might also help to display the contents of ES and BX at the
start of each write to confirm that the address is being set up
correctly.
> In article <y93fxz2...@nestle.csail.mit.edu>
> > I don't think there should be any obstacle to reading memory when I've
> > already demonstrated that I can write to 0xb800:0000, but it is at least
> > conceivable that there is some problem reading memory.
>
> ... and I have no idea what that problem might be! Clutching at
> straws, could it be that somehow the various ROMs have not yet
> been mapped into their "normal" address space at this point in
> the boot sequence and that your prog is therefore reading fresh
> air? I have no idea -- perhaps if you ask in comp.lang.asm.x86
> someone there could put us both straight!
Thanks for the suggestions. Someone suggested to me that reading screen
memory is inherently flaky because video ram is more complex than ordinary
ram. So now I'm trying to inform myself about how this is so. But it is very
helpful that you think there is nothing wrong, in principle with my program.
Writing to screen memory with mov instructions works. And to a great extent
wout and say work to write to the screen. It is just the use of interrupt 13h
with AH=2 and AH=3 to write and read the screen that seems not to work. I'm
going to try to write a different program that uses interrupt 13h to write to
some other part of memory. I don't know where the RAM is located but it should
at least be safe to write and read the stack. So I can use int 13h with that.
To display it after that, I can write a loop that either uses wout or mov
instructions to write to the screen. It's a little more complicated but worth
a try.
I also tried to use dsk_do_rw to capture the entire first megabyte of
memory by writing to sectors 1-16 of each of tracks 1-64 on side 0
and then again on side 1. That seemed to result in the destruction of
the floppy, but that doesn't necessarily mean that the program caused it.
Anyway, I'm now fresh out of floppies and need to buy another box.
> > dsk_wrflag equ *-1 ; byte data area is the immediate
> > dsk_do_int13:
> > push bp
> > mov bp,#5 ;number of tries
> > dsk_do_int13a: pusha
> > int 0x13
> > jnc dsk_io_exit
> > dec bp ;does not affect the carry
> > jz dsk_io_exit
> > xor ax,ax ;reset disk controllers
> > int 0x13
> > popa
> > dec bp
> > jmp dsk_do_int13a
>
> jnz dsk_do_int13a ?
Since 5 is odd and we always reach this jump after two decrements, bp will
always be nonzero at this point. It is the jz dsk_io_exit whenever bp
becomes negative that is appropriate.
> > I would try your C program out
> > under FREEDOS but I have never succeeded in installing djgpp under FREEDOS
> > and have never succeeded in installing it as a cross-compiler from Linux to
> > DOS. I'm interested in accomplishing both of these some time, but at the
> > moment I find it conceptually simpler and more educational to try to do it
> > with a boot sector program. Merely having done as much as I've done with
> > this is far beyond anything I've been able to do before. Krishnakumar's
> > article changed my life.
>
> If it helps, I could compile/fix/compile/etc that C prog with one
> of the C compilers I have here, and email the exe to you? With
> any luck it should run under FreeDOS -- just say the word.
Thanks for the offer. I'll keep it in mind. Would you consider running
my program on your machine and tell me if you have similar problems?
You could either assemble it directly or include it in a C program.
> It might also help to display the contents of ES and BX at the
> start of each write to confirm that the address is being set up
> correctly.
Thanks for the suggestion. My program has the lines
push es
pop ax
call wout
to at least check the es. I could easily add lines to display bx as well.
> pe...@nospam.demon.co.uk writes:
[..]
> Thanks for the suggestions. Someone suggested to me that reading screen
> memory is inherently flaky because video ram is more complex than ordinary
> ram.
Really? It might be "slower", but I can't see how that makes it
"inherently flaky". I too would like enlightenment...
> So now I'm trying to inform myself about how this is so. But it is very
> helpful that you think there is nothing wrong, in principle with my program.
Not that it proves a lot, but I wrote the C program I mentioned
and after some debugging/fixing got it to create a file of the
mem from a0000 to fffff OK (in the sense that it was the correct
size and the bits I checked against a DEBUG inspection tallied).
So in principle there is not a problem reading upper memory.
[snip]
> I also tried to use dsk_do_rw to capture the entire first megabyte of
> memory by writing to sectors 1-16 of each of tracks 1-64 on side 0
> and then again on side 1. That seemed to result in the destruction of
> the floppy, but that doesn't necessarily mean that the program caused it.
Will the floppy not reformat? I can't believe that the media is
physically destroyed, so you might be able to find a util that
will rewrite the boot sector so that DOS FORMAT will recognise
it and allow a format. I don't know whether DISKCOPY would work
using another "good" floppy -- haven't tried it.
> Anyway, I'm now fresh out of floppies and need to buy another box.
:-( It would be worth your while getting hold of a decent disk
sector editor, so that you can revitalize "dead" disks with a
shiny new boot sector copied from a good diskette.
> > > dsk_do_int13a: pusha
> > > int 0x13
> > > jnc dsk_io_exit
> > > dec bp ;does not affect the carry
> > > jz dsk_io_exit
> > > xor ax,ax ;reset disk controllers
> > > int 0x13
> > > popa
> > > dec bp
> > > jmp dsk_do_int13a
> >
> > jnz dsk_do_int13a ?
>
> Since 5 is odd and we always reach this jump after two decrements, bp will
> always be nonzero at this point. It is the jz dsk_io_exit whenever bp
> becomes negative that is appropriate.
Indeed -- I didn't spot the double decrement per loop and the
exit further up.
> > If it helps, I could compile/fix/compile/etc that C prog with one
> > of the C compilers I have here, and email the exe to you? With
> > any luck it should run under FreeDOS -- just say the word.
>
> Thanks for the offer. I'll keep it in mind. Would you consider running
> my program on your machine and tell me if you have similar problems?
> You could either assemble it directly or include it in a C program.
I sort of did that, inasmuch as I too have a shortage of spare
floppies, so replaced dsk_do_rw with a routine that writes the
data to a previously opened file on a ramdisk. As you would
expect, this file was different from the one produced by the C
version but identical in the ROM areas. It proves at least that
reading memory is OK, so would conclude that the numbers in your
program are correct and that your problem must be in your int 13
writes or in dd's interpretation of what was written (or both!).
I suspect the latter, though.
This is a text-only group so I can't post binaries here, and the
source listings are rather too long for something that is likely
of little interest to others, so if you'd like me to email them
to you just let me know.
Pete
PS. did you check the _entire_ contents of your original written
sectors on the floppy? The first 64K or so in my dump files were
entirely FF bytes (graphics video ram).
> In article <y93sl30...@nestle.csail.mit.edu>
> a...@nestle.csail.mit.edu "Allan Adler" writes:
> > Thanks for the suggestions. Someone suggested to me that reading screen
> > memory is inherently flaky because video ram is more complex than ordinary
> > ram.
>
> Really? It might be "slower", but I can't see how that makes it
> "inherently flaky". I too would like enlightenment...
I'm still trying to inform myself about this. There have been different
kinds of chips used to implement video ram. Some, for example, are dual
ported, making it possible to read the chip at the same time one writes
to it. That doesn't sound like a convincing reason to me for it being
unreliable to simply read screen memory, but possibly another argument
is the fact that there is another int 13h function that will look at
a video ram location (maybe AH=7, I forget) and put the character in AH
and its attribute in AL. If one could simply and reliably read the video
ram, one could do essentially the same with a mov instruction and no
interrupt would be needed.
Once I'm able to read the upper memory, I can hopefully read the code
for interrupt 13h, AH=7 (?) and find out exactly how one can read
the video memory without using interrupts. But one thing at a time.
Anyway, the fact that this interrupt exists also shows that I should be
able to write a simple loop that will capture the screen and write it
to the floppy. Since you say you have been able to copy upper memory
using my program and yours, more or less, maybe you can tell whether the
screen, as depicted in the bytes starting with 0xb8000, really is
the way the screen looks.
> Not that it proves a lot, but I wrote the C program I mentioned
> and after some debugging/fixing got it to create a file of the
> mem from a0000 to fffff OK (in the sense that it was the correct
> size and the bits I checked against a DEBUG inspection tallied).
> So in principle there is not a problem reading upper memory.
I'll make another effort to implement either djgpp on FREEDOS or to
implement the djgpp cross-compiler to DOS under Linux. I realize I
could just use your executable, but it would be better if I can also
make modifications in the program and compile them.
> > I also tried to use dsk_do_rw to capture the entire first megabyte of
> > memory by writing to sectors 1-16 of each of tracks 1-64 on side 0
> > and then again on side 1. That seemed to result in the destruction of
> > the floppy, but that doesn't necessarily mean that the program caused it.
>
> Will the floppy not reformat? I can't believe that the media is
> physically destroyed, so you might be able to find a util that
> will rewrite the boot sector so that DOS FORMAT will recognise
> it and allow a format. I don't know whether DISKCOPY would work
> using another "good" floppy -- haven't tried it.
I haven't tried to reformat it and I haven't looked at the floppy under
FREEDOS yet. This isn't a problem with boot sectors. I wrote a C program
from Linux that wrote 2 x 18 x 80 x 512 consecutive 0's to /dev/fd0,
completely obliterating any structure that any operating system might
recognize. That was to clean the floppy so that whatever got copied by
my boot sector program would stand out clearly. My technique for reading
the floppy afterwards has been to use, from Linux,
dd if=/dev/fd0 of=flop bs=512
and this has worked consistently, until now. What happens now is that I
get an error message complaining that some sector is unreadable and aborting.
It is possible that this is just a bad sector and that dd doesn't know how
to deal with that. I'll ask about that on a Linux group. In any case, I
prefer to work with a perfect floppy, since there are already enough
variables to worry about in figuring out why my programs aren't working.
> > Anyway, I'm now fresh out of floppies and need to buy another box.
>
> :-( It would be worth your while getting hold of a decent disk
> sector editor, so that you can revitalize "dead" disks with a
> shiny new boot sector copied from a good diskette.
I use a variant of the C program write.c from Krishnakumar's article,
"Writing your own Toy OS", to write the boot sector programs to the floppy.
But I could just as easily use dd to capture the boot sector of a good
diskette and they modify the C program to write that to the bad diskette.
But I think there probably is something wrong with the diskette.
> > > > dec bp ;does not affect the carry
> > > > jz dsk_io_exit
> > > > xor ax,ax ;reset disk controllers
> > > > int 0x13
> > > > popa
> > > > dec bp
> > > > jmp dsk_do_int13a
Let me ask you this: why is it necessary to reset the disk controller between
calls to dsk_do_rw?
> > Would you consider running
> > my program on your machine and tell me if you have similar problems?
> > You could either assemble it directly or include it in a C program.
>
> I sort of did that, inasmuch as I too have a shortage of spare
> floppies, so replaced dsk_do_rw with a routine that writes the
> data to a previously opened file on a ramdisk. As you would
> expect, this file was different from the one produced by the C
> version but identical in the ROM areas. It proves at least that
> reading memory is OK, so would conclude that the numbers in your
> program are correct and that your problem must be in your int 13
> writes or in dd's interpretation of what was written (or both!).
> I suspect the latter, though.
I think dd is probably reading the diskette correctly. I don't know why
my program produced only FF's for upper memory, but it is conceivable that
it is wrong in a way that has side effects that affect the other reads
and writes.
Anyway, now that I have figured out other ways to do the screen capture
and to capture memory from 0xC0000 to 0xFFFFF and to write from the floppy
to the screen, I'll try again as soon as I have some more floppies to
destroy, which I expect will be today.
Incidentally, about 13 years ago, I was using a Sparc workstation that had
a floppy drive that could read floppies formatted for DOS. I used to use
it to copy files from my PC running Linux to and from the Sparc. When it
copied large files, say 800 kb or so, it used to destroy the diskette.
I thought there was something wrong with the floppy drive, but the sysadmin
looked into it and said it was a software problem. He declined to give details.
He just upgraded some software and the problem went away. But that suggests
that there might be some ways of writing diskette routines in assembly
language that can damage a floppy. That would be good to know about.
> This is a text-only group so I can't post binaries here, and the
> source listings are rather too long for something that is likely
> of little interest to others, so if you'd like me to email them
> to you just let me know.
Thanks, I'll keep it in mind. As long as I have some hope of getting
the bootsector programs to work, I prefer to do it that way. Also, it
will be better to install djgpp, both directly on FREEDOS and as a
cross compiler on Linux. Once I've gotten further with the bootsector
program, I'll probably start asking here about problems installing
djgpp.
> PS. did you check the _entire_ contents of your original written
> sectors on the floppy? The first 64K or so in my dump files were
> entirely FF bytes (graphics video ram).
Yes. What I did, under Linux, was to execute
od -bc
on the file flop produced by dd, as described above. As long as the
lines produced by the octal dump are different, od will list them, but
when lines repeat, it doesn't list the repeats. For example, when I filled
the floppy with 0's and examined it with dd and od, it just showed one line
of 0's. One can figure out how many repetitions are involved from examining
line numbers. I also write a C program to examine the file flop and print
a message if it found a byte that was not 0.
In the case of dd and od with the original written sectors, it was visibly
all FF's for the written sectors and 0's for the others. But the experience
where the memory wrapped due to an error in incrementing the BX register
led to some reads that were not all FF's from lower memory.
Anyway, today I hope to get some more floppies and to write the new
versions of the programs that will hopefully sidestep and isolate the
problems I've been having up until now.
> Let me ask you this: why is it necessary to reset the disk controller between
> calls to dsk_do_rw?
I meant, between calls to int 13h with ah=3?
> Anyway, now that I have figured out other ways to do the screen capture
> and to capture memory from 0xC0000 to 0xFFFFF and to write from the floppy
> to the screen, I'll try again as soon as I have some more floppies to
> destroy, which I expect will be today.
I bought a box of floppies. Before trying out any new ideas, I worked on
getting the program to work for capturing upper memory. The problems are
still there. I'll try some of the other ideas tomorrow.
From Gilluwe's comments on some of the functions of int 10h and their
subfunctions, it seems that in principle there should be nothing wrong with
reading video memory, since he explicitly recommends doing so to avoid the
time consuming use of these subfunctions.
One of the things that does show up in the octal dump is 3 occurrences of
the string "Hello, World!". This string is used by the subroutine hello:
(which I don't use) on the boot sector, so the BIOS copies it (along with
the rest of the boot sector) to some area of RAM, and that at least got
picked up by my program. I'm not sure why there are 3 copies, though.
> > Anyway, now that I have figured out other ways to do the screen capture
> > and to capture memory from 0xC0000 to 0xFFFFF and to write from the floppy
> > to the screen, I'll try again as soon as I have some more floppies to
> > destroy, which I expect will be today.
>
> I bought a box of floppies. Before trying out any new ideas, I worked on
> getting the program to work for capturing upper memory. The problems are
> still there. I'll try some of the other ideas tomorrow.
I can use int 10h with AH=8 to read a character and its attribute from
screen memory and display that information on the screen by a mov
instruction back to the screen. I can also move a character either from
screen memory to AX or from 0xC0000 to AX and display it on the screen
using wout. So, there is no problem reading the upper memory, in
principle. I've written a loop to copy all of upper memory to some
RAM in lower memory and I'll try it out tomorrow. If it seems to work,
I'll combine that with a routine to use dsk_do_rw to copy that RAM now
in lower memory to a floppy. I expect it will work.
After reading more of Gilluwe's discussion of the diskette system,
it seems that it is vulnerable to timing errors. So, I think that is
what the whole problem is and now I'm getting interested in timing.
> My boot sector program copies the upper memory area (0xA0000 to 0xFFFFF)
> to sectors 1-16 of tracks 1-48 on side 0 (i.e. head = 0) of the floppy.
I still haven't succeeded in getting my boot sector progam to copy the
BIOS (0xC0000 to 0xFFFFF) to a floppy and I still don't know why it
doesn't work. However, I asked on a linux group how one would copy the
BIOS to a file and was told to execute, as root:
dd if=/dev/mem of=bios.dump bs=1024 skip=768 count=256
I did so and it worked fine. So, now I have a copy of the BIOS. I wrote
a C program to search for consecutive hex bytes 55 AA and found 22. Of
these, almost half seem to occur consecutively, i.e. 55 AA 55 AA, sometimes
with several repetitions. I had the program print the location of the
55 AA's and the decimal values of the two following bytes; the values 85 170
are a dead giveaway for consecutive 55 AA's. So, it appears that there are
maybe a dozen genuine ROM BIOS beginnings. I'll run more tests and see what
sense I can make of the dump.
>Allan Adler <a...@nestle.csail.mit.edu> writes:
>> My boot sector program copies the upper memory area (0xA0000 to 0xFFFFF)
>> to sectors 1-16 of tracks 1-48 on side 0 (i.e. head = 0) of the floppy.
>I still haven't succeeded in getting my boot sector progam to copy the
>BIOS (0xC0000 to 0xFFFFF) to a floppy and I still don't know why it
>doesn't work. However, I asked on a linux group how one would copy the
>BIOS to a file and was told to execute, as root:
>dd if=/dev/mem of=bios.dump bs=1024 skip=768 count=256
>I did so and it worked fine. So, now I have a copy of the BIOS. I wrote
>a C program to search for consecutive hex bytes 55 AA and found 22.
" hexdump -C bios.dump | less "
and then
"/" and "55 AA"
> I wrote
> a C program to search for consecutive hex bytes 55 AA and found 22. Of
> these, almost half seem to occur consecutively, i.e. 55 AA 55 AA,
sometimes
> with several repetitions. I had the program print the location of the
> 55 AA's and the decimal values of the two following bytes; the values 85
170
> are a dead giveaway for consecutive 55 AA's. So, it appears that there are
> maybe a dozen genuine ROM BIOS beginnings. I'll run more tests and see
what
> sense I can make of the dump.
Reiterating my 11/1 post, there is BIOS above 0xF0000 on all PC's, no 0x55
0xAA needed. Between, 0xC0000 and 0xF0000, you check every 2k (0x800) for
0x55 0xAA as bytes (or for 0xAA55 as words). I.e.,
0xC0000,0xC0800,0xC1000,0xC1800, ... adding 0x800 each time.
Rod Pemberton
> Allan Adler writes:
> > I wrote a C program to search for consecutive hex bytes 55 AA and found 22.
>
> " hexdump -C bios.dump | less "
>
> and then
>
> "/" and "55 AA"
Thanks for telling me about hexdump and the way of invoking vi to search for
the string. On my machine, there is a -c option that prints the actual bytes,
not their hex values. There is no -C option and the searches fail.
OK, thanks for the clarification. Since I have all the 55 aa I can see which
ones lie on 2k boundaries. Are you saying that there is no 55 aa for the BIOS
lying at 0xF0000? According to Gilluwe, the 55 aa is part of a format that
first marks the BIOS entry with 55 aa and then follows it with the length of
the code and then with the address of the first instruction. Without the
55 aa how does one know that information?
> Rod Pemberton writes:
> > Reiterating my 11/1 post, there is BIOS above 0xF0000 on all PC's, no 0x55
> > 0xAA needed. Between, 0xC0000 and 0xF0000, you check every 2k (0x800) for
> > 0x55 0xAA as bytes (or for 0xAA55 as words). I.e.,
> > 0xC0000,0xC0800,0xC1000,0xC1800, ... adding 0x800 each time.
>
> OK, thanks for the clarification. Since I have all the 55 aa I can see which
> ones lie on 2k boundaries. Are you saying that there is no 55 aa for the BIOS
> lying at 0xF0000? According to Gilluwe, the 55 aa is part of a format that
> first marks the BIOS entry with 55 aa and then follows it with the length of
> the code and then with the address of the first instruction. Without the
> 55 aa how does one know that information?
I was just confused. The first instruction executed should be at location
0xFFFF0.
Now I'd like to start there and try to disassemble it. My idea is to
write a simulator for the 8086 chip that tries to execute the BIOS
starting at location 0xFFFF0 using my copy of the BIOS. It is a big
job to write such a simulator, taking into account all the possible
opcodes and their subcases, so I'm trying to make the program incremental
in some way. Also, I'm still thinking about what data structures I want
to use. If there is a simple simulator with source code to imitate, that
would be helpful.
Basically, I would proceed as follows after defining the basic data
structures. I'd start with the first instruction at 0xFFFF0 and disassemble
it by hand. Then I'd add the opcode, and 2d byte if any, to the list of
instructions that the simulator knows about, along with a routine telling
it how to implement the instruction. One data structure would be an
array of 1000000 integers, representing memory, for example. Another
could be another array of 1000000 integers, initialized to -1's, such
that each entry is changed to n if it is known to be the first byte of
an n byte instruction, the other bytes of the instruction being marked
with 0, or to -2 if it is known to be data.
My guess is that a small number of such opcode cases will suffice to
disassemble most of the instructions in the BIOS and, when one comes
along that the program doesn't recognize, it will prompt me to modify
the program slightly to incorporate it.
Part of the problem is that I don't know the state of the registers and
RAM when the machine is about to execute the instruction at 0xFFFF0. Maybe
they are unpredictable and the BIOS necessarily begins by initializing the
segment registers.
The precise execution of instructions depends on the contents of certain
registers, such as segment registers, so simple disassembly isn't good
enough. Gilluwe says as much when plugging his own smart disassembler in
his book, The Undocumented PC. He doesn't say exactly what concept he
uses to get around this problem, but simply talks about dealing with the
program as a whole. But it seems that all that is really required is to
run the program in a simulator that keeps track of the information that
is acquired as the binary is executed.
I haven't written any code yet since I'm still somewhat intimidated by
this approach and also because a little more thought now might make
things work better. For example, I used the LateX listings program to
print out the source code for the as86 assembler so I could study it.
Maybe some of its data structures would be appropriate for use with
what I'm considering.
The cpu start state is well defined:
http://www.sandpile.org/ia32/initial.htm
http://www.sandpile.org/aa64/initial.htm
For 16-bits, you can use the ia32 info but shorten eax to ax, ignore
protection info (base, limit, rights) - i.e., CS startup value is it's
selector value F000h, and ignore any register you don't recognize... I'll
summarize: AX=BX=CX=SP=BP=SI=DI=0, CS=F000h, DS=ES=FS=GS=SS=0, flags=2.
There is one register missing: DX. It contains the chip id signature. It
returns a subset of the information that CPUID returns for pre-486 cpu's and
some 486's. Sandpile calls it 000ETFMSh and refers one to the CPUID page.
E is for extended... (could be two, not sure which). T is for type. F is
for family. M is for model. S is for stepping. This is under EAX output
of "standard level 0000_0001h" section. I'm not sure if the BIOS actually
uses this information..., but my BIOS knows the cpu type somehow, probably
CPUID instruction. Exact details of what is in DX and the differences from
CPUID for some early cpu's is here (of course, you should consult the x86
manuals for your cpu...):
http://grafi.ii.pw.edu.pl/gbm/x86/reset.html
Christian Ludloff's Sandpile.org collects detailed information on Intel or
AMD cpus from those companies source documents and other sources:
http://www.sandpile.org
Rod Pemberton
> I was just confused. The first instruction executed should be at location
> 0xFFFF0.
>
> Now I'd like to start there and try to disassemble it. My idea is to
> write a simulator for the 8086 chip that tries to execute the BIOS
> starting at location 0xFFFF0 using my copy of the BIOS.
That is still the general idea but I've started with something simpler.
I wrote a C program which declares an array of size 1048576 of int and
then reads the binary copy of memory locations 0xC0000-0xFFFFF and copies
it to the last 256k positions in the array. It then enters an interactive
loop where it prompts me for commands. The only commands it recognizes
so far are q (for quit) and d (for dump). dump is followed by a hexadecimal
address (e.g. ffff0) which causes the program to print in hex the 16 bytes
starting at the indicated address and then prompt again. Using it on ffff0
it correctly printed out the last 16 bytes of the first 1 meg of memory, as
contained in a hex dump of the binary file. They are:
ea 5b e0 00 f0 30 35 2f 32 39 2f 39 37 00 fc d4
The ea 5b e0 00 f0 is jmp f000:e05b. Then d fe05b prints out
e9 12 13 20 43 1b 41 77 61 72 64 20 43 6f 64 75 and e9 12 14 is
jmp 0x1412. Since this jump is a 3 byte instruction, the jump will be to
fe05bh+3h+1412h= ff470h. Executing d ff470 produces
8e ea fa fc e4 64 a8 04 75 12 8c c8 8e d0 bc 84
The 8e looks like the beginning of a mov instruction, but in binary
the following ea is 11101010, so the mode field is 11, the reg field
is 101 and the r/m field is 010. According to The 8086 Book, the leftmost
bit of the reg field has to be 0; it does list the case of the bit being 1
but says this instruction is not used. Gilluwe's book, The Undocumented PC,
comes with a floppy containing some programs to test for undocumented features
of the PC. I ran the program that tests for undocumented instructions and,
on the old PC from which this BIOS was taken, there are a few candidates
listed for undocumented instructions, but this one is not among them.
This is a pretty early stage of my interactive program, so the problem
might be there, or I might just be making stupid mistakes in reading and
disassembling the output by hand. I would use DEBUG but I haven't figured
out how to install it yet on the FREEDOS partition of this PC. The file
is there but it requires some other files from djgpp and I haven't dealt
yet with the problems of locating the right programs and installing them.
The C program I mentioned above is running under Linux on another machine,
using a copy of the binary copy of the BIOS I transfered from the older
machine.