It’s certainly possible – I did it once, when I designed electronics to add a floppy drive to my TRS-80 computer.
The TRS-80 ran only programs written with BASIC and could only load and save files to an audio cassette. The ‘standard’ operating system of the day was CP / M – but the memory architecture of the TRS-80 made CP / M impossible to run on.
BASIC was too slow to write a floppy disk driver.
So I had to write my own operating system to get my homemade floppy interface working.
I had no programming language other than BASIC.
The first step was to write a simple Z80 assembler in BASIC so that I could write the assembly language and get the machine code. My assembler was VERY rough – he only composed about half of the Z80 instruction set and did not even allow you to comment! But I knew that I would soon throw away this code – so it’s not worth spending much time on.
I have EPROM memory and let the assembler write its output to the EPROM – and when it was ready to jump to the start of the EPROM to “boot” my code.
EPROMs are “read only” memory – and to erase them for reuse, you had to bake them under bright UV light for 20 minutes! So the cycle time for bug fixes depended on the number of EPROM chips I had! Loading my BASIC Assembler audio tape – Loading the assembler source code audio tape was a terrible business.
At about that time, I bought a second (used) TRS-80 so I could edit and assemble the assembly source code on a computer – by reading the other computer’s EPROM without having to reload tape every time!
I start using assembly language software to start my operating system – to read the keyboard, to write on the screen as a large scrolling text window, to read and write blocks to the floppy drive, and to format a blank floppy disk , At the beginning of the EPROM I use a table with jump instructions.
Since this process was quite lengthy and I could only write assembler, I decided to keep things simple. I did not need any hierarchical directories – and it’s unlikely that there are more than a few hundred files stored on the floppy drive – so I saved a “hard disk directory” of fixed size with 256 entries – each with a list of disk blocks, a short list, filename fixed length and a flag that means “DELETED” or not. I have also saved a bitmap of the hard disk blocks that indicate to each hard drive block whether it is being used or is free.
With this function, I read a RAM file at a specific location – to read a named file at a specific location in the RAM and jump to a specific location.
This allowed me to write a primitive “shell” program where you could type the name of a file with a list of parameters and load it from diskette drive into memory and then execute it. This was also in the EPROM. The operating system always initializes itself and then starts the shell.
Finally I was able to boot my TRS-80 into BASIC – which started the operating system and immediately put it into a “shell”.
The TRS-80 did not have a memory management chip-so the shell always loaded programs with the same memory address-and you could run only one program at a time. When the program finishes, only the operating system is restarted, and you get a shell prompt again!
My next step was to rewrite my assembler program from BASIC to assembly language so I could put programs together from the hard drive and save the result to my hard drive.
From that point on, I was able to write a program file to the directory, copy a file, delete a file, format a disk, copy a disk (I only had one hard disk drive and not much RAM – so that meant a lot of exchange in and out of data carriers!) … and so on.
I wrote a super simple text editor that let you edit one line at a time so that it was also ported to my subset of language assembly.
I had already begun writing a compiler for a tiny subset of the Pascal language (TINY!) – but doing that in language assembly was quickly getting old. A human-readable language assembly (in my Z80 subgroup) was written. So as soon as I’ve done it halfway, I’ve written it in the same Pascal subset