The question is quite broad and as it stands the answer is yes. In fact, the answer is yes for most programming languages but there is a catch.
You need to translate your OS code into something that the machine understands. And it is the machine and its native language and your capability to translate from "your" language to the "native" language that would determine if you can write an operating system in your language or not.
General Theory
Let's say a machine has a lever. And only two things are possible -when the lever is pushed down the machine shall pour coffee and when pulled up the machine shall stop pouring coffee. Now imagine my native language translator is a human who can read C code printed on a paper and decide whether to push the lever up or down. So, for example upon reading the following line our human "compiler-linker" will push the lever down.
printf("pour coffee"); //Support more similar syntax in later versions!
And on reading the following line our human "compiler-linker" will pull the lever up.
printf("stop pouring coffee"); //Support more similar syntax in later versions!
We have successfully written an operating system for this coffee machine entirely in C. For that matter, we can train our compiler to translate Java, C#, KSH, Perl, English, Slavik, Sanskrit etc… and "operate" the machine. Hence the term, Operating System.
Of course most modern computers are capable of doing something more complex than this. And hence the complexity of their instruction set goes beyond just a simple lever state. These instruction sets will ultimately just put some transistors/magnets/LEDs etc. in some state and we humans will interpret that as a result.
The problem hence becomes one of manipulating this machine to achieve the states we want. And if your language (e.g., C) can be translated into that native instruction set and if you can use this bridge to make the machine do what you want then you shall have a successful operating system.
Concrete Example
I am running an x86_64 system right now. This system does not know what C is. It does not know what OS X, Windows, or BSD are. It is ultimately running some x86_64 assembly code. That code was not written by humans (for most part). It was probably written by humans in C or C++ and translated into the assembly language. If I copy this compiled code on to my power PC machine it will not work. I need the original C/C++/whatever_language codebase and I need to translate that code to the power PC language. If I am trying something in C that is not translatable to power PC language then that feature will not work on the new operating system.
What about devices?
The more devices you plug into the machine (e.g., a monitor, a mouse, a printer, an audio output etc.) the more code you will have to write for the bridges. That is what the drivers do. You can write a driver in C if you want. All you need is a way to translate C into the "instruction set" of that device. Usually, it is easier to just embed some code/circuit into the device that reads standard electrical signals and trick the core computer into generating those electrical signals using the standard instruction set and device buses (e.g., USB).
What about bootstrapping?
Now of course, the operating system these days "ships" with the machine. And what people do is just install the code [compiled into the instruction set language of the machine] to some place where the machine can find it on its own. The trick is just to bootstrap the machine to find that code when booting it up. A JVM bootstrap example is below:
public final class Hello {
public static void main(String[] args_) {
System.out.println("Hello, World!");
}
}
0000: cafe babe 0000 002e 001a 0a00 0600 0c09 ................
What about… ?
Please see Tanenbaum or ask another question.
hlt
.