First off that linked reference is a bad thing for stack overflow, it could go away and ruin this whole question making it something we have to discard.
Second that reference is clearly about JAVA and JVM and extremely misleading right off the bat, if you dont know this stuff, that is a very bad article to start with.
As a programmer I assume you understand that if you give 100 programmers a programming task you are going to end up with somewhere between 1 and 100 different solutions.
hardware is no different, give 100 chip designers the same design task, you get between 1 and 100 different designs. hardware/board designers, same deal. If you tell the chip designers to invent their own instruction set and design a processor it is going to lean toward 100 different designs. if you give them the instruction set (machine code specification) you will still get between 1 and 100 different designs.
If it is CISC or CISC like you may end up with microcoding, with RISC you could too but that is kind of the idea of RISC to not need/want microcoding.
All programming languages is way too vague. At the same time they are generally converted to some lower level language, and ultimately to something parse-able. We have to be vague with an answer as there are exceptions to every statement I can make about this. But C/C++ are "compiled", generally, into assembly langauge for a particular target, the one the compiler is either told to target and often the one you are running it on, a "cross-compiler" is used to compile for a different target. I dont want to keep using the generally, and mostly and other vague terms...then the assembler converts the assembly language into mostly machine code, but if it is an object then some things are left for the linker to fill in and finalize. but even the .exe or other similar foramats (coff, elf, etc) the majority of the file is the program, machine code and data, but some of the file tells the operating system how to load that program and start it. You have to look up each "binary" file format type to see how they do this. And then in some cases it may still also be operating system dependent.
There is absolutely no reason that you cant generate these executable files directly or in any path you choose. you can use a hex editor (notepad NOT being one of these) and start typing in the bytes for the file, the .exe headers, the machine code, the data, the whole thing. you can certainly write a program in some language compile that and run it and that program can directly create another program, not using a compiler just write bytes out.
JAVA, early Pascal, Python, BASIC for that matter. These dont necessarily (although Pascal does these days) compile directly into "machine code" for the processor you are running on. By design. JAVA and Python desire to run everywhere, operating system independent. They have their own machine code, their compilers compile the source down into a machine code that is not the machine code for the target processor, the one you are running on, but a generic one they invented. Then you have a JVM JAVA Virtual Machine, which is a program, probably not written in JAVA, that IS operating system dependent and reads the bytes of that java machine code and executes it as a processor would. same for python and in the early days the same for pascal. BASIC, traditionally you read the BASIC code at runtime and parse and run it as you go. granted all of these likely have a toolchain someone wrote (or just gcc for some of them) that can instead of compiling to the langauge specific target, instead can be compiled into machine code for the processor you are running on, typically and by design not but, sometimes can be done.
processors themselves are not that much different, just logic that reads the machine code, parses the bits out and does what the bits say to do. just like the JVM does for JAVA, but in hardware and much faster. Sometimes, more likely with CISC (x86), you have microcode, yet another instruction set, exactly like the JVM in the sense that it perhaps with the help of hardware/logic takes the machine code, and in microcoded software breaks that up and executes it. we generally dont get to see that microcode because you take 100 designers and you get 100 results, each generation of processor they dont want to carry around the same microcode, x86 being a perfect example of this, esp since intel has two or more design centers that do their own thing and every other or every third chip design is from one of those design centers, so we keep going between the preferences of those design centers. or at least they used to do that dont know what they do now.
yes you can absolutely write a windows program without visual studio/visual basic. you can use a hex editor and just type in the bytes, you can write in assembly language and assemble it with an assembler (and linker depending) that is not visual anything based (take gnu binutils for example).
as mentioned in other answers and comments, or not. before there were any of these tools "compiling" and "assembling" was a human that wrote down what they wanted to do (probably in a flow chart) then wrote it down again in some form of assembly language, something human readable, then next to that or starting on a new page, wrote the machine code. and then flipped switches or otherwise entered that machine code into the processor and started it. using that method you can eventually write an assembler, and then now that you have an assembler you can write that assembler again in assembler and not hand written machine code. and now that you have an assembler you can invent languages and implement those in assembler, then you can re-write those in the same language and compile them with that langauge. and use that language to make other languages. And cross compile langauges or assemble, for other platforms which you have invented but have no tools and dont want to hand write machine code. repeat this for decades and here we are. even the logic is designed using programming languages verlog and/or vhdl, which clearly are influenced by languages like C and ADA, which came before them.
I take this function, but what is the difference between a function and a program? let you ponder that.
unsigned int fun ( unsigned int a, unsigned int b )
{
return (a+b);
}
I compile it. the compiler produces assembly language as its output
.arch armv5t
.fpu softvfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 2
.eabi_attribute 34, 0
.eabi_attribute 18, 4
.arm
.syntax divided
.file "so.c"
.text
.align 2
.global fun
.type fun, %function
fun:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
add r0, r0, r1
bx lr
.size fun, .-fun
.ident "GCC: (Ubuntu 5.2.1-22ubuntu1) 5.2.1 20151010"
.section .note.GNU-stack,"",%progbits
then I assemble that into an object. a hexdump of that:
0000000 457f 464c 0101 0001 0000 0000 0000 0000
0000010 0001 0028 0001 0000 0000 0000 0000 0000
0000020 019c 0000 0000 0500 0034 0000 0000 0028
0000030 000a 0007 0001 e080 ff1e e12f 4700 4343
0000040 203a 5528 7562 746e 2075 2e35 2e32 2d31
0000050 3232 6275 6e75 7574 2931 3520 322e 312e
0000060 3220 3130 3135 3130 0030 2941 0000 6100
0000070 6165 6962 0100 001f 0000 3505 0054 0306
0000080 0108 0109 0412 0114 0115 0317 0118 0119
0000090 021a 021e 2e00 7973 746d 6261 2e00 7473
00000a0 7472 6261 2e00 6873 7473 7472 6261 2e00
00000b0 6574 7478 2e00 6164 6174 2e00 7362 0073
00000c0 632e 6d6f 656d 746e 2e00 6f6e 6574 472e
00000d0 554e 732d 6174 6b63 2e00 5241 2e4d 7461
00000e0 7274 6269 7475 7365 0000 0000 0000 0000
00000f0 0000 0000 0000 0000 0000 0000 0001 0000
0000100 0000 0000 0000 0000 0004 fff1 0000 0000
0000110 0000 0000 0000 0000 0003 0001 0000 0000
0000120 0000 0000 0000 0000 0003 0002 0000 0000
0000130 0000 0000 0000 0000 0003 0003 0006 0000
0000140 0000 0000 0000 0000 0000 0001 0000 0000
0000150 0000 0000 0000 0000 0003 0005 0000 0000
0000160 0000 0000 0000 0000 0003 0004 0000 0000
0000170 0000 0000 0000 0000 0003 0006 0009 0000
0000180 0000 0000 0008 0000 0012 0001 7300 2e6f
0000190 0063 6124 6600 6e75 0000 0000 0000 0000
00001a0 0000 0000 0000 0000 0000 0000 0000 0000
*
00001c0 0000 0000 001b 0000 0001 0000 0006 0000
00001d0 0000 0000 0034 0000 0008 0000 0000 0000
00001e0 0000 0000 0004 0000 0000 0000 0021 0000
00001f0 0001 0000 0003 0000 0000 0000 003c 0000
0000200 0000 0000 0000 0000 0000 0000 0001 0000
0000210 0000 0000 0027 0000 0008 0000 0003 0000
0000220 0000 0000 003c 0000 0000 0000 0000 0000
0000230 0000 0000 0001 0000 0000 0000 002c 0000
0000240 0001 0000 0030 0000 0000 0000 003c 0000
0000250 002e 0000 0000 0000 0000 0000 0001 0000
0000260 0001 0000 0035 0000 0001 0000 0000 0000
0000270 0000 0000 006a 0000 0000 0000 0000 0000
0000280 0000 0000 0001 0000 0000 0000 0045 0000
0000290 0003 7000 0000 0000 0000 0000 006a 0000
00002a0 002a 0000 0000 0000 0000 0000 0001 0000
00002b0 0000 0000 0011 0000 0003 0000 0000 0000
00002c0 0000 0000 0094 0000 0055 0000 0000 0000
00002d0 0000 0000 0001 0000 0000 0000 0001 0000
00002e0 0002 0000 0000 0000 0000 0000 00ec 0000
00002f0 00a0 0000 0009 0000 0009 0000 0004 0000
0000300 0010 0000 0009 0000 0003 0000 0000 0000
0000310 0000 0000 018c 0000 000d 0000 0000 0000
0000320 0000 0000 0001 0000 0000 0000
000032c
not necessarily for this program but often easier to see what the compiler produced by disassembling the object rather than the verbose assembly.
Disassembly of section .text:
00000000 <fun>:
0: e0800001 add r0, r0, r1
4: e12fff1e bx lr
That happens to have the machine code for us, so we could just type those bytes in. As written this isnt a complete program as it needs an operating system specific entry and exit at a minimum.
I could run this code on this processor I have chosen bare metal, and could for example wrap it with this bootstrap:
.globl _start
_start:
mov sp,#0x8000
bl fun
b .
assemble and link and I get this for example
Disassembly of section .text:
00000000 <_start>:
0: e3a0d902 mov sp, #32768 ; 0x8000
4: eb000000 bl c <fun>
8: eafffffe b 8 <_start+0x8>
0000000c <fun>:
c: e0800001 add r0, r0, r1
10: e12fff1e bx lr
I can convert that into a binary image that could be loaded into ram or flash depending for that processor and run
0000000 d902 e3a0 0000 eb00 fffe eaff 0001 e080
0000010 ff1e e12f
0000014
Not using basic, not using visual studio, not running windows, I can write this program
#include <stdio.h>
static const unsigned int fun[]=
{
0xe3a0d902,
0xeb000000,
0xeafffffe,
0xe0800001,
0xe12fff1e,
};
int main ( void )
{
FILE *fp;
fp=fopen("fun.bin","wb");
if(fp==NULL) return(1);
fwrite(fun,1,sizeof(fun),fp);
fclose(fp);
return(0);
}
compile and run it (yes an exe from an exe) and it produces the same binary file shown in a hexdump here:
0000000 d902 e3a0 0000 eb00 fffe eaff 0001 e080
0000010 ff1e e12f
0000014
I could also just take a hex editor and just type those bytes in. and not use a language to create this binary file. although it is still an exe from an exe because I used a hexeditor which is a program. and I say exe here loosly as this is not in the .exe file format, but exe in the sense that it is an executable program although mostly useless.
yes I am well aware that I didnt load r0 nor r1 before calling fun, doesnt matter processor registers wake up with bits in them (except in some silicon simulations which are not two state but three, four, or more state and depending on the logic design) so the add will work.