A training loader in pure asm that simulates some shell behavior

Processable commands are:
    time (it really prints BIOS time)
    ls (it prints string similar to output of ls in tiny filesystem)
    cat /usr/file.txt (it just reprints filename without '.txt' extension)

Building:
    clang -c -o boot.o boot.s -- builds object file 'boot.o'
    ld -o boot.bin --oformat binary boot.o -- makes binary block of 512 bytes

That file 'boot.bin' can be written for example on usb flash using dd so make
it able to boot due to last bytes of block equal to 0x55aa.

To boot in QEMU:
    qemu-system-i386 boot.bin

=====

.code16
.text
  .global _start
  
_start:

shell:
  movl %esp, %ebx  # save start of stack
  movb $0x0e, %ah  # put the number of BIOS-function of printing symbol to %ah
  movb $0x0a, %al  # put hexadecimal ASCII code of 'new line' to %al
  int  $0x10       # and call 'print character' interrupt
  movb $0x0d, %al  # print 'carriage return'
  int  $0x10
  movb $0x24, %al  # print '$' character
  int  $0x10
  movb $0x20, %al  # print space too
  int  $0x10
read_chars:        # main loop of reading string and saving it to the stack
  movb $0x00, %ah  # read pressed char
  int  $0x16
  push %ax
  movb $0x0e, %ah  # print pressed char for interactivity
  int  $0x10
  cmp  $0x0d, %al  # call router when Enter pressed
  je   router
  jmp  read_chars

router:
  pop  %ax
  pop  %ax
  cmp  $0x65, %al  # check if last letter in shell was e
  je   time_check
  cmp  $0x73, %al  # check if last letter in shell was s
  je   ls_check
  cmp  $0x74, %al  # check if last letter in shell was t
  je   txt_check
time_check:
  pop  %ax
  cmp  $0x6d, %al  # check if last letter before e was m
  je   time_check2
time_check2:
  pop  %ax
  cmp  $0x69, %al  # check if last letter before m was i
  je   time_check3
time_check3:
  pop  %ax
  cmp  $0x74, %al  # check if last letter before i was t
  je   time
ls_check:
  pop  %ax
  cmp  $0x6c, %al  # check if last letter before s was l
  je   ls
txt_check:
  pop  %ax
  cmp  $0x78, %al  # check if last letter before t was x
  je   txt_check2
txt_check2:
  pop  %ax
  cmp  $0x74, %al  # check if last letter before x was t
  je   txt
jmp  shell

time:
  movb $0x0e, %ah
  movb $0x0a, %al     # new line
  int  $0x10          # and
  movb $0x0d, %al     # carriage return
  int  $0x10
  clc
  movb $0x02, %ah     # read RTC clock
  int  $0x1a
  
  movb $0x0e, %ah
  push %cx
  shrb $4,    %ch
  add  $0x30, %ch
  movb %ch,   %al     # print first digit of hours in decimal
  int  $0x10
  pop  %cx

  push %cx
  shlb $4,    %ch
  shrb $4,    %ch
  add  $0x30, %ch
  movb %ch,   %al   # second digit of hours
  int  $0x10
  pop  %cx

  movb $0x3a, %al   # print colon between hours and minutes
  int  $0x10

  push %cx
  shrb $4,    %cl
  add  $0x30, %cl
  movb %cl,   %al   # print minutes
  int  $0x10
  pop  %cx

  push %cx
  shlb $4,    %cl
  shrb $4,    %cl
  add  $0x30, %cl
  movb %cl,   %al
  int  $0x10
  pop  %cx

  jmp  shell

ls:
  movb $0x0e, %ah
  movb $0x0a, %al  # new line
  int  $0x10       # and
  movb $0x0d, %al  # carriage return
  int  $0x10
  movb $0x2f, %al  # /
  int  $0x10
  movb $0x62, %al  # b
  int  $0x10
  movb $0x69, %al  # i
  int  $0x10
  movb $0x6e, %al  # n
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x2f, %al  # /
  int  $0x10
  movb $0x64, %al  # d
  int  $0x10
  movb $0x65, %al  # e
  int  $0x10
  movb $0x76, %al  # v
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x2f, %al  # /
  int  $0x10
  movb $0x6c, %al  # l
  int  $0x10
  movb $0x69, %al  # i
  int  $0x10
  movb $0x62, %al  # b
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x2f, %al  # /
  int  $0x10
  movb $0x75, %al  # u
  int  $0x10
  movb $0x73, %al  # s
  int  $0x10
  movb $0x72, %al  # r
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x20, %al  # space
  int  $0x10
  movb $0x2f, %al  # /
  int  $0x10
  movb $0x6b, %al  # k
  int  $0x10
  movb $0x65, %al  # e
  int  $0x10
  movb $0x72, %al  # r
  int  $0x10
  movb $0x6e, %al  # n
  int  $0x10
  movb $0x65, %al  # e
  int  $0x10
  movb $0x6c, %al  # l
  int  $0x10
  jmp  shell

txt:
  movb $0x0e, %ah
  movb $0x0a, %al  # new line
  int  $0x10       # and
  movb $0x0d, %al  # carriage return
  int  $0x10
  sub  $0x12,%ebx  # go to address of first char in 'filename'
txt_char:
  sub  $0x02,%ebx
  movw (%ebx),%ax  # print chars of 'filename'
  cmp  $0x2e, %al  # if dot is found go back to 'shell'
  je   shell
  movb $0x0e, %ah
  int  $0x10
  jmp  txt_char

.fill 510-(.-_start), 1, 0  # fill the rest of bytes with zeroes

.byte 0x55   # last two bytes must be 55 and aa for BIOS recognizing
.byte 0xaa   # that our 512 bytes of data are able to boot

=====

References:
https://medium.com/@g33konaut/writing-an-x86-hello-world-boot-loader-with-assembly-3e4c5bdd96cf
http://www.ctyme.com/intr/rb-2273.htm
https://en.wikipedia.org/wiki/X86_instruction_listings