; Exemple de secteur de boot (disquette)

;       Auteur : Benoit Papillault
;       Creation: Juin 1997
;       Derniere modification: Samedi 5 Juillet 1997

STACK_SIZE      equ     100d    ; au moins 53 pour que les interruptions
                                ; du BIOS se deroule corectement
SEGMENT_INITIAL equ     7c0h    ; la valeur de CS lorsque ce code est execute
                                ; par le BIOS ou le MBR du disque

; Pour debugger sous DOS avec Turbo-Debuggeur
; SEGMENT_INITIAL equ     cs

; lorsque le pc boote, le premier secteur de la disquette est charge en
; memoire a l'adresse 0:7c00h, lorsque l'execution commence, on a:
; CS=0000h et IP=7c00h

bios    segment at 0ffffh
        org     0
bios_reboot: 
bios    ends

bios_data       segment at 40h
        org     72h
bios_flag       dw      ?
bios_data       ends

        ; on cree un segment data avec "at 0h" pour que TASM comprenne bien
        ; que le segment n'est pas initialise et que donc il ne doit pas
        ; lui reserve de place (on a que 512 octets [et encore!], on ne 
        ; peut pas se permettre de gaspiller, donc on utilise un truc pas
        ; beau). On utilisera ce segment comme s'il s'agissait de _TEXT
        ; (meme si TASM ne veut pas!)

_DATA   segment at 0h
        org     200h    ; permet de "mettre" les donnes non initialisee
                        ; a la suite du premier secteur en memoire

        db      STACK_SIZE dup (?)
tos:
sav_ax  dw      ?
sav_ss  dw      ?
sav_sp  dw      ?
sav_si  dw      ?
sav_ds  dw      ?
_DATA   ends

_TEXT   segment
        org     0h
        assume  cs:_TEXT,ds:_DATA

        ; avec de tels assume, on peut faire a peu pres ce qui nous convient
        ; attention cependant a eviter des instructions mov mavar,ax
        ; avec mavar dans ce segment car un CS: sera genere (et encore un
        ; octets de gache)

start:
        jmp     short debut     ; debut doit se trouver a l'offset 3eh
                                ; sinon MS-DOS refuse de lire la disquette
        nop

        org     03h
        db      'MSDOS5.0'
        dw      200h
        db      1h
        dw      1h
; 10h
        db      2h
        dw      0e0h
        dw      0b40h
        db      0f0h
        dw      9h
        dw      12h
        dw      2h
        dw      0h
        dw      ?
; 20h
        dd      0h
        db      0h
        db      0h
        db      29h     ; signature
        db      'PAPI'  ; serial number
        db      'DISK16     '
        db      'FAT12   '

        org     40h
debut:        
; on sauve DS et AX pour plus tard (en esperant que l'on puise mettre 
; quatre octets dans la pile actuelle
        push    ds
        push    ax

; on initialise DS
        mov     ax,SEGMENT_INITIAL
        mov     ds,ax

; on sauvegarde quelques registres dans une pile inconnue
        pop     sav_ax
        pop     sav_ds
        mov     sav_ss,ss
        mov     sav_sp,sp
        mov     sav_si,si

; on mets en place notre pile
        cli
        mov     ss,ax
        mov     sp,offset tos
        sti

        mov     si,offset r_ax
        call    prints
        mov     ax,sav_ax
        call    print_ax
        mov     si,offset r_bx
        call    prints
        mov     ax,bx
        call    print_ax
        mov     si,offset r_cx
        call    prints
        mov     ax,cx
        call    print_ax
        mov     si,offset r_dx
        call    prints
        mov     ax,dx
        call    print_ax
        mov     si,offset r_sp
        call    prints
        mov     ax,sav_sp
        call    print_ax
        mov     si,offset r_bp
        call    prints
        mov     ax,bp
        call    print_ax
        mov     si,offset r_si
        call    prints
        mov     ax,sav_si
        call    print_ax
        mov     si,offset r_di
        call    prints
        mov     ax,di
        call    print_ax
        mov     si,offset r_ds
        call    prints
        mov     ax,sav_ds
        call    print_ax
        mov     si,offset r_es
        call    prints
        mov     ax,es
        call    print_ax
        mov     si,offset r_ss
        call    prints
        mov     ax,sav_ss
        call    print_ax
        mov     si,offset r_cs
        call    prints
        mov     ax,cs
        call    print_ax
        mov     si,offset r_ip
        call    prints
        call    get_ip
retour_get_ip:
        sub     ax,retour_get_ip - start
        call    print_ax
        
        ; un retour chariot
        mov     al,13
        call    printc
        mov     al,10
        call    printc

        mov     si,offset Mess
        call    prints

        mov     ah,0
        int     16h

        mov     al,7
        call    printc

;        mov     dl,0
;        int     19h

; Pour eviter le test de la memoire, on met 40:72 a 1234
        mov     ax,seg bios_data
        mov     es,ax
        assume  es:bios_data
        mov     bios_flag,1234h

; cold boot ??? !!!
        jmp     far ptr bios_reboot

;        mov     ax,4c00h
;        int     21h

boucle_infinie:
        xor     ax,ax
        int     16h
        mov     al,'.'
        call    printc
        jmp     short boucle_infinie

        ; imprime une chaine (termine par zero) dans DS:SI

prints:
        ; cld
        ; lodsb   ; equivalent de mov al,ds:si et inc si mais en moins d'octets
        mov     al,[ds:si]
        inc     si
        cmp     al,0
        je      short fin_prints
        call    printc
        jmp     short prints
fin_prints:
        ret

        ; retourne la valeur de IP (adresse de retour de la fonction) dans AX
        ; modifie le registre BP

get_ip:
        mov     bp,sp
        mov     ax,[bp]
        ret

        ; imprime la valeur en hexadecimal des 4 bits de poids faibles de AL
        ; aucune modification des registres

print_a4:
        push    ax
        
        and     al,0fh  ; AL = le premier chiffre
        cmp     al,10
        jl      short print_a4_ok
        add     al,'A'-'0'-10
print_a4_ok:
        add     al,'0'
        call    printc

        pop     ax
        ret
; fin de print_a4

        ; imprime la valeur hexa de AX 
        ; aucune modification des registres

print_ax:
        push    cx
        mov     cl,4
        rol     ax,cl
        call    print_a4
        rol     ax,cl
        call    print_a4
        rol     ax,cl
        call    print_a4
        rol     ax,cl
        call    print_a4
        pop     cx
        ret

        ; affiche le caractere AL
        ; la position du curseur est mise a jour
        ; on utilise la fonction 0eh de l'interruption 10h
        ; avec AH=0eh, AL=caractere, BH=page (ici 0) et
        ; BL=couleur d'ecriture (ici 7=blanc)

printc:
        push    ax
        push    bx
        mov     ah,0eh
        mov     bx,7
        int     10h
        pop     bx
        pop     ax
        ret
        
; debut de la zone de donnees propres au programme
Mess    db      'Chargement du boot...',13,10,7,0
r_ax    db      'AX=',0
r_bx    db      '  BX=',0
r_cx    db      '  CX=',0
r_dx    db      '  DX=',0
r_sp    db      '  SP=',0
r_bp    db      '  BP=',0
r_si    db      '  SI=',0
r_di    db      '  DI=',0
; ligne suivante
r_ds    db      13,10,'DS=',0
r_es    db      '  ES=',0
r_ss    db      '  SS=',0
r_cs    db      '  CS=',0
r_ip    db      '  IP=',0

end:    ; pour marquer la fin du code et des donnes statiques

        org     1feh
        db      55h,0aah
_TEXT   ends
        end     start
