This is the mail archive of the ecos-discuss@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: i386 flopy load problem.


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 */
-----------------------------------------------------------------------

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]