;        Exemple de routines d'affichage de sprites
;        (c) Pict/Logon System 1993 pour A100%
;        Assemble avec DAMS charge en #4000
;
;        Coordonnees du sprite
x       EQU     40
y       EQU     2
;
;        Dimensions du sprite
haut    EQU     13
larg    EQU     4
size    EQU     larg*haut
mask    EQU     4*size
;
;        Adresse de la pile du programme
;
stack   ORG     #2000
;
;        Adresse des donnees du sprite
;
sprite  EQU     #1000
tabspr  DW      size*0+sprite
        DW      size*2+sprite
        DW      size*1+sprite
        DW      size*3+sprite
;
;        Largeur de l'ecran en word
r1      EQU     40
;
;        Routine de chargement du sprite
;
nom1    DM      sprite  .BIN
load    LD      b,#0C
        PUSH    de
        CALL    #BC77
        POP     hl
        CALL    #BC83
        JP      #BC7A
;
;        Debut du programme
run
;       ENT     $
        LD      hl,nom1
        LD      de,sprite
        CALL    load
;
;        Passage en mode 1 et affichage d'un fond quelquonque
;
go      LD      a,1
        CALL    #BC0E
        LD      hl,fond
affond  LD      a,(hl)
        INC     hl
        OR      a
        JP      z,finaff
        PUSH    hl
        CALL    #bb5A
        POP     hl
        JP      affond
finaff
;
;        Bloquage du Firmware pour avoir la jouissance de tous
;        les registres du Z-80 qu'on sauvegarde
;
        DI
        LD      hl,(#38)
        LD      (sys1+1),hl
        LD      hl,#C9FB
        LD      (#38),hl
        LD      (stasys1+1),sp
        LD      sp,stack
        EXX
        EX      af,af
        PUSH    hl
        PUSH    de
        PUSH    bc
        PUSH    af
;
;        Initialisation des couleurs du sprite
;
        LD      bc,#7F00
        LD      de,#5458
        LD      hl,#4D4B
        OUT     (c),c
        OUT     (c),d
        INC     c
        OUT     (c),c
        OUT     (c),e
        INC     c
        OUT     (c),c
        OUT     (c),h
        INC     c
        OUT     (c),c
        OUT     (c),l
;
;        Creation de la table des ordonnees
;        (hauteur normale= 200 lignes)
;        (adresse normale= #C000)
;
        LD      hl,#C000
        LD      de,r1*2+#C000
        LD      bc,#800
        LD      ix,taby
        LD      a,200
creey   LD      (ix+1),h
        LD      (ix+0),l
        INC     ix
        INC     ix
        ADD     hl,bc
        JP      nc,nocarry
        ADD     hl,de
nocarry DEC     a
        JP      nz,creey
;
;        Appel du listing d'affichage (listing1,listing2)
;
        CALL    listing2
;
;        Attente d'appui sur Espace
;
key     LD      bc,#f782
        OUT     (c),c
        LD      bc,#f40e
        OUT     (c),c
        LD      bc,#f6c0
        OUT     (c),c
        XOR     a
        OUT     (c),a
        LD      bc,#f792
        OUT     (c),c
        LD      de,#f4f6
        LD      c,#45
        LD      b,e
        OUT     (C),c
        LD      b,d
        IN      d,(c)
        LD      bc,#f782
        OUT     (c),c
        DEC     c
        OUT     (c),a
        RL      d
        JP      c,key
;
;        Mets le pen 1 blanc
        LD      bc,#7f01
        LD      a,#4b
        OUT     (c),c
        OUT     (c),a
;
;        Retablissement du Firmware
;
firm
        DI
        POP     af
        POP     bc
        POP     de
        POP     hl
        EXX
        EX      af,af
stasys1 LD      sp,0
sys1    LD      hl,0
        LD      (#38),hl
        EI
        RET
;
fond    DM      LOGON SYSTEM
        DB      10,13
        DM      AMSTRAD 100%
        DB      0
taby    DS      2*200
;
;        Programme principal : on transforme
;        les coordonees x et y en adresse ecran.
;
listing1
;        on s'occupe d'abord de l'abscisse
        LD      bc,x
        XOR     a
        LD      d,a
        SRL     b
        RR      c
        RLA
        SRL     b
        RR      c
        RLA
        ADD     a,a
;        Les 2 derniers bits de x determinent le sprite a afficher
;        parmi les sprites decales au pixel dans la memoire
        LD      e,a
        LD      hl,tabspr
        ADD     hl,de
        LD      e,(hl)
        INC     hl
        LD      d,(hl)
        LD      a,c
;
;        L'ordonnee permet de pointer dans une table sur l'adresse
;        de la ligne {cran correspondante
        LD      hl,y
        ADD     hl,hl
        LD      bc,taby
        ADD     hl,bc
        LD      c,(hl)
        INC     hl
        LD      b,(HL)
;
;        On additionne a cette adresse
;
;        l'abscisse en octets
        LD      h,0
        LD      l,a
        ADD     hl,bc
;        On a l'adresse finale dans HL
        LD      a,haut
;        ... qu'on transfere dans DE
        EX      de,hl
;
;        ROUTINES D'AFFICHAGE DU SPRITE
;
;        (jp loop1_1,loop1_2,...,loop1_6)
;
routine JP      loop1_5
;
;        1ERE ROUTINE AFFICHAGE LE PLUS SIMPLE: LDIR SEULEMENT
;
loop1_1 LD      bc,larg
        LDIR
        LD      bc,#800-larg
        EX      de,hl
        ADD     hl,bc
        JP      nc,nocarry1
        LD      bc,r1*2+#c000
        ADD     hl,bc
nocarry1        EX      de,hl
        DEC     a
        JP      nz,loop1_1
        RET
;
;        2EME ROUTINE: A PEU PRES IDENTIQUE A LA PREMIERE
;        MAIS ON REMPLACE LDIR PAR AUTANT DE LDI QUE LE SPRITE
;        EST LARGE EN OCTETS.
;
loop1_2 LDI
        LDI
        LDI
        LDI
        LD      bc,#800-larg
        EX      de,hl
        ADD     hl,bc
        JP      nc,nocar1_2
        LD      bc,r1*2+#c000
        ADD     hl,bc
nocar1_2        EX      de,hl
        DEC     a
        JP      nz,loop1_2
        RET
;
;        3EME ROUTINE: ON TESTE CHAQUE OCTET DU SPRITE A AFFICHER:
;        S'IL EST NUL, ON NE L'AFFICHE PAS.
;
loop1_3 EX      de,hl
loop1_31        PUSH    af
        LD      b,larg
loop1_32        LD      a,(de)
        INC     de
        OR      a
        JP      z,noaff1_3
        LD      (hl),a
noaff1_3        INC     hl
        DJNZ    loop1_32
        LD      bc,#800-larg
        ADD     hl,bc
        JP      nc,nocar1_3
        LD      bc,#c050
        ADD     hl,bc
nocar1_3        POP     af
        DEC     a
        JP      nz,loop1_31
        RET
;
;        4EME ROUTINE:LE SPRITE ET LE FOND SONT "MELANGES" AVEC UN OR
;        LE RESULTAT N'EST PAS TOUJOURS GRACIEUX,MAIS LA RAPIDITE
;        D'AFFICHAGE EST CONVENABLE... ON AURAIT PU UTILISER UN XOR
;        A LA PLACE DE XOR CE QUI D'EMPLOYER LA MEME ROUTINE POUR L'AFFICHAGE
;        ET L'EFFACAGE, MAIS LE RESULTAT A L'ECRAN EST SOUVENT TRES LAID...
;
LOOP1_4 EX      de,hl
loop1_41        PUSH    af
        LD      b,larg
loop1_42        LD      a,(de)
        OR      (hl)
        LD      (hl),a
        INC     hl
        INC     de
        DJNZ    loop1_42
        LD      bc,#800-larg
        ADD     hl,bc
        JP      nc,nocar1_4
        LD      bc,r1*2+#c000
        ADD     hl,bc
nocar1_4        POP     af
        DEC     a
        JP      nz,loop1_41
        RET
;
;        5EME ROUTINE: AFFICHAGE MASQUE:LE PLUS EFFICACE MAIS AUSSI LE PLUS
;        LENT! A MOINS D'UTILISER DU CODE AUTO-GENERE (VOIR LISTING 2)
;
LOOP1_5
;        on recupere le masque correspondant au sprite decale
        LD      bc,mask
        PUSH    hl
        ADD     hl,bc
        LD      b,h
        LD      c,l
        POP     hl
        EX      de,hl
        EXX
        DI
        LD      (sp1_5+1),sp
        LD      sp,#800-larg
loop1_51        EX      af,af
        LD      b,4
loop1_52        EXX
;
;        DE=sprite
;        BC=masque
;        HL=adresse ecran
;
        LD      a,(bc)
        AND     (hl)
        EX      de,hl
        OR      (hl)
        EX      de,hl
        LD      (hl),a
        INC     hl
        INC     de
        INC     bc
        EXX
        DJNZ    loop1_52
        EXX
        ADD     hl,sp
        JP      nc,nocar1_5
        LD      sp,r1*2+#c000
nocar1_5
        EXX
        EX      af,af
        DEC     a
        JP      nz,loop1_51
sp1_5   LD      sp,0
        EI
        RET
;
;        6EME ROUTINE:AFFICHAGE A LA PILE: RAPIDE MAIS LE FOND EST DETRUIT
;        ET LE SPRITE DOIT ETRE COMPOSE D'UN NOMBRE PAIR D'OCTETS EN LARGEUR
;
loop1_6 DI
        LD      (sp1_6+1),sp
        LD      sp,hl
        EX      de,hl
        LD      b,a
loop1_61        LD      c,larg/2
loop1_62
        POP     de
        LD      (hl),e
        INC     hl
        LD      (hl),d
        INC     hl
        DEC     c
        JP      nz,loop1_62
        LD      de,#800-larg
        ADD     hl,de
        JP      nc,nocar1_6
        LD      de,#c050
        ADD     hl,de
nocar1_6        DJNZ    loop1_61
sp1_6   LD      sp,0
        EI
        RET
;
;        Listing2:Sprite Autogenere: les donnees du sprite
;        sont incrustees dans la routine
;
listing2
        CALL    gener
        CALL    affiche
        RET
;
;        routine d'affichage en code genere: cette partie
;        determine laquelle des 4 routines doit etre appelee.
;        on se sert de la pile pour recuperer l'adresse de la
;        ligne de l'ecran et celle de la routine d'affichage dont on a besoin.
;
affiche
        DI
        LD      (sp2+1),sp
;
;        on recupere d'abord l'adresse de la routine d'affichage
;        tout en transformant l'abscisse.
;
        LD      de,x
        LD      a,e
        SRL     d
        RR      e
        SRL     d
        RR      e
        LD      sp,tabadsp
        AND     3
        ADD     a,a
        LD      h,d
        LD      l,a
        ADD     hl,sp
        LD      sp,hl
        POP     ix
;
;        on recupere maintenant l'adresse de la ligne de l'ecran.
;
        LD      sp,taby
        LD      hl,y
        ADD     hl,hl
        ADD     hl,sp
        LD      sp,hl
        POP     hl
        ADD     hl,de
        LD      bc,#800-larg+1
        LD      de,r1*2+#c000
sp2     LD      sp,0
        EI
;
;        saut a la routine d'affichage selon l'abscisse
;
        JP      (ix)
;
;        Programme de generation des 4 routines de sprites.
;
gener   LD      iy,sprout
        LD      hl,sprite
        LD      de,sprite+mask
        LD      ix,tabadsp
        LD      a,4
codebyte
        PUSH    af
        PUSH    hl
        PUSH    iy
        POP     hl
        LD      (ix+0),l
        LD      (ix+1),h
        POP     hl
        INC     ix
        INC     ix
        CALL    calc
        POP     ix
        POP     af
        DEC     a
        JP      nz,codebyte
        RET
;
calc    LD      b,haut
pokeline        PUSH    bc
        LD      b,larg
poke    LD      a,(de)
;
;        on teste d'abord si le masque est plein
;
        CP      #ff
;
;        si oui, pas la peine d'afficher l'octet, on passe au suivant.
;
        JP      z,nxtbyte
;
;        on teste ensuite si le masque est nul: dans ce cas, pas besoin
;        de masquer, on affiche la donnee de l'octet du sprite directement
;
        OR      A
        JP      nz,maskbyte
        LD      a,(hl)
        LD      (iy+0),#36
        INC     iy
        LD      (iy+0),a
        INC     iy
nxtbyte LD      (iy+0),#23
        INC     iy
        JP      coded
maskbyte
        PUSH    bc
;
;        on recopie le "bout" de code qui affiche et masque un octet du sprite
;
pkr     LD      a,(ix+0)
        LD      (iy+0),a
        INC     ix
        INC     iy
        DJNZ    pkr
        POP     bc
;
;        on incruste dans le code le masque et la donnee de l'octet du sprite
;
        LD      a,(de)
        LD      (iy-5),a
        LD      a,(hl)
        LD      (iy-3),a
coded
        INC     de
        INC     hl
        DJNZ    poke
;
;        on passe a la ligne le "INC L" inutile en decrementant IY
;
        DEC     iy
        LD      b,4
;
;        on recopie le "bout" de code qui permet de descendre d'une ligne.
;
        LD      ix,nxtline
pknx    LD      a,(ix+0)
        LD      (iy+0),a
        INC     ix
        INC     iy
        DJNZ    pknx
        POP     bc
        DJNZ    pokeline
;
;        fin de la routine d'affichage. On n'a pas besoin de redescendre
;        d'une ligne donc on elimine le morceau de code precedent en reculant
;        le pointeur du code genere pour reecrire par dessus.
;
        LD      BC,-4
        ADD     IY,BC
        LD      (iy+0),#c9
        INC     iy
        RET
;
;        "morceau" de programme necessaire pour masquer et afficher un octet.
;
oper                     bytes
        LD      a,(hl)
        AND     0
        OR      0
        LD      (hl),a
        INC     l
;
;        "morceau de programme necessaire pour descendre d'une ligne.
;
nxtline                  bytes
        ADD     hl,bc
        JR      nc,noaddde
        ADD     hl,de
noaddde
end_
;
;        table cree par le generateur qui contient les adresses
;        des 4 routines generees.
;
tabadsp DS      4*2
;
;        les routines sont generees dans l'espace de memoire debutant ici meme.
sprout