OSDev.org

The Place to Start for Operating System Developers
It is currently Wed Apr 24, 2024 8:32 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: How can I implement a sleep function without interrupts?
PostPosted: Fri Aug 31, 2018 11:56 pm 
Offline

Joined: Fri Aug 31, 2018 11:43 pm
Posts: 3
I'm playing around with writing a kernel, and I would like to be able to play sound from the speaker. I need the tones to be specific lengths, so I need some sort of sleep function. I've tried using INT 15h, 86h to sleep but the system crashes at the INT instruction and reboots. This happens both in QEMU and on real hardware. I'm not using interrupts (yet), so I'm guessing I can't use INT. Here is my assembly:
Code:
usleep:
    push ebp
    mov ebp,esp
    mov eax,ebp
    mov ah,0x86
    mov edx,eax
    int 0x15
    mov esp,ebp
    pop ebp
    ret
It's defined in my C code as:
Code:
extern void usleep(int);
I'm not very experienced in x86 assembly in general, so I'm not sure if this code works how I think it does. How could I sleep an amount of time without using interrupts? (If it's simply impossible without interrupts, please provide instructions on how to enable interrupts because the wiki page on interrupts was a bit too complicated for me to understand.)


Top
 Profile  
 
 Post subject: Re: How can I implement a sleep function without interrupts?
PostPosted: Sat Sep 01, 2018 12:55 am 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

JackMacWindows wrote:
I'm playing around with writing a kernel, and I would like to be able to play sound from the speaker. I need the tones to be specific lengths, so I need some sort of sleep function. I've tried using INT 15h, 86h to sleep but the system crashes at the INT instruction and reboots. This happens both in QEMU and on real hardware. I'm not using interrupts (yet), so I'm guessing I can't use INT. Here is my assembly:
Code:
usleep:
    push ebp
    mov ebp,esp
    mov eax,ebp
    mov ah,0x86
    mov edx,eax
    int 0x15
    mov esp,ebp
    pop ebp
    ret


You're in protected mode, and the BIOS is designed for real mode, so none of the BIOS functions ("ints") will work.

To sleep, you need a timer IRQ and a scheduler.

To delay you only need a counter that you can poll. Depending on which ranges of computers you support (how old or new) the possible counters (in order of best to worst, for both effective precision and overhead to access) are:
  • CPU's TSC
  • HPET
  • ACPI's counter
  • PIT's counter
  • CMOS real time clock

The general idea would be something like "while(get_count() < start_time + delay) {}"; but for some of the counters you have to worry about the counter rolling over (e.g. PIT's count is only 16 bits and will roll over every ~55 milliseconds), for all of them you'll need to scale "delay" to match how often the counter is incremented or decremented, and for some of them (e.g. CPU's TSC) you'll have to measure how often the counter is incremented or decremented using some other counter or timer.


Cheers,

Brendan

_________________
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.


Top
 Profile  
 
 Post subject: Re: How can I implement a sleep function without interrupts?
PostPosted: Sat Sep 01, 2018 12:56 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4594
Location: Chichester, UK
You can read the current value of the timer chip, so I guess you could use polling but that wouldn't be very efficient.

I think your real problem is trying to run before you can walk. Any semi-serious OS is going to need to use interrupts so I think you should devote your time to understanding how they work. It's then fairly trivial to program the timer to interrupt after a given time interval. More likely you will have continuous interrupts every so many ms and part of the timer interrupt will check if any counters set for delays have reached zero.

Note that this is nothing to do with BIOS or DOS interrupts (software interrupts). It is a question of programming a particular event (key pressed, sector read from disk, timer elapsed, etc.) to inform the processor that it has occurred.


Top
 Profile  
 
 Post subject: Re: How can I implement a sleep function without interrupts?
PostPosted: Sat Sep 01, 2018 7:50 am 
Offline
Member
Member
User avatar

Joined: Fri Oct 27, 2006 9:42 am
Posts: 1925
Location: Athens, GA, USA
sigh And this why I never liked the term 'software interrupt' for System Calls. Feel free to skip the rant coming up; my reply about the specific problems you are looking at will follow that.

/me pulls out soapbox and hopes it won't break under my weight if I stand on it
While the mechanism used for calling system services in real mode x86 (and several other CPU architectures, including ARM and MIPS) is sort of the same as that for interrupts (e.g., they both use the same or similar lookup table mechanisms), the purpose of them is quite different.

Unfortunately, this is another one of the instances where no one seems to agree on their terminology, and each ISA has it's own name for the service call instruction (if it has one). On x86, it's called INT; if memory serves, on 68K it's TRAP; going from memory again, on the old IBM mainframes it was SVC ('SerVice Call'); on MIPS, it's SYSCALL; and on ARM, it was originally SWI ('SoftWare Interrupt'), but this was later deprecated in favor of the alias SVC, with both being accepted by most ARM assemblers today.

And that's not even getting into the three other mechanisms for implementing system calls which were later introduced for x86 protected mode: Traps (which are completely unrelated to the 68K instruction mentioned above, how nice); Call Gates (which are a bit hairy to get into right now); and the SYSENTER instruction (which for maximum confusion, gets renamed in long mode to SYSCALL). Or the various software-based approaches which different x86 OSes have relied on over the years, some of which have been brain-breakingly weird.
steps off of soapbox

OK, back to the matter at hand.

First off, what both Brendan and iansjack said already is pretty much the heart of the issue, at least on the surface: it sounds as if this just isn't something you are ready to address in your system, for a few different reasons.

Part of the reason we are having trouble answering this is because you don't mention whether you are in real mode or not. This is a crucial question with the given code, since - as has already been stated - all of the standard BIOS calls are written for 16-bit real mode, and won't run in any form of protected or long mode without heroic efforts (e.g., setting up Virtual 8086 support, which would only work if you already have p-mode interrupt handling set up). Furthermore, interrupt handling in protected mode is radically different from interrupt handling in real mode, meaning that the interrupt vectors to the BIOS calls found in the real mode Interrupt Vector Table won't even exist in your p-mode Interrupt Descriptor Table unless you put them there (and there's no sane reason to do so in most cases).

As an aside, according to the RBIL entry discussing that interrupt vector, the interrupt only gives a timing resolution of 997 microseconds, or just a little better than a tenth of a second. This probably is fast enough for your intended use here, but not even remotely fast enough for a general sleep() routine, and certainly not fast enough for the function usually indicated by the name usleep() (where the 'u' is an ASCII-ism for the Greek letter mu (μ), short for micro - in other words, a sleep with a microsecond resolution). I mean, call it what you want, you do you, but be aware that you'll have trouble getting help from others if you use common function names in unexpected ways.

But all of that may be irrelevant, for one simple reason: as far as I know, most motherboards don't even have a PC Speaker of that type any more, and modern Sound systems (most current ones being based on the Intel High Definition Audio spec) work in a completely different manner from the old PC speaker subsystem of the original IBM PC. While my understanding is that most implementations of AC97 and later sound standards will still emulate the PC speaker, AFAIK it isn't required by those standards, so there's no guarantee that your hardware does or not. If anyone can correct or clarify this point, please do so.

Maybe we need to start over, as this is getting into 'XY Problem' territory. Can you tell us what you want to use the speaker for?

_________________
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.


Top
 Profile  
 
 Post subject: Re: How can I implement a sleep function without interrupts?
PostPosted: Sat Sep 01, 2018 10:28 am 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

Schol-R-LEA wrote:
But all of that may be irrelevant, for one simple reason: as far as I know, most motherboards don't even have a PC Speaker of that type any more, and modern Sound systems (most current ones being based on the Intel High Definition Audio spec) work in a completely different manner from the old PC speaker subsystem of the original IBM PC. While my understanding is that most implementations of AC97 and later sound standards will still emulate the PC speaker, AFAIK it isn't required by those standards, so there's no guarantee that your hardware does or not. If anyone can correct or clarify this point, please do so.

Maybe we need to start over, as this is getting into 'XY Problem' territory. Can you tell us what you want to use the speaker for?


I'm not sure about JackMacWindows; but I use PC speaker to try to alert the user that the OS can't boot (especially for headless systems where there's no video), with different noises at different stages (e.g. continuous tone if 1st stage boot loader can't continue, 1 second beeps if 2nd stage can't continue, etc). Of course in this case if it doesn't work it can't make anything worse.

Note that you can have multiple sound cards plus PC speaker (e.g. this computer has PC speaker plus 3 sound cards - one built into the motherboard and one in each of the two video cards, where there's no speakers plugged into any of the sound cards and PC speaker is the only thing that actually works). There's no reason for any sound card to support or emulate PC speaker (they are all completely separate devices); and a more likely problem is that the PIT chip is being emulated by HPET (but it's still rare - I don't think any of my computers actually do this).


Cheers,

Brendan

_________________
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.


Top
 Profile  
 
 Post subject: Re: How can I implement a sleep function without interrupts?
PostPosted: Sat Sep 01, 2018 3:27 pm 
Offline

Joined: Fri Aug 31, 2018 11:43 pm
Posts: 3
Schol-R-LEA wrote:
Part of the reason we are having trouble answering this is because you don't mention whether you are in real mode or not. This is a crucial question with the given code, since - as has already been stated - all of the standard BIOS calls are written for 16-bit real mode, and won't run in any form of protected or long mode without heroic efforts (e.g., setting up Virtual 8086 support, which would only work if you already have p-mode interrupt handling set up). Furthermore, interrupt handling in protected mode is radically different from interrupt handling in real mode, meaning that the interrupt vectors to the BIOS calls found in the real mode Interrupt Vector Table won't even exist in your p-mode Interrupt Descriptor Table unless you put them there (and there's no sane reason to do so in most cases).

As far as I know, I am running in real mode. I don't switch to protected mode anywhere, but I'm able to address more than 1 MB of RAM using the manual memory probing code from Detecting Memory (x86). (I'm using manual probing because I can't use BIOS calls, and even though I'm aware it's slow, I just wanted to do something other than printing a string.)

Schol-R-LEA wrote:
As an aside, according to the RBIL entry discussing that interrupt vector, the interrupt only gives a timing resolution of 997 microseconds, or just a little better than a tenth of a second. This probably is fast enough for your intended use here, but not even remotely fast enough for a general sleep() routine, and certainly not fast enough for the function usually indicated by the name usleep() (where the 'u' is an ASCII-ism for the Greek letter mu (μ), short for micro - in other words, a sleep with a microsecond resolution). I mean, call it what you want, you do you, but be aware that you'll have trouble getting help from others if you use common function names in unexpected ways.

I originally named it sleep(), but I changed it because 1) I thought maybe something else was named 'sleep' (which is false), and 2) I thought it would be giving me μs-precision, but I see that that is not the case. I don't need the timer to be that precise, as I only need around 100 ms.

Schol-R-LEA wrote:
But all of that may be irrelevant, for one simple reason: as far as I know, most motherboards don't even have a PC Speaker of that type any more, and modern Sound systems (most current ones being based on the Intel High Definition Audio spec) work in a completely different manner from the old PC speaker subsystem of the original IBM PC.
Maybe we need to start over, as this is getting into 'XY Problem' territory. Can you tell us what you want to use the speaker for?

I am aware of many motherboards not having a PC speaker; in fact, my main machine doesn't have one (though I have considered adding one at some point). The reason I want to use it is for the '\a' escape code; one use being to beep when trying to backspace with nothing typed in.

I attached my (almost) complete code at the bottom, so you can review it more thoroughly. For now, I'm going to just use a for loop since I don't need exact timing, though in the future I may need better control.


Attachments:
File comment: Source code
kernel-src.tar.gz [5.13 KiB]
Downloaded 25 times
Top
 Profile  
 
 Post subject: Re: How can I implement a sleep function without interrupts?
PostPosted: Sat Sep 01, 2018 6:46 pm 
Offline
Member
Member
User avatar

Joined: Fri Oct 27, 2006 9:42 am
Posts: 1925
Location: Athens, GA, USA
OK, before I continue, I want to ask one question: how are you handling Code Management, and specifically, have you set up version control for your code (using a VCS tool such as Subversion, Git, BitBucket, or Mercurial), and if you have, do you have an offsite repo on a free repo host such as GitHub, SourceForge, CloudForge, or Git?

I ask this mainly because many newcomers here don't realize the importance of VCS, and the fact that you posted the archive file instead of a link to a repo has me concerned. Trust me, if you don't have that set up already, you would be wise to drop everything else on this project (and any other software projects you are working on that are bigger than "Hello, World" or something like that) and set up an account on a repo host. Which toolset and host you use is your choice, but if you don't have something to protect your code and keep track of the change history, it will come back to bite you.

Now then, about the code itself.

Looking at the kernel.asm the first things that I see is
Code:
;nasm directive - 32 bit
bits 32
section .text
        ;multiboot spec
        align 4
        dd 0x1BADB002            ;magic
        dd 0x00                  ;flags
        dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero


The bits 32 directive, and the comment referring to a MultiBoot header, tells me that the system is in fact going to be in 32-bit protected mode at this point. I am assuming you are using a Multiboot compatible boot loader, probably GRUB, and that it is loading the ELF file with the compiled and linked code, correct? And if I may speculate further, that you compiled the C code with GCC?

If this is all correct, then yes, you are definitely running in 32-bit p-mode. GRUB always siwtches to 32-bit protected mode before loading a kernel image unless specifically directed otherwise, and the default loadable format for GRUB is ELF32. Furthermore, on the x86 platform, GCC really only targets 32-bit and 64-bit code. While neither of those is absolute (GRUB can always chain-load formats it doesn't know, and I've been told that it is possible to coax GCC to target real mode), you would know it if that's what you were doing.

So, this pretty rules out the possibility of calling the BIOS. It also means that your next order of business more or less must be to move the memory and interrupt management from the minimal GDT and IDT which GRUB sets up, to ones organized whichever your OS will need them. You won't be able to progress much further until you have that in place.

There are alternatives, but they all would amount to starting over from scratch, including either writing your own bootloader, or finding one for a 16-bit real mode kernel which you could adapt. You would also need to find a 16-bit real mode C compiler, if you do that. I personally wouldn't recommend it.

Sorry to be the bearer of bad news, but this is pretty much where things stand now.

One final question to bookend the first: are you following a tutorial of any sort, and if so, which one?

_________________
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.


Top
 Profile  
 
 Post subject: Re: How can I implement a sleep function without interrupts?
PostPosted: Sat Sep 01, 2018 10:12 pm 
Offline

Joined: Fri Aug 31, 2018 11:43 pm
Posts: 3
Schol-R-LEA wrote:
OK, before I continue, I want to ask one question: how are you handling Code Management, and specifically, have you set up version control for your code (using a VCS tool such as Subversion, Git, BitBucket, or Mercurial), and if you have, do you have an offsite repo on a free repo host such as GitHub, SourceForge, CloudForge, or Git?
I wasn't planning on really releasing the code, but since you say it's a good idea I've put it up at https://github.com/MCJack123/thin-krnl. The intention is to have something similar to a BASIC interpreter. (I might use BASIC, but I'm not sure right now how the syntax will work as I want to get the basic drivers out of the way first.)
Schol-R-LEA wrote:
The bits 32 directive, and the comment referring to a MultiBoot header, tells me that the system is in fact going to be in 32-bit protected mode at this point. I am assuming you are using a Multiboot compatible boot loader, probably GRUB, and that it is loading the ELF file with the compiled and linked code, correct? And if I may speculate further, that you compiled the C code with GCC?

If this is all correct, then yes, you are definitely running in 32-bit p-mode. GRUB always siwtches to 32-bit protected mode before loading a kernel image unless specifically directed otherwise, and the default loadable format for GRUB is ELF32. Furthermore, on the x86 platform, GCC really only targets 32-bit and 64-bit code. While neither of those is absolute (GRUB can always chain-load formats it doesn't know, and I've been told that it is possible to coax GCC to target real mode), you would know it if that's what you were doing.
I am indeed compiling the C with GCC. But when I test it, I use qemu with the -kernel option, which probably sets it into protected mode. When running on real hardware, though, I use GRUB.
Schol-R-LEA wrote:
So, this pretty rules out the possibility of calling the BIOS. It also means that your next order of business more or less must be to move the memory and interrupt management from the minimal GDT and IDT which GRUB sets up, to ones organized whichever your OS will need them. You won't be able to progress much further until you have that in place.

There are alternatives, but they all would amount to starting over from scratch, including either writing your own bootloader, or finding one for a 16-bit real mode kernel which you could adapt. You would also need to find a 16-bit real mode C compiler, if you do that. I personally wouldn't recommend it.

Sorry to be the bearer of bad news, but this is pretty much where things stand now.
Oh well, I guess I'll go get started with writing ~memory and~ interrupt managers. What resources are available where I can see how to implement them? I'm not sure exactly what I should be looking for.
Schol-R-LEA wrote:
One final question to bookend the first: are you following a tutorial of any sort, and if so, which one?
I got the base code from https://arjunsreedharan.org/post/827107 ... e-a-kernel and built up other code from the wiki to make what I have now.

EDIT: I forgot to mention this, but I'm using liballoc as my memory manager. Right now, the allocation is just simply giving the next address available and not freeing anything (this is temporary), but for anyone else who is looking for memory management give it a try. (The ~ represents strikethrough since [s] isn't supported here.)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 215 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group