Page 1 of 1

Reading the default cursor width in text mode

Posted: Wed Jun 04, 2014 3:58 am
by dlarudgus20
According to http://www.stanford.edu/class/cs140/pro ... reg.htm#0A, We can control cursor width by Cursor Start Register(0Ah) and Cursor End Register(0Bh). When I write on these regieters, I can see cursor width changing. However, How can I "read" the registers? (Because I want to read default-setting before I change the registers)

At first, I tried this:

Code: Select all

static void ckGetCursorWidthDefault()
{
	// TODO: buggest buggy!
	ckPortOutByte(BaseVideoPort, 0x0a);
	DefaultBeginHeight = ckPortInByte(BaseVideoPort + 1) & 0x1f;
	ckPortOutByte(BaseVideoPort, 0x0b);
	DefaultEndHeight = ckPortInByte(BaseVideoPort + 1) & 0x1f;
}
Um, It seems to work well... However, I've noticed other I/O port ruined! (Keyboard, RTC TimeStamp, ..)

These registers are read-only? If so, How can I read default-setting of cursor width? If not, What's wrong in my code?

Re: read default cursor width in text mode

Posted: Wed Jun 04, 2014 4:20 am
by Combuster
Given the circumstances, the likely problem is that BaseVideoPort isn't set to the correct choice of 0x3B4 or 0x3D4, which locate the CRTC in the IO space.

Note that for a VGA or emulation thereof, the IO ports are always fixed in the same location starting from 0x3B0 onward, which means that a "base video port" is both useless and if taken by it's name should most likely not point to the CRTC registers.

Re: read default cursor width in text mode

Posted: Thu Jun 05, 2014 5:47 am
by dlarudgus20
the IO ports are always fixed in the same location starting from 0x3B0 onward, which means that a "base video port" is both useless and if taken by it's name should most likely not point to the CRTC registers.
Actually, I initialized "BaseVideoPort" from BIOS data area (http://wiki.osdev.org/Memory_Map_(x86)# ... _.28BDA.29)

Code: Select all

BaseVideoPort = *(uint16_t *)0x0463;
Do you mean this code is actually unnecessary? Is it absolutely fine to read/write 0x3D4 port?


..............Um, I figure out what is mess. I tried "Debug build" of my OS, and I can see it work. Then maybe it is an optimization problem - I guess "ckPortInByte" and "ckPortOutByte" are mess. They are:

Code: Select all

static inline uint8_t ckPortInByte(uint16_t port)
{
	uint8_t ret;
	__asm__ ( "in %1, %0" : "=a"(ret) : "d"(port) );
	return ret;
}

static inline void ckPortOutByte(uint16_t port, uint8_t data)
{
	__asm__ ( "out %0, %1" : : "a"(data), "d"(port) );
}
I tried these four inline-assembly:

Code: Select all

__asm__ ( "in %1, %0" : "=a"(ret) : "d"(port) );
__asm__ ( "out %0, %1" : : "a"(data), "d"(port) );    // (0) doesn't work, of course

__asm__ ( "in %1, %0" : "=a"(ret) : "d"(port) : "memory" );
__asm__ ( "out %0, %1" : : "a"(data), "d"(port) : "memory" );    // (1) doesn't work

__asm__ __volatile__ ( "in %1, %0" : "=a"(ret) : "d"(port) );
__asm__ __volatile__ ( "out %0, %1" : : "a"(data), "d"(port) );    // (2) works

__asm__ __volatile__ ( "in %1, %0" : "=a"(ret) : "d"(port) : "memory" );
__asm__ __volatile__ ( "out %0, %1" : : "a"(data), "d"(port) : "memory" );    // (3) works
I understand that may be optimized-out.....

Re: read default cursor width in text mode

Posted: Thu Jun 05, 2014 5:57 am
by sortie
You are doing an unaligned read to get the base video port. That is undefined behavior. Also, use the inline assembly from the wiki inline assembly example page.

Re: read default cursor width in text mode

Posted: Thu Jun 05, 2014 6:04 am
by dlarudgus20
sortie wrote:You are doing an unaligned read to get the base video port. That is undefined behavior. Also, use the inline assembly from the wiki inline assembly example page.
UNDEFINED BEHAVIOR?? Why?? Could you explain the reason in detail?

Re: read default cursor width in text mode

Posted: Thu Jun 05, 2014 6:07 am
by sortie
No need to yell. You are reading an uint16_t. Those must be 2-byte aligned in memory. However, the pointer you are reading it at is not 2-byte aligned (notice the hex value ends in a 3). That read is undefined behavior or UB. UB is toxic. Never do it, the compiler assumes it never happens, and code that rely on it end to break horribly when optimized. You really ought to do an online search for undefined behavior in C.

Re: read default cursor width in text mode

Posted: Thu Jun 05, 2014 6:15 am
by dlarudgus20
sortie wrote:No need to yell. You are reading an uint16_t. Those must be 2-byte aligned in memory. However, the pointer you are reading it at is not 2-byte aligned (notice the hex value ends in a 3). That read is undefined behavior or UB. UB is toxic. Never do it, the compiler assumes it never happens, and code that rely on it end to break horribly when optimized. You really ought to do an online search for undefined behavior in C.
Oh, Thank you...

However, anyway, there's in platform-dependent coding and I need to read BIOS data area - Isn't it almost okay anyway?

Still, I agree that is toxic - um, then...... How about this?

Code: Select all

__asm__ ( "movw (0x0463), %0" : "=r"(BaseVideoPort) );

Re: read default cursor width in text mode

Posted: Thu Jun 05, 2014 7:07 am
by sortie
No, because that doesn't solve the problem in a good way and because it demonstrates a critical misunderstanding on your part. You forgot to follow my advise and go an online search and find a good article such as http://blog.llvm.org/2011/05/what-every ... -know.html.

Mind that inline assembly is also UB if you lie to the compiler. That is, if you don't write it in exactly the correct manner (just adding volatiles is not enough, and sometimes they shouldn't even be there) the result is UB and bad things happen. Never write inline assembly, unless you are an expert in inline assembly. It's surprisingly hard to get right. Always reuse community-reviewed inline assembly such as the functions at http://wiki.osdev.org/Inline_Assembly/Examples or just use a regular assembly file.

The problem with UB is that it happens at the C compile level, not at the machine level. Unaligned reads are undefined behavior with gcc, even if the machine actually supports it. This is not a problem in practice, unless you write bad code such as your above example. (It's hard to make an unaligned pointer without invoking UB while doing so, well, I can think of a few ways but still). The solution is quite simply to turn your unaligned read into two smaller aligned reads:

Code: Select all

uint8_t* bda = (uint8_t*) 0x400;
uint16_t base_video_port = bda[0x63] << 0 | bda[0x64] << 8;
Notice how this does no unaligned reads and use exclusively well-defined behaviour. When you find yourself writing inline assembly, you should ask yourself whether it is possible to write well-defined C instead (you need to read that above link for information on UB to avoid this minefield). Mind that UB isn't the same as `platform-dependent' or `implementation-specified'. It means that the standard literally says that anything is allowed to happen. Thus a theoretical compiler that follows the standard is allowed to summon Darth Vader and have him destroy the compiler with a lightsaber whenever you do unaligned reads. The idea is that the compiler is allowed to define undefined behavior (if it wants to) as something implementation-specific, but it is not required to, and many C compilers do not do so in most cases.

Re: read default cursor width in text mode

Posted: Thu Jun 05, 2014 8:13 am
by dlarudgus20
sortie wrote:No, because that doesn't solve the problem in a good way and because it demonstrates a critical misunderstanding on your part. You forgot to follow my advise and go an online search and find a good article such as http://blog.llvm.org/2011/05/what-every ... -know.html.

Mind that inline assembly is also UB if you lie to the compiler. That is, if you don't write it in exactly the correct manner (just adding volatiles is not enough, and sometimes they shouldn't even be there) the result is UB and bad things happen. Never write inline assembly, unless you are an expert in inline assembly. It's surprisingly hard to get right. Always reuse community-reviewed inline assembly such as the functions at http://wiki.osdev.org/Inline_Assembly/Examples or just use a regular assembly file.

The problem with UB is that it happens at the C compile level, not at the machine level. Unaligned reads are undefined behavior with gcc, even if the machine actually supports it. This is not a problem in practice, unless you write bad code such as your above example. (It's hard to make an unaligned pointer without invoking UB while doing so, well, I can think of a few ways but still). The solution is quite simply to turn your unaligned read into two smaller aligned reads:

Code: Select all

uint8_t* bda = (uint8_t*) 0x400;
uint16_t base_video_port = bda[0x63] << 0 | bda[0x64] << 8;
Notice how this does no unaligned reads and use exclusively well-defined behaviour. When you find yourself writing inline assembly, you should ask yourself whether it is possible to write well-defined C instead (you need to read that above link for information on UB to avoid this minefield). Mind that UB isn't the same as `platform-dependent' or `implementation-specified'. It means that the standard literally says that anything is allowed to happen. Thus a theoretical compiler that follows the standard is allowed to summon Darth Vader and have him destroy the compiler with a lightsaber whenever you do unaligned reads. The idea is that the compiler is allowed to define undefined behavior (if it wants to) as something implementation-specific, but it is not required to, and many C compilers do not do so in most cases.
I knew these UB's problem you're saying, but I thought it's almost (not fully) okay and I need to access bios data area, so I did that. (There was no way except that in my brain!)

But your solution, using 1-byte pointer, looks great.. Thank you so much!

Re: Reading the default cursor width in text mode

Posted: Thu Jun 05, 2014 8:29 am
by sortie
Undefined behaviour is never `almost (not fully) okay', it is entirely unacceptable. Don't write fragile code that relies on compiler internals or behaviour that just isn't guaranteed or actually forbidden.

Re: Reading the default cursor width in text mode

Posted: Thu Jun 05, 2014 8:43 am
by Combuster
(ahem) Purism aside,

The exact value in the BDA certainly wasn't used in many a commercial application at the time - especially that of DOS games. It might also not be useful as you can toggle between 3Bx and 3Dx addresses without the BDA value following suit - that's something that needs to be done manually. But the most basic test is to show which value it has exactly, because I wouldn't be able to tell.

But most commonly, the IO ports in question are simply hardcoded, and your additional step is not performed.