# Very simple OS in Assembly

I made a very simple OS in Assembly. But my code is too long. I want to make my code shorter.

Here is my code:

    BITS 16

start:
mov ax, 07C0h
mov ss, ax
mov sp, 4096

mov ax, 07C0h
mov ds, ax

mov si, text_string
call print_string

jmp $text_string db 'OS', 0 print_string: mov ah, 0Eh .repeat: lodsb cmp al, 0 je .done int 10h jmp .repeat .done: ret times 510-($-$$) db 0 dw 0xAA55  Makefile: DIR=build build (shell mkdir -p (DIR)) (shell nasm -f bin OS1.asm -o build/OS1.flp) (shell mkisofs -no-emul-boot -o build/OS1.iso -b OS1.flp build)  Can I make this code shorter? Is it possible to do? If it is, how? (This code may be updated in my GitHub account.) • If you scale this OS up, then the way to make it shorter is to compile (most of) it in C, with assembly sections only as needed. Nov 20, 2020 at 16:37 • What do you mean with "OS"? Nov 21, 2020 at 19:57 • @superbrain Operating System Nov 22, 2020 at 7:24 • Ok, that's what I thought. But an operating system in 20 lines of assembly rather seems way too short to be true rather than too long. I wrote a lot of assembly for years but that was over 20 years ago, so now I can't understand what your code does but I'm curious. What does it do? Nov 22, 2020 at 13:06 ## 2 Answers Can I make this code shorter? • If you use the ORG 7C00h directive, then loading the segment registers will be shorter code. • You can choose to setup the stack below the bootloader and shave off that addition. Even if you leave the stack where you placed it before (above the bootloader), with SS=0 the change would become mov sp, 9E00h. • You avoid having to load the string address (and the need to come up with suitable label names) if you store the actual string directly beneath the call instruction and have the print_string routine jump back to behind the terminating zero. This technique becomes more interesting if more than 1 string is to be printed.  BITS 16 ORG 7C00h start: cld <--- Not shorter but better! xor ax, ax mov ds, ax mov ss, ax mov sp, 7C00h call print_string db 'OS', 0 jmp  print_string: pop si mov bx, 7 <--- Not shorter but better! mov ah, 0Eh .repeat: cs lodsb ; Need CS override since SI is an offset in code segment! cmp al, 0 je .done int 10h jmp .repeat .done: jmp si times 510-(-$$) db 0
dw 0xAA55


The CS segment override on the LODSB instruction is required unless you know that DS == CS. Many bootloaders will have a far jump at the top to ensure that this is the case. e.g. jmp 0000h:7C05h as the very first instruction. This will set CS=0.

## Not shorter but better

• Adding a CLD instruction makes sure that the LODSB instruction will function correctly.
• Passing all the required parameters to the BIOS.Teletype function is best. BH is DisplayPage and BL is GraphicsColor. If you know that the screen is in a text video mode then you can drop the setting for BL and just write mov bh, 0 to select the video page.

You initialize ax with the same value twice, so you could save one command by rearranging the code bit:

mov ax, 07C0h
mov ds, ax
mov ss, ax


You could save 5 commands if you give up on print_string being a callable function and just inline its code. You won't need to initialize the stack registers and you won't need the call and ret commands.

Of course, this will preclude reuse of the function if you decide to expand your OS.

There is also a middle ground, though I think it is a bit hacky:
Keep a "function like" structure for print_string but avoid using call and ret.

Instead, save the return address to dx and use jmp.

I haven't actually tested this, but it does compile and should work:

   BITS 16

start:
mov ax, 07C0h
mov ds, ax

mov si, text_string
mov dx, return_target
jmp print_string
return_target:
jmp \$

text_string db 'OS', 0

print_string:
mov ah, 0Eh

.repeat:
lodsb
cmp al, 0
jne .do_print
jmp dx

.do_print:
int 10h
jmp .repeat


You will be essentially creating your own non-standard calling convention, but you will save 3 commands as a result and still keep your printing code reusable.

• I see the benefit from not spending instructions on creating a stack but why avoid using a stack? The int 10h already uses the stack and so can call print_string. Nov 21, 2020 at 17:20
• @SepRoland the OP asked how to make the code shorter, I interpreted it as "do the same thing with less instructions". I don't claim this code to be good in any way. Regarding the int instruction, I may have forgoten some some details as it has been a long time since I touched on assembly... So to be clear: will my proposed code result in some undefined behavior when int 10h is called due to stack registers being in unknown state? Nov 21, 2020 at 17:47
• The BIOS will have set up a stack, but it might not be very large (the original IBM PC used 256 bytes at the end of the Interrupt Vector Table). So while you can get away without setting up your own stack here, you cannot rely on using it for very long. Nov 21, 2020 at 18:01