Floppy Driver keep halting on reset (Solved by BenLunt)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
Ycep
Member
Member
Posts: 401
Joined: Mon Dec 28, 2015 11:11 am

Floppy Driver keep halting on reset (Solved by BenLunt)

Post by Ycep »

Now only a code snippet for other post.

Code: Select all

#include<hal.h>
#include"floppy.h"
#include"dma.h"
#include"bitoper.h"
#include"conio.h"
int CurDrive = 0;
extern void puts(char*);
static volatile bool IRQFire = false;
extern void cerror(char*);
extern void cinfo(char*);
#include "visio.h"
void WaitIRQ()
{
	int i=0;
	while(!IRQFire)
	{
		i++;
		if(i==3000){cerror("Waiting IRQ timed out.");return;}
		sleep(1);
	}
	IRQFire = false;
}
__declspec(naked) void FloppyIrq()
{
	_asm
	{
		pushad
	}
	IRQFire=true;
	eoimsg(6);
	_asm
	{
		popad
		iretd
	}
}
short irq = 6;
bool FloppyDMAinit(unsigned char* buffer)
{
	union 
	{
		unsigned char addressbyte[4];
		unsigned char countbyte[4];
		unsigned long val;
	}address, count;
	address.val=(unsigned)buffer;
	count.val=(unsigned)disk->SectorSize-1;
	if ((address.val>>24)||(count.val >> 16)||(((address.val&0xffff)+count.val)>>16))return false;
	DmaReset();
	MaskChannel(2);
	DmaResetFlipFlop(1);
	DmaSetAddress(2,address.addressbyte[0],address.addressbyte[1]);
	DmaResetFlipFlop(1);
	DmaSetCount(2,count.countbyte[0],count.countbyte[1]);
	DmaSetRead(2);
	outb(0x0e, 0xff);
	return true;
}
void MotorControl(char no=0,bool sw=true)
{
	if(no>3)
	{
		cerror("Can't control motor on unexistent floppy drive.");
		return;
	}
	unsigned char motor;
	switch (no)
	{
	case 0:
		motor=MotorDriveZero;
		break;
	case 1:
		motor=MotorDriveOne;
		break;
	case 2:
		motor=MotorDriveTwo;
		break;
	case 3:
		motor=MotorDriveThree;
		break;
	}
	if(sw&&!Convert(disk->Motors,no))
	{
		outb(DigitalOutReg, motor|EnableIRQDMA|Reset);
		ChangeBit(&disk->Motors,no,true);
	}
	else if(!sw&&Convert(disk->Motors,no))
	{
		outb(DigitalOutReg, Reset);
		ChangeBit(&disk->Motors,no,false);
	}
	if(disk->PhysicalSize==Small)sleep(200);
	else if(disk->PhysicalSize==Big)sleep(300);
	else cerror("Physical floppy size not defined.");
}
void SendCommandByte(unsigned char cmd)
{
	for (int i = 0; i < 500; i++)
		if (inb(MainStatusReg) & 0x80)
			return outb(FifoReg, cmd);
}

unsigned char ReadDataByte()
{
	for (int i = 0; i < 500; i++)
		if (inb(MainStatusReg) & 0x80)
			return inb(FifoReg);
	return 0;
}
void CheckInt(unsigned int* st0, unsigned int* cyl)
{
	SendCommandByte(SenseInterrupt);
	*st0 = ReadDataByte();
	*cyl = ReadDataByte();
}
int recalibrate()
{
	unsigned int st0, cyl;
	if (CurDrive >= 4)
		return -2;
	for (int i = 0; i < 10; i++)
	{
		SendCommandByte(ReCalibrate);
		SendCommandByte(CurDrive);
		WaitIRQ ();
		CheckInt(&st0,&cyl);
		if (!cyl)
		{
			return 0;
		}
	}
	cerror("Failed to recalibrate.");
	return -1;
}
int FloppySeek(unsigned char cyl, unsigned char head)
{
	unsigned int st0, cyl0;
	if (CurDrive >= 4)
		return -1;
	for (int i = 0; i < 10; i++ )
	{
		SendCommandByte(Seek);
		SendCommandByte(head<<2|CurDrive);
		SendCommandByte(cyl);
		WaitIRQ();
		CheckInt(&st0,&cyl0);
		if(cyl0==cyl)
			return 0;
	}
	cerror("Failed to seek floppy.");
	return -1;
}
extern void cinfo(char*);
void reset()
{
	unsigned int st0, cyl;
	outb(ControlReg, 0);
	outb(DigitalOutReg, 0);
	outb(DigitalOutReg, EnableIRQDMA|Reset);
	WaitIRQ();
	for (int i=0; i<4; i++)CheckInt(&st0,&cyl);
	outb (ControlReg, Normal);
	recalibrate();
}
#include "visio.h"
void FloppyInit()
{
	disk=new Floppy;
	disk->Motors=0;
	disk->DataSize=NormalS;
	disk->PhysicalSize=Small;
	disk->SectorSize=512;
	disk->SPT=18;
	stvect(38, FloppyIrq);
	MotorControl(CurDrive);
	reset();
}
void SelectDrive(char no)
{
	switch (no)
	{
	case 0:
		outb(DigitalOutReg, DriveZero);break;
	case 1:
		outb(DigitalOutReg, 0);break;
	case 2:
		outb(DigitalOutReg, 0);break;
	case 3:
		outb(DigitalOutReg, 0);break;
	}
}
void* ReadSector(int lba)
{
	int Head=0, Track=0, Sector=1;
	Head=(lba%(disk->SPT*2))/disk->SPT;
	Track=lba/(disk->SPT*2);
	Sector=lba%disk->SPT+1;
	cinfo("About to select drive...");
	SelectDrive(CurDrive);
	cinfo("Recalibarate...");
	recalibrate();
	cinfo("Seeking...");
	if(FloppySeek(Track,Head))return 0;
	cinfo("Initializing DMA...");
	FloppyDMAinit((unsigned char*)131072);
	cinfo("Read to channel 2.");
	DmaSetRead(2);
	SendCommandByte(Read|MT|SK|MF);
	SendCommandByte((Head<<2)|CurDrive);
	SendCommandByte(Track);
	SendCommandByte(Head);
	SendCommandByte(Sector);
	SendCommandByte(2);
	SendCommandByte(Sector+1);
	SendCommandByte(0x27);
	SendCommandByte(0xff);
	WaitIRQ();
	for (int i = 0; i<7; i++)
		ReadDataByte();
	cinfo("Finished.");
	return (void*)131072;
}
void* getInfo()
{
	return disk;
}
Last edited by Ycep on Sun Jun 05, 2016 6:45 am, edited 5 times in total.
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by Nable »

1. "IRQFire" variable have to be marked as volatile (please, correct me if I'm wrong but I had a problem in a similar situation when I've omitted "volatile" modifier).
2. AFAIR, doing "cli" and "sti" in an interrupt handler is unnecessary and may bring problems.
3. What compiler is it? Is it the one from MS?
4. You can use Bochs's built-in debugger to find the exact place where your code hangs.
User avatar
Ycep
Member
Member
Posts: 401
Joined: Mon Dec 28, 2015 11:11 am

Reply

Post by Ycep »

Nable wrote:1. "IRQFire" variable have to be marked as volatile (please, correct me if I'm wrong but I had a problem in a similar situation when I've omitted "volatile" modifier).
2. AFAIR, doing "cli" and "sti" in an interrupt handler is unnecessary and may bring problems.
3. What compiler is it? Is it the one from MS?
4. You can use Bochs's built-in debugger to find the exact place where your code hangs.
I'm sorry, that didn't help.
Compiler is Visual Studio 2010. ( I do not want VS10 vs GCC wars)
I checked EIP but it points to hlt in nonsense function of IDTinit. It do not call any HLT.
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Reply

Post by Nable »

lukaandjelkovic wrote:I checked EIP but it points to hlt in nonsense function of IDTinit. It do not call any HLT.
Maybe your code corrupts memory somewhere. Try single-step debugging starting from FloppyInit function.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by BenLunt »

Hi,

My guess is that it is the next line that is causing the problem.

> outb(DigitalOutReg, EnableIRQDMA|Reset);

Here you have told the FDC to allow interrupts.

Have you set up a valid IRQ yet?

As suggested, I would use the builtin Bochs debugger, set a break point, then single step to see what happens.

Bochs has a great "break point" technique. Make sure the breakpoint flag is set in your bochsrc.txt file
then place the following instruction in your code:

xchg bx,bx

Bochs will break at the point.

void reset()
{
uint32_t st0, cyl;
_asm xchg bx,bx // <---- Bochs debugger will break right here, then you can single step from there.
outb(ControlReg, 0);
outb(DigitalOutReg, 0); <Problem
outb(DigitalOutReg, EnableIRQDMA|Reset);
//WaitIRQ();
for (int i=0; i<4; i++)CheckInt(&st0,&cyl);
outb (ControlReg, Normal);
//recalibrate();
}

Chapters 3 and 4 in "FYSOS: Media Storage Devices" (http://www.fysnet.net/media_storage_devices.htm)
shows how to communicate with the Floppy Disk Controller.

Thanks,
Ben
User avatar
Ycep
Member
Member
Posts: 401
Joined: Mon Dec 28, 2015 11:11 am

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by Ycep »

BenLunt wrote:Hi,

My guess is that it is the next line that is causing the problem.

> outb(DigitalOutReg, EnableIRQDMA|Reset);

Here you have told the FDC to allow interrupts.

Have you set up a valid IRQ yet?

As suggested, I would use the builtin Bochs debugger, set a break point, then single step to see what happens.

Bochs has a great "break point" technique. Make sure the breakpoint flag is set in your bochsrc.txt file
then place the following instruction in your code:

xchg bx,bx

Bochs will break at the point.

void reset()
{
uint32_t st0, cyl;
_asm xchg bx,bx // <---- Bochs debugger will break right here, then you can single step from there.
outb(ControlReg, 0);
outb(DigitalOutReg, 0); <Problem
outb(DigitalOutReg, EnableIRQDMA|Reset);
//WaitIRQ();
for (int i=0; i<4; i++)CheckInt(&st0,&cyl);
outb (ControlReg, Normal);
//recalibrate();
}

Chapters 3 and 4 in "FYSOS: Media Storage Devices" (http://www.fysnet.net/media_storage_devices.htm)
shows how to communicate with the Floppy Disk Controller.

Thanks,
Ben
Wait,
How could i enable breakpoints? What should i do?
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by BenLunt »

First, you must have a Bochs build that has the debug gui supported. The release version from the website should have this already.

Then, in your bochsrc.txt file, make sure you have the following line, assuming you are using a Win32 host.

Code: Select all

display_library: win32, options="gui_debug"  # use Win32 debugger gui
Then in that same bochsrc.txt file, have the following line(s):

Code: Select all

#=======================================================================
# MAGIC_BREAK:
# This enables the "magic breakpoint" feature when using the debugger.
# The useless cpu instruction XCHG BX, BX causes Bochs to enter the
# debugger mode. This might be useful for software development.
#
# Example:
#   magic_break: enabled=1
#=======================================================================
magic_break: enabled=1
Now start Bochs using the debugging environment, and 'c' to continue execution. The debugger will run through your code until if finds the XCHG BX,BX instruction.
User avatar
Ycep
Member
Member
Posts: 401
Joined: Mon Dec 28, 2015 11:11 am

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by Ycep »

BenLunt wrote:First, you must have a Bochs build that has the debug gui supported. The release version from the website should have this already.

Then, in your bochsrc.txt file, make sure you have the following line, assuming you are using a Win32 host.

Code: Select all

display_library: win32, options="gui_debug"  # use Win32 debugger gui
Then in that same bochsrc.txt file, have the following line(s):

Code: Select all

#=======================================================================
# MAGIC_BREAK:
# This enables the "magic breakpoint" feature when using the debugger.
# The useless cpu instruction XCHG BX, BX causes Bochs to enter the
# debugger mode. This might be useful for software development.
#
# Example:
#   magic_break: enabled=1
#=======================================================================
magic_break: enabled=1
Now start Bochs using the debugging environment, and 'c' to continue execution. The debugger will run through your code until if finds the XCHG BX,BX instruction.
Thanks!
Hmm, it seem to stop onto breakpoint, and if i click Continue it just stops at mov esp, ebp.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by BenLunt »

At first load up, yes it will "not go anywhere" until you hit 'c' for continue, which will run as if you did not have the debugger going. To stop the execution, it either needs to find the 'xchg bx,bx' instruction or find a breakpoint that you have already entered.

What I usually do is set a break point at 0x07C00 just to skip all of the BIOS and boot code.

lb 0x07C00

Then I hit the 'c' key

c

This will run until the point where my boot loader has taken control. This just assures that I have successfully loaded my boot code and the "machine" is in the state I wish it to be in. I then 'c' again until it stops at my 'xchg bx,bx' .

If it does not, and hangs or in your case, stops somewhere else, that means that it did not get to my 'xchg bx,bx'. Therefore, I place another 'xchg bx,bx' at a known location before this point to see if it makes it there.

Continue to place 'xchg bx,bx' throughout your code until you find where the problem is.

To know which 'xchg bx,bx' you are at, is the key and there are a few things you can do.
  • 1. Know the physical address of the 'xchg bx,bx' at assemble/compile time.
    2. Place a number of 'nop' instructions just after, so that these nop instructions will be displayed in the debugger. Then count the nops. One for the first 'xchg bx,bx', 2 for the second, and so on.
    3. Place an "do nothing" instruction just before or just after: 'mov eax,00001' for the first, 'mov eax,00002' for the second, etc., being sure to preserve eax if needed.
As for the 'mov esp,ebp' instruction you mention, look to see where this instruction actually exists. Is it within the BIOS code? (0x0F000 - 0xFFFFF ?). Is it in the address space you expect your driver to be?

Hope this helps,
Ben
User avatar
Ycep
Member
Member
Posts: 401
Joined: Mon Dec 28, 2015 11:11 am

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by Ycep »

BenLunt wrote:At first load up, yes it will "not go anywhere" until you hit 'c' for continue, which will run as if you did not have the debugger going. To stop the execution, it either needs to find the 'xchg bx,bx' instruction or find a breakpoint that you have already entered.

What I usually do is set a break point at 0x07C00 just to skip all of the BIOS and boot code.

lb 0x07C00

Then I hit the 'c' key

c

This will run until the point where my boot loader has taken control. This just assures that I have successfully loaded my boot code and the "machine" is in the state I wish it to be in. I then 'c' again until it stops at my 'xchg bx,bx' .

If it does not, and hangs or in your case, stops somewhere else, that means that it did not get to my 'xchg bx,bx'. Therefore, I place another 'xchg bx,bx' at a known location before this point to see if it makes it there.

Continue to place 'xchg bx,bx' throughout your code until you find where the problem is.

To know which 'xchg bx,bx' you are at, is the key and there are a few things you can do.
  • 1. Know the physical address of the 'xchg bx,bx' at assemble/compile time.
    2. Place a number of 'nop' instructions just after, so that these nop instructions will be displayed in the debugger. Then count the nops. One for the first 'xchg bx,bx', 2 for the second, and so on.
    3. Place an "do nothing" instruction just before or just after: 'mov eax,00001' for the first, 'mov eax,00002' for the second, etc., being sure to preserve eax if needed.
As for the 'mov esp,ebp' instruction you mention, look to see where this instruction actually exists. Is it within the BIOS code? (0x0F000 - 0xFFFFF ?). Is it in the address space you expect your driver to be?

Hope this helps,
Ben
Is there any other solution without using that Bochs debugger?
At same time, it's somewhere in my IDT initialization function. How the fu** it could be there!? First of all, it crashes in reset() in FloppyInit(). But please, can anyone just check my whole code again?
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by neon »

I'm not convinced the problem is in the provided code. Eip should not have jumped like that to a random halt instruction. Makes me suspicious of a stack or memory related error. It would be helpful if someone could review his code though - it might have small errors - I just don't think its related to the bigger problem. The problem just surfaces when reset is called -- doesn't mean reset itself is the problem.

Any case, you do have to get comfortable with the bochs debugger -- it really doesn't get easier then this.

I'd also suggest looking at the disassembly of FloppyIrq. There is a reason why I use asm code blocks and not separate asm statements (i.e. using _asm {...} vs _asm ; _asm ; etc. Keep in mind that, in the long term, irq's should be in assembly language - but its a good idea to make sure the generated code is what it should be. If its not, it could certainly cause eip to jump.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
User avatar
Ycep
Member
Member
Posts: 401
Joined: Mon Dec 28, 2015 11:11 am

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by Ycep »

Hmmm. If Floppy-IRQ is problem i tried to make it all in assembly. Still do not work. What's wrong with this below?

Code: Select all

pushad
setz[IRQFire] ;Set if zero IRQFire, like IRQFire=true
push 6; Push number 6 to stack since that is required parameter
call eoimsg ;Sends EOI to PIC
add esp, 1 ;Because we pushed 6 to stack, let's move stack pointer by 1 
popad
iretd
mikegonta
Member
Member
Posts: 229
Joined: Thu May 19, 2011 5:13 am
Contact:

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by mikegonta »

lukaandjelkovic wrote:Hmmm. If Floppy-IRQ is problem i tried to make it all in assembly. Still do not work. What's wrong with this below?

Code: Select all

add esp, 1 ;Because we pushed 6 to stack, let's move stack pointer by 1 
Next time push 24 so that you can move the stack pointer by the required 4.
Mike Gonta
look and see - many look but few see

https://mikegonta.com
MDenham
Member
Member
Posts: 62
Joined: Sat Nov 10, 2012 1:16 pm

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by MDenham »

Since the following instruction is popad, if the procedure you're calling doesn't pop the parameter already (protip: it should) you can clean that up with just "pop eax" (or any other register since it'll just get overwritten with the correct contents on the next instruction).

Really, there are very few situations where you should be doing math directly on SP/ESP/RSP; that's what BP/EBP/RBP are for :-)
User avatar
Ycep
Member
Member
Posts: 401
Joined: Mon Dec 28, 2015 11:11 am

Re: Sending 0 to Floppy Digital Output Register freezes Boch

Post by Ycep »

mikegonta wrote:Next time push 24 so that you can move the stack pointer by the required 4.
Nope. That isn't working. Anyways, it seem you wrongly understood my eoimsg function, this is how it looks:

Code: Select all

eoimsg (unsigned char intno)
Since it's char i should move stack pointer by 1.
Next time push 24:
pushing 6 and calling eoimsg should be something like eoimsg(6);
__________________________________
[qoute="MDenham"]
Since the following instruction is popad, if the procedure you're calling doesn't pop the parameter already (protip: it should) you can clean that up with just "pop eax" (or any other register since it'll just get overwritten with the correct contents on the next instruction).
[/quote]
Thanks :) , so, this is how it should look then:

Code: Select all

pushad
setz[IRQFire]
	push 6
	call eoimsg
add esp, 1
popad
pop eax
iretd
Note that IRQFire is boolean.
Post Reply