1
votes

I am creating a bootloader in assembly language; having it create a Christmas tree using a loop and using linux bash to run it, but i am facing a problem when trying to subtract from the register so the loop would terminate. I tried creating a function in order to decrease the value of the register but the function Trunk only runs once

[BITS 16] 
[ORG 0x7C00] 
top: 
    ;; Put 0 into ds (data segment) 
    ;; Can't do it directly 
    mov ax,0x0000 
    mov ds,ax 
    ;; si is the location relative to the data segment of the 
    ;; string/char to display  
    mov ch, 5 ;
    mov cl, 1 ;
    call Pyramid
    mov ch, 4 ;
    mov cl, 3
    mov al, 2
    call Trunk
    jmp $ ; Spin 
    
Pyramid:
    mov dh, ch ; print the dot 
    mov dl, cl; dec ch
    call SPACE;
    call DO;
    dec ch;
    add cl, 2;
    mov si, cr 
    call writeString ; See below 
    cmp ch, 0 ; compare to see if what is store in ch is 0
    jne Pyramid ; if ch does not contain 0 call dotsLoop again.
    ret ;
    
Trunk: 
    mov dh, ch;
    mov dl, cl;
    call SPACE
    call DO
    call TLevel
    mov si, cr
    call writeString
    cmp al, 0
    jmp Trunk
    ret ;
    
TLevel:
    dec al
    ret 
SPACE:
    mov si, S;
    call writeString;
    dec dh;
    cmp dh, 0;
    jne SPACE;
    ret;
DO:
    mov si, D;
    call writeString;
    dec dl;
    cmp dl, 0;
    jne DO;
    ret;
writeString: 
    mov ah,0x0E ; Display a chacter (as before) 
    mov bh,0x00 ;
    mov bl,0x07; 
nextchar: 
    Lodsb ; Loads [SI] into AL and increases SI by one 
    ;; Effectively "pumps" the string through AL 
    cmp al,0 ; End of the string? 
    jz done 
    int 0x10 ; BIOS interrupt 
    jmp nextchar 
done: 
    ret 
    S db ' ',0 ; Null-terminated 
    D db '*',0 ;
    cr db 13,10,0;
    times 510-($-$$) db 0;
    dw 0xAA55
1
This is a bootloader. You can't run it as a native Linux executable. It would crash when it reached an int 0x10, if you built it into an ELF executable. I assume that's not what you're actually doing and you just described it wrong. Hopefully you're running it in something with a debugger that lets you single step. - Peter Cordes

1 Answers

1
votes

I tried creating a function in order to decrease the value of the register but the function Trunk only runs once

In your TLevel routine you use the AL register for counting the iterations of Trunk, but you forget that all of those calls to SPACE, DO, and writeString clobber the value in AL.

Either select a different register as a counter or preserve the AX register in the writeString procedure.

writeString:
    PUSH AX       ; PRESERVE AX
    mov  ah, 0x0E 
    mov  bx, 0x0007 
nextchar: 
    Lodsb 
    cmp  al, 0
    je   done 
    int  0x10
    jmp  nextchar 
done:
    POP  AX       ; RESTORE AX
    ret

Trunk:
  ...
  cmp al, 0
  jmp Trunk
  ret

In order to actually loop depending on the counter in AL, you need to replace that unconditional jump jmp by the conditional jump jne.

Do note that having a separate routine just to dec al is wasteful.
Why don't you write Trunk this way:

Trunk: 
    mov  dx, cx   ; LOAD BOTH COUNTERS IN ONE INSTRUCTION
    call SPACE
    call DO
    mov  si, cr
    call writeString
    dec  al
    jnz  Trunk
    ret