org 0x0100 ; Adresse de début .COM

;Ecriture de la chaîne hello dans la console
    mov si, hello; met l'adresse de la chaîne à afficher dans le registre SI
;    call affiche_chaine

    mov bx, 1; on va écrire les nombres avec un caractère terminal
    int 0x11
    test ax, 0b00000000000001
    jnz lecteurs_disquette
    mov si, pas
;    call affiche_chaine
fin_disquette:
    mov si, disquettes
;    call affiche_chaine
test_coprocesseur:
    test ax, 0b00000000000010
    jnz coprocesseur_present
    mov si, pas
;    call affiche_chaine
coprocesseur_present:
    mov si, coprocesseur
;    call affiche_chaine
test_memoire:
    push ax
    and ax, 0b00000000001100
    shr ax, 2
    inc ax; une zone mémoire est donnée gratis.
    shl ax, 4; les zones mémoires sont comptées par paquets de 16 ko
    mov di, hello
    call nombre_vers_chaine
    mov si, hello
;    call affiche_chaine
    mov si, memoire_dispo
;    call affiche_chaine

    pop ax
    push ax
    and ax, 0b00000000110000
    shr ax, 4
    cmp ax, 0b00
    je mode_graphique_actif
test_mode_texte_couleur40:
    cmp ax, 0b01
    je mode_texte_couleur40_actif
test_mode_texte_couleur80:
    cmp ax, 0b10
    je mode_texte_couleur80_actif
test_mode_texte_mono:
    cmp ax, 0b11
    je mode_texte_mono_actif
test_DMA:
    mov si, au_demarrage
;    call affiche_chaine
    pop ax
    test ax, 0b00000100000000
    jnz DMA_present
    mov si, pas
;    call affiche_chaine
DMA_present:
    mov si, DMA
;    call affiche_chaine
test_RS232:
    push ax
    and ax, 0b00111000000000
    shr ax, 9
    mov di, hello
    call nombre_vers_chaine
    mov si, hello
;    call affiche_chaine
    mov si, RS232
;    call affiche_chaine
    pop ax
    test ax, 0b01000000000000
    jnz manette_presente
    mov si, pas
;    call affiche_chaine
manette_presente:
    mov si, manette
;    call affiche_chaine
test_modem:
    test ax, 0b10000000000000
    jnz modem_present
    mov si, pas
;    call affiche_chaine
modem_present:
    mov si, modem_interne
;    call affiche_chaine
    shr ax, 14
    mov di, hello
    call nombre_vers_chaine
    mov si, hello
;    call affiche_chaine
    mov si, imprimantes
;    call affiche_chaine
;lecture mémoire disponible
    int 0x12
    mov di, hello
    call nombre_vers_chaine
    mov si, hello
;    call affiche_chaine
    mov si, memoire_dispo
;    call affiche_chaine

    mov ax, 0x4F00 ; demande infos sur le pilote VESA
    mov di, hello
    int 10h
    cmp al, 0x4F ; Si AL <> 0x4F, on n'a pas de VESA, donc fin.
    jne fin
    mov si, hello + 0x06 ; pointeur vers le nom de l'OEM stocké offset:segment
    lodsw; on charge l'adresse d'offset dans ax
    mov bx, ax ; BX contient l'adresse d'offset
    lodsw ; on charge l'adresse de segment dans ax
    mov si, bx ; SI pointe sur le nom de l'OEM
    push ds ; on sauvegarde DS
    mov ds, ax ; ds contient l'adresse de segment du nom de l'OEM
    call affiche_chaine
    pop ds ; on restaure DS
    mov si, retour_chariot
    call affiche_chaine

    mov si, hello + 0x0E ; pointeur vers la liste des modes supportés
    lodsw ; on charge l'adresse d'offset dans ax
    mov cx, ax ; cx contient l'adresse d'offset
    lodsw ; on charge l'adresse de segment dans ax
    mov si, cx ; si pointe sur le premier mode supporté
    mov dx, ax ; dx contient l'adresse de segment
lit_mode_suivant:
    push ds
    mov ds, dx ; ds contient l'adresse de segment de la liste des modes
    lodsw ;charge dans ax le mode
    pop ds
    cmp ax, 0xFFFF ; Fin de la liste
    je arret_modes
    xor ah, ah ; on enlève le haut du mode
    mov di, hello ; on écrit dans hello
    mov bx, 1
    call nombre_vers_chaine
    mov al, ' '
    mov ah, 0 ; on met 2 caractères d'un coup après la chaîne : un espace et le zéro terminal.
    stosw ; les caractères sont dépilés, c'est à dire qu'il faut placer le premier dans la zone basse
    push si ;sauve si sur la pile
    mov si, hello
    call affiche_chaine
    pop si ; on récupère si
    jmp lit_mode_suivant
arret_modes:
    mov si, retour_chariot ; Affichage d'un retour chariot
    call affiche_chaine
    mov cx, [mode_souhaite] ; On s'enquiert du mode souhaité
;suite_mode_VESA:
    mov ax, 0x4F01 ; demande infos sur le mode VESA
    mov di, hello
    int 0x10
    cmp al, 0x4F ; Si AL <> 0x4F, la fonction n'est pas supportée, on se contentera du VGA.
    jne adresse_mode_13h
    cmp ah, 0x01 ; idem en cas de AH à 1
    je adresse_mode_13h
    mov si, hello ; pointeur vers la zone de données remplie par l'interruption
    lodsw
    test ax, 0b1 ; mode supporté ?
    jz adresse_mode_13h ; On se contente du VGA
    mov si, hello + 0x06 ; pointeur vers la taille de la fenêtre graphique
    mov di, taille_fenetre ; pointeur vers notre structure à nous
    mov ax, ds ; DS = ES
    mov es, ax
    movsw ; SI pointe maintenant vers l'adresse du segment de la fenêtre A
    movsw ; taille_fenetre et adr_fen_A sont maintenant stockées
    mov ax, [adr_fen_A]
    or ax, ax ; on teste l'adresse du segment de la fenêtre. Si elle est nulle, on passe en mode 0x13
    jnz adresse_OK
adresse_mode_13h:
    mov word [mode_souhaite], 0x0013 ; infos du mode 0x13, le mode VGA
    mov word [adr_fen_A], 0xA000
adresse_OK:
    mov di, hello ; met l'adresse de la chaîne à lire dans le registre SI
    call lit_chaine ; On attend l'utilisateur pour nettoyer l'écran

    mov ax, 0x4F02
    mov bx, [mode_souhaite]
    int 0x10 ; Changement de mode vidéo
    mov si, adr_fen_A
    lodsw
    mov es, ax;
    xor di, di
    mov ax, [taille_fenetre]
    shr ax, 1 ; On va remplir 2 octets par 2 octets, on divise donc la taille par 2
    mov cx, 1000 ; Elle est donnée en 1000 octets
    mul cx ; Donc on multiplie par 1000
    mov cx, ax ; Et on met ça dans le compteur
    mov ax, 0x0101 ; Deux fois de suite la couleur à appliquer
    rep stosw ; et on boucle

    mov ax, 160 ; Coordonnée X
    mov bx, 100 ; Coordonnée Y
    mov dx, 0x03; Couleur
    call affiche_point
    push 100
    push 10
    push 100
    push 20
    mov dx, 0x04; Couleur
    call affiche_ligne
    pop ax
    pop ax
    pop ax
    pop ax
    push 102
    push 20
    push 102
    push 10
    mov dx, 0x05; Couleur
    call affiche_ligne
    pop ax
    pop ax
    pop ax
    pop ax
    push 114
    push 10
    push 104
    push 10
    mov dx, 0x06; Couleur
    call affiche_ligne
    pop ax
    pop ax
    pop ax
    pop ax
    push 104
    push 20
    push 114
    push 20
    mov dx, 0x07; Couleur
    call affiche_ligne
    pop ax
    pop ax
    pop ax
    pop ax
    push 126
    push 20
    push 124
    push 10
    mov dx, 0x08; Couleur
    call affiche_ligne
    pop ax
    pop ax
    push 116
    push 18
    mov dx, 0x09; Couleur
    call affiche_ligne
    pop ax
    pop ax
    push 124
    push 30
    mov dx, 0x0A; Couleur
    call affiche_ligne
    pop ax
    pop ax
    push 116
    push 22
    mov dx, 0x0B; Couleur
    call affiche_ligne
    pop ax
    pop ax
    push 132
    push 10
    mov dx, 0x0C; Couleur
    call affiche_ligne
    pop ax
    pop ax
    push 136
    push 16
    mov dx, 0x0D; Couleur
    call affiche_ligne
    pop ax
    pop ax
    push 136
    push 24
    mov dx, 0x0E; Couleur
    call affiche_ligne
    pop ax
    pop ax
    push 132
    push 30
    mov dx, 0x0F; Couleur
    call affiche_ligne
    pop ax
    pop ax
    pop ax
    pop ax
    mov di, hello; met l'adresse de la chaîne à lire dans le registre SI
    call lit_chaine ; On attend la volonté de l'utilisateur
    mov ax, 0x4F02 ; Retour au mode texte
    mov bx, 0x0003; 80 * 25, mode texte
    int 0x10
    mov di, hello; met l'adresse de la chaîne à lire dans le registre SI
    call lit_chaine ; On attend la volonté de l'utilisateur
fin:
    ret

lecteurs_disquette:
;On a un lecteur de disquette. Les bits 6 et 7 de AX en donnent le nombre
    push ax
    and ax, 0b0000000011000000
    shr ax, 6
    inc ax
    mov di, hello
    call nombre_vers_chaine
    mov si, hello
    call affiche_chaine
    pop ax
    jmp fin_disquette

mode_graphique_actif:
    mov si, mode_graphique
    call affiche_chaine
    jmp test_mode_texte_couleur40

mode_texte_couleur40_actif:
    mov si, mode_texte_couleur40
    call affiche_chaine
    jmp test_mode_texte_couleur80

mode_texte_couleur80_actif:
    mov si, mode_texte_couleur80
    call affiche_chaine
    jmp test_mode_texte_mono

mode_texte_mono_actif:
    mov si, mode_texte_mono
    call affiche_chaine
    jmp test_DMA

;écrit dans la chaîne pointée par DI le nombre contenu dans AX
;si BL est à un, on écrit un caractère terminal
;BH contient le nombre minimal de caractères à utiliser
nombre_vers_chaine:
    push cx
    push dx
    push bx
    mov bx, 10
    mov cx, 1
    xor dx, dx
stocke_digit:
    div bx
;    mov dl, ah
    push dx ;sauve le reste dans la pile
    inc cx
;    xor ah, ah
    xor dx, dx
    or ax, ax
    jne stocke_digit

    inc bh
ajout_zero:
    cmp bh, cl
    jbe boucle_digit
    push 0
    inc cx
    jmp ajout_zero
;Affichage du chiffre
boucle_digit:
    dec bh
    loop affiche_digit
    pop bx ; on récupère le paramètre bx
    test bl, 0b1 ; s'il est à 1, on écrit un caractère terminal
    jz depile
    mov byte [di], 0
depile:
    pop dx
    pop cx
ret

affiche_digit:
    pop ax
    add ax, '0'
    stosb ; met AL dans l'octet pointé par DI et incrémente DI
    jmp boucle_digit
;fin nombre_vers_chaine

affiche_chaine:
    push ax
    push bx
    push cx
    push dx
    xor bh, bh; RAZ de bh, qui stocke la page d'affichage
    mov ah, 0x03
    int 0x10; appel de l'interruption BIOS qui donne la position du curseur, stockée dans dx
    mov cx, 1; nombre de fois où l'on va afficher un caractère
affiche_suivant:
    lodsb
    or al, al;on compare al à zéro pour s'arrêter
    jz fin_affiche_suivant
    cmp al, 13
    je nouvelle_ligne
    mov ah, 0x0A;on affiche le caractère courant cx fois
    int 0x10
    inc dl; on passe à la colonne suivante pour la position du curseur
    cmp dl, 80
    je nouvelle_ligne
positionne_curseur:
    mov ah, 0x02;on positionne le curseur
    int 0x10
    jmp affiche_suivant
fin_affiche_suivant:
    pop dx
    pop cx
    pop bx
    pop ax
    ret
nouvelle_ligne:
    inc dh; on passe à la ligne suivante
    xor dl, dl; colonne 0
    jmp positionne_curseur
;fin de affiche_chaine

lit_chaine:
    push ax
    push cx
    push dx
    mov ah, 0x03
    int 0x10; appel de l'interruption BIOS qui donne la position du curseur, stockée dans dx
    mov cx, 1
attend_clavier:
    mov ah, 0x01;on teste le buffer clavier
    int 0x16
    jz attend_clavier
    ;al contient le code ASCII du caractère
    xor ah, ah; on lit le buffer clavier
    int 0x16
    stosb
    cmp al, 13
    je fin_attend_clavier
    ;al contient le code ASCII du caractère
    mov ah, 0x0A;on affiche le caractère courant cx fois
    int 0x10
    inc dl; on passe à la colonne suivante pour la position du curseur
    mov ah, 0x02;on positionne le curseur
    int 0x10
    jmp attend_clavier
fin_attend_clavier:
    inc dh; on passe à la ligne suivante pour la position du curseur
    xor dl, dl
    mov ah, 0x02;on positionne le curseur
    int 0x10
    mov byte [di], 0;on met le caractère terminal dans si
    pop dx
    pop cx
    pop ax
    ret
;fin de lit_chaine

;fonction affiche_point : on est déjà dans un mode graphique
;AX : Coordonnée X du point
;BX : Coordonnée Y du point
;dL : Couleur du point
affiche_point:
    push si ; On sauve les registres qu'on va manipuler
    push es
    push di
    push ax
    mov si, adr_fen_A ; On lit l'adresse de départ de la mémoire vidéo
    lodsw
    mov es, ax ; On va dans la mémoire vidéo
    mov di, bx ; BX contient la coordonnée Y, qu'on va traiter
    shl di, 6 ; = Y * 64
    push di ; Y2 = Y * 64
    shl di, 2 ; = Y2 * 4 = Y * 256
    pop ax ; on récupère Y2
    add di, ax ; IndexY = Y * 256 + Y2 = Y * 256 + Y * 64 = Y * 320
    pop ax ; On récupère la coordonnée X
    add di, ax ; index = IndexY + X
    push ax ; On remet X dans la pile
    mov al, dl ; On fixe la couleur du pixel
    stosb ; Et on l'écrit dans la mémoire vidéo
    pop ax ; On restaure les registres manipulés
    pop di
    pop es
    pop si
    ret
;fin de affiche_point

;fonction affiche_ligne : on est déjà dans un mode graphique
; DL contient la couleur
affiche_ligne:
    jmp depart_affiche_ligne
Y2: dw 0
X2: dw 0
Y1: dw 0
X1: dw 0
deltaX: dw 0
deltaY: dw 0
incX: dw 0
incY: dw 0
e: dw 0
couleur: db 0
depart_affiche_ligne:
    push si
    push ax
    mov ax, sp
    mov si, ax
    add si, 6 ; SI pointe sur Y2
    push bx
    push cx
    push dx
    push di
    mov di, Y2
    mov ax, ds
    mov es, ax
    mov cx, 4
    rep movsw
    mov [couleur], dl
    mov ax, [X2]
    mov bx, [X1]
    sub ax, bx
    mov [deltaX], ax
    mov cx, [Y2]
    mov bx, [Y1]
    sub cx, bx
    mov [deltaY], cx
    or ax, ax ; test deltaX
    jnz test_deltaX_positif
    or cx, cx ; test deltaY
    jnz test_deltaY_deltaX_nul
fin_affiche_ligne:
    mov dl, [couleur]
    mov ax, [X2]
    mov bx, [Y2]
    call affiche_point
    mov ax, [X1]
    mov bx, [Y1]
    call affiche_point
    pop di
    pop dx
    pop cx
    pop bx
    pop ax
    pop si
    ret

deltaX_positif:
    or cx, cx
    jnz test_deltaY_deltaX_positif
    ;vecteur horizontal vers la droite
    mov cx, [deltaX]
    mov dx, 1
    mov word [incX], 1
    mov word [incY], 0
    jmp ligne_H_V

test_deltaY_deltaX_nul:
    mov word [incY], 1
    mov word [incX], 0
    cmp cx, 0
    jns ligne_H_V
    neg cx
    mov word [incY], -1
ligne_H_V:
    mov ax, [X1]
    mov bx, [Y1]
    mov dl, [couleur]
boucle_H_V:
    loop avance_H_V
    jmp fin_affiche_ligne

avance_H_V:
    add ax, [incX]
    add bx, [incY]
    call affiche_point
    jmp boucle_H_V

test_deltaX_positif:
    cmp ax, 0
    jns deltaX_positif
    or cx, cx
    jnz test_deltaY_deltaX_negatif
    ;vecteur horizontal vers la gauche
    mov cx, [deltaX]
    neg cx
    mov dx, -1
    mov word [incX], -1
    mov word [incY], 0
    jmp ligne_H_V

charge_registres:
    shl cx, 1
    shl ax, 1
    mov [deltaY], cx
    mov [deltaX], ax
    mov ax, [X1]
    mov bx, [Y1]
    ret

charge_e_deltaX_et_cmp_X2:
    mov [e], ax
    call charge_registres
    mov cx, [X2]
    ret

charge_e_deltaY_et_cmp_Y2:
    mov [e], cx
    call charge_registres
    mov cx, [Y2]
    ret

affiche_et_charge_eY:
    mov dl, [couleur]
    call affiche_point
    add bx, [incY]
    mov dx, [e]
    ret

affiche_et_charge_eX:
    mov dl, [couleur]
    call affiche_point
    add ax, [incX]
    mov dx, [e]
    ret

octants1_et_4:
    call charge_e_deltaX_et_cmp_X2
depart_boucle1:
    call affiche_et_charge_eX
    cmp ax, cx
    je fin_affiche_ligne
    sub dx, [deltaY]
    cmp dx, 0
    jns X_pret1
    add bx, [incY]
    add dx, [deltaX]
X_pret1:
    mov [e], dx
    jmp depart_boucle1

deltaY_positif_deltaX_negatif:
    neg ax
deltaY_positif_deltaX_positif:
    mov word [incY], 1
    ;deltaY > 0, deltaX > 0
    cmp ax, cx
    jae octants1_et_4
    neg ax
    call charge_e_deltaY_et_cmp_Y2
depart_boucle2_et_3:
    call affiche_et_charge_eY
    cmp bx, cx
    je fin_affiche_ligne
    add dx, [deltaX]
    cmp dx, 0
    jns X_pret2_et_3
    add ax, [incX]
    add dx, [deltaY]
X_pret2_et_3:
    mov [e], dx
    jmp depart_boucle2_et_3

octant5:
    call charge_e_deltaX_et_cmp_X2
depart_boucle5:
    call affiche_et_charge_eX
    cmp ax, cx
    je fin_affiche_ligne
    sub dx, [deltaY]
    cmp dx, 0
    js X_pret5
    add bx, [incY]
    add dx, [deltaX]
X_pret5:
    mov [e], dx
    jmp depart_boucle5

octant8:
    neg cx
    call charge_e_deltaX_et_cmp_X2
depart_boucle8:
    call affiche_et_charge_eX
    cmp ax, cx
    je fin_affiche_ligne
    add dx, [deltaY]
    cmp dx, 0
    jns X_pret8
    add bx, [incY]
    add dx, [deltaX]
X_pret8:
    mov [e], dx
    jmp depart_boucle8

test_deltaY_deltaX_positif:
    mov word [incX], 1
    cmp cx, 0
    jns deltaY_positif_deltaX_positif
    ;deltaY < 0, deltaX > 0
    mov word [incY], -1
    neg cx
    cmp ax, cx
    jae octant8
    neg cx
    jmp octants6_et_7

test_deltaY_deltaX_negatif:
    mov word [incX], -1
    cmp cx, 0
    jns deltaY_positif_deltaX_negatif
    ;deltaY < 0, deltaX < 0
    mov word [incY], -1
    cmp ax, cx
    jbe octant5
    neg ax
octants6_et_7:
    call charge_e_deltaY_et_cmp_Y2
depart_boucle6_et_7:
    call affiche_et_charge_eY
    cmp bx, cx
    je fin_affiche_ligne
    add dx, [deltaX]
    cmp dx, 0
    js X_pret6_et_7
    add ax, [incX]
    add dx, [deltaY]
X_pret6_et_7:
    mov [e], dx
    jmp depart_boucle6_et_7
;AFFICHE_LIGNE ENDP

mode_souhaite: dw 0x0013
taille_fenetre: dw 0
adr_fen_A: dw 0
disquettes: db ' lecteur(s) de disquette installé(s).', 13, 0
pas: db 'Pas de ', 0
coprocesseur: db 'Coprocesseur arithmétique.', 13, 0
memoire_dispo: db ' ko.', 13, 0
mode_graphique: db 'Mode graphique', 0
mode_texte_couleur40: db 'Mode texte couleur 40 * 25', 0
mode_texte_couleur80: db 'Mode texte couleur 80 * 25', 0
mode_texte_mono: db 'Mode texte monochrome 80 * 25', 0
au_demarrage: db ' au démarrage.', 13, 0
DMA: db 'DMA.', 13, 0
RS232: db ' port(s) RS232 disponible(s).', 13, 0
manette: db 'Manette de jeu.', 13, 0
modem_interne: db 'Modem interne.', 13, 0
imprimantes: db ' imprimante(s) connectée(s).', 13, 0
heure: db '00 h 00 min 00 s', 13, 0
retour_chariot: db 13, 0
hello: db 'Bonjour papi. Je cherche une ligne de plus de quatre vingts caractères. Ce doit être relativement facile à trouver, non ?', 13, 0
