This is the mail archive of the
ecos-discuss@sources.redhat.com
mailing list for the eCos project.
Re: i386 flopy load problem.
- To: Fabrice_Gautier at sdesigns dot com, jlarmour at cygnus dot co dot uk
- Subject: Re: [ECOS] i386 flopy load problem.
- From: Rolf Manderscheid <rvm at yottayotta dot com>
- Date: Thu, 2 Nov 2000 23:01:08 -0700
- Cc: ecos-discuss at sourceware dot cygnus dot com
Fabrice Gautier wrote:
> The current code for loading from the floppy won't work in many case. It
> will be subject to what i call the "buffer cross 64k boundary problem": The
> bios function can't handle a buffer that cross a 64k boundary (This is also
> refered as "DMA access across 64k boundary" in some docs)
>
> The current code load track by track, and so there is a good chance to cross
> a 64K boundary during a track load, if the image size is big enough.
>
> The only way to avoid the problem is to load one sector at a time, and to
> align _start on 512 bytes. This is slower but who cares?
Actually, you can still read full tracks ... most of the time.
I'm embarrassed to say that I had a fix for the floppy boot problem
for some time but I've neglected to contribute it to the mailing list.
I promise to share and play well with others from now on :-)
I was under the impression that loading your program from floppy was
not the normal mode of operation. Instead, you would make a floppy
with the gdb stubs on it *once* and then use it to download your
programs. Running this way, you would never run into the space
limitation imposed by the distributed boot block. Maybe somebody can
explain to me *exactly* how that is supposed to work. I had no
trouble building the stubs but the out-of-the-box linker scripts put
the start address for the monitor and programs at the same place, so
the monitor clobbers itself while loading the program. I didn't
pursue that because we actually want our program to start up
automatically, so I fixed the boot block to handle larger programs.
If I were to pursue it, I would change the ldi script to move the
start address of loaded programs ... this makes me think I'm missing
something. I would be pleased if somebody enlightened me.
Even though Fabrice has patched the problem, I think the boot block
below is still of interest because it will read entire tracks whenever
possible, retries failed reads, and aborts on failure with a message
(terse maybe, but a message nonetheless!). See the comments near the
abort for the meaning of each status.
This fix to the loader allows programs up to roughly 640K in size
(text+data+bss). A simple way to get more space for text and data is
to move the bss section into extended memory, you only have to change
the ldi script (diffs below). If you do this, don't forget to move
any cyg_mempools you create using extended memory to after _end.
If you still need more space, make sure you're compiling with
optimization ... I've seen that reduce text space by about 20%
Moving the text and/or data section is a little more work, because you
actually have to load it! You can only specify 20 bits of address in
the es:bx pair as the destination address of the read floppy bios
routine, so one would need to load tracks into a buffer in standard
memory space and then copy it into its final destination. We haven't
needed that yet but if we do I'll be sure to contribute it promptly :-)
Cheers!
rvm
----
Rolf Manderscheid YottaYotta, Inc
rmanderscheid@YottaYotta.com #301, 10328 81 Ave
780.439.9000 Edmonton, Alberta, Cananda T6E 1X2
----------------------------------------------------------------------
diff -c -r1.1 -r1.2
*** mlt_i386_pc_floppy.ldi 2000/08/09 17:28:00 1.1
--- mlt_i386_pc_floppy.ldi 2000/10/24 16:47:08 1.2
***************
*** 31,36 ****
--- 31,37 ----
MEMORY
{
ram : ORIGIN = 0x0, LENGTH = 0xA0000
+ exram : org = 0x100000, len = 64M
}
SECTIONS
***************
*** 45,51 ****
SECTION_gcc_except_table (ram, ALIGN (0x1), LMA_EQ_VMA)
SECTION_rel__got (ram, ALIGN (0x1), LMA_EQ_VMA)
SECTION_data (ram, ALIGN (0x8), LMA_EQ_VMA)
! SECTION_sbss (ram, ALIGN (0x4), LMA_EQ_VMA)
! SECTION_bss (ram, ALIGN (0x10), LMA_EQ_VMA)
SECTIONS_END
}
--- 46,52 ----
SECTION_gcc_except_table (ram, ALIGN (0x1), LMA_EQ_VMA)
SECTION_rel__got (ram, ALIGN (0x1), LMA_EQ_VMA)
SECTION_data (ram, ALIGN (0x8), LMA_EQ_VMA)
! SECTION_sbss (exram, 0x100000, LMA_EQ_VMA)
! SECTION_bss (exram, ALIGN (0x10), LMA_EQ_VMA)
SECTIONS_END
}
----------------------------------------------------------------------
boot block change log:
----------------------------------------------------------------------
- fixed the loader. The original platform.inc loader code would
load entire tracks from the floppy starting at _start (configurable,
but currently 0x1000). Each track has 18 sectors of 512 bytes each,
so loading would proceed as follows:
0x1000 - 0x33ff head 0, track 0
0x3400 - 0x57ff head 1, track 0
0x5800 - 0x7bff head 0, track 1
0x7c00 - 0x9fff head 1, track 1
0xa000 - 0xc3ff head 0, track 2
0xc400 - 0xe7ff head 1, track 2
BAD -> 0xe800 - 0x10bff head 0, track 3
Seems reasonable, right? The only problem is that the floppy
controller will not DMA across an absolute 64k boundary. In fact,
the "int" call returns an error code that says exactly that, but
that error code wasn't being emitted. Worse still, it wasn't
being checked so the program would continue as though it had loaded
in its entirety, where it actually only loaded 0xd800 bytes.
The new version:
- loads entire tracks if possible, and partial tracks if necessary.
- gives an indication of progress during loading
- checks the return code of the IO operation, and aborts
(with a message) in case of failure.
-----------------------------------------------------------------------
.macro hal_cpu_init
#ifdef CYG_HAL_STARTUP_FLOPPY
/* This code is loaded from a floppy disk when the PC powers up. */
.code16
.set disk, 0 /* floppy A: */
/* sectorsPerTrack could be 36, 18, 15, or 9. A fancier boot block
would try them all */
.set sectorsPerTrack, 18
.set sectorShift, 9
.set bytesPerSector, (1 << sectorShift) /* 2^sectorShift */
.set bytesPerTrack, (sectorsPerTrack * bytesPerSector)
.set screenSize, 80*25
.set stackSize, 0
.macro declare size name
.set \name, - stackSize - \size
.set stackSize, stackSize + \size
.endm
declare 4, addr
declare 4, length
declare 2, bytesThisRead
declare 2, trackBytesUnread
declare 1, sectorsThisRead
declare 1, retryCount
declare 1, head
declare 1, track
declare 1, sector
declare 1, row
declare 1, column
cld /* always count up */
/* set up the base and stack pointers and stack segment */
/* %%% this assumes too much about the address of _start %%% */
movl $_start, %eax
movw %ax, %bp
movw %ax, %sp
subw $stackSize, %sp /* make space for variables */
shr $16, %eax
movw %ax, %ss
/* Ask the BIOS for info about the amount of RAM available.
* We push these onto the stack for later use.
*/
xorl %eax, %eax
movb $0x88, %ah /* Get the amount of extended memory. */
int $0x15
shl $10, %eax
pushl %eax
xorl %eax, %eax
int $0x12 /* Get the amount of standard memory. */
shl $10, %eax
pushl %eax
movl $_edata, %eax /* last address to load */
movl $_start, %ebx /* destination address. */
subl %ebx, %eax /* length of image */
movl %eax, length(%bp)
movl %ebx, addr(%bp)
movb $0, head(%bp)
movb $0, track(%bp)
movb $'\n', %al
call 9f
movb $'L', %al
call 9f
movb $'o', %al
call 9f
movb $'a', %al
call 9f
movb $'d', %al
call 9f
movb $'i', %al
call 9f
movb $'n', %al
call 9f
movb $'g', %al
call 9f
0: /* newTrack: */
movw $bytesPerTrack,trackBytesUnread(%bp)
movb $1,sector(%bp) /* sector numbers start at one */
movb $'.', %al
call 9f /* putc('.') */
1: /* readTrack: */
cmpl $0, length(%bp)
jle 1f /* doneRead */
/* The read must not cross an absolute 64K boundary, so we might
* not be able to read a whole track. Set ax to the number of
* bytes remaining in this 64K segment.
*/
movl $0x10000, %eax
movl addr(%bp),%ebx /* 0x10000 - (addr & 0xffff) */
andl $0xffff,%ebx
subl %ebx, %eax
xorl %ebx,%ebx
movw trackBytesUnread(%bp), %bx
cmpl %ebx, %eax
jl 2f /* startRead */
movw %bx, %ax
2: /* startRead: */
movw %ax, bytesThisRead(%bp)
shr $sectorShift, %ax /* %al := sector count */
movb %al, sectorsThisRead(%bp)
movb $0x2, %ah /* op: read */
movl addr(%bp),%edi /* address into es:bx */
movl %edi,%esi
shr $4,%esi
andw $0xff00,%si
movw %si, %es
movw %di, %bx
andw $0xfff, %bx
movb track(%bp), %ch
movb sector(%bp), %cl
movb $disk, %dl
movb head(%bp), %dh
int $0x13 /* read it */
cmpb %al, sectorsThisRead(%bp)
je 4f /* success */
cmpb $3, retryCount(%bp)
jne 3f /* retry */
/* error code in %ah:
* 0x01 Bad command
* 0x02 No address mark
* 0x03 Disk write protected
* 0x04 Bad sector
* 0x08 DMA overrun
* 0x09 DMA attempt across 64K boundary
* 0x10 Bad CRC
* 0x20 Controller failure
* 0x40 Seek failed
* 0x80 No response
*/
/* emit abort message and error status */
movb $'\n', %al
call 9f
movb $'A', %al
call 9f
movb $'b', %al
call 9f
movb $'o', %al
call 9f
movb $'r', %al
call 9f
movb $'t', %al
call 9f
movb $':', %al
call 9f
movb $' ', %al
call 9f
/* lazy man's hex to ascii conversion (ie. no 'a' to 'f' digits
* ... none appear in the possible error codes)
*/
movb %ah, %al
shrb $4, %al
addb $'0', %al
call 9f
movb %ah, %al
andb $0xf, %al
addb $'0', %al
call 9f
/* abort */
5: nop
jmp 5b
3: /* retry: */
addb $1, retryCount(%bp)
jmp 1b /* readTrack */
4: /* success: */
movb $0, retryCount(%bp)
/*
* the following jump goes to the loaded code the first time
* through, it has no effect on subsequent passes.
*/
ljmp $0, $5f /* continue */
5: /* continue: */
addb %al,sector(%bp)
xorl %eax,%eax
movw bytesThisRead(%bp), %ax
addl %eax,addr(%bp)
subl %eax,length(%bp)
subw %ax,trackBytesUnread(%bp)
cmpw $0,trackBytesUnread(%bp)
jne 1b /* we've read a partial track, go read the rest */
addb %dh,track(%bp) /* track += head; */
negb %dh /* head = 1 - head; */
addb $1,%dh
movb %dh,head(%bp)
jmp 0b /* goto newTrack; */
/* putc: puts the character specified in %al onto the screen at
the current cursor location, and updates the cursor,
scrolling if necessary. The only special character
recognized is '\n', which causes the current cursor
position to move to the next line.
*/
9:
pusha
movb $3, %ah /* get current cursor location into %dx */
xorb %bh, %bh
int $0x10 /* %dh = row, %dl = col */
cmpb $'\n', %al
jne 6f /* emit character */
movb $80, %dl
jmp 7f /* fix cursor */
6: /* emit character */
movb $9, %ah
movw $0x0007, %bx /* page=0 attribute=white-on-black */
movw $1, %cx /* repetition count */
int $0x10 /* emit it */
incb %dl /* next column */
7: /* fix cursor */
cmpb $80, %dl /* last column? */
jne 8f /* if not then save cursor */
xorb %dl, %dl /* column = 0 */
incb %dh /* next row */
cmpb $25, %dh /* last row? */
jne 8f /* if not then save cursor */
/* scroll */
pushw %dx /* save row/col */
movw $0601, %ax /* scroll up one line */
movb $7, %bh /* attribute = white on black */
xorw %cx, %cx /* from (0,0) */
movb $24, %dh /* to (24,79) */
movb $79, %dl
int $0x10 /* scroll away */
popw %dx /* restore row/col */
8: /* save cursor */
movb $2, %ah /* save cursor position */
xorb %bh, %bh /* page 0 */
int $0x10 /* %dh = row, %dl = col */
popa
ret
/* the boot block signature: */
. = _start + 510
.byte 0x55
.byte 0xaa
1: /* doneRead */
-----------------------------------------------------------------------