Tachibana
Langue : français

Groupe 6 — Sprites Officiel

6,1 Sprite Reset · 6,2 Sprite Set · 6,3 Sprite Hide · 6,4 Sprite Collision · 6,5 Sprite Position

Un sprite est une image de la mémoire graphique (voir le groupe 5) affichée par-dessus l'écran sans l'abîmer : les sprites occupent les 4 bits hauts de chaque pixel, le fond les 4 bits bas. 5,36 lit la couche des sprites, 5,33 le fond.

6,2 place un sprite. Son numéro d'image est l'index de l'image (0 à 63) dans les sprites 16 × 16, ou dans les sprites 32 × 32 si l'on ajoute $40. La valeur $80 veut dire « inchangé », comme $8080 pour la position et $80 pour le retournement et l'ancre : on peut ainsi ne modifier qu'un réglage. NeoBASIC efface le bit 7 du numéro d'image, si bien que sprite n image $80 (la numérotation de 5,7) y désigne l'image 0 ; par l'appel direct, $80 sur un sprite neuf ne dessine rien (vérifié au banc).

L'ancre dit quel point du sprite se trouve aux coordonnées données : 0 le centre, puis 1 à 9 comme le pavé numérique (7 en haut à gauche, 5 au centre, 3 en bas à droite). La documentation du firmware parle d'un octet de drapeaux (32 bits) en P8 ; le code ne le lit pas, la taille vient du bit 6 du numéro d'image.

Les exemples chargent le fichier graphique du groupe 5 déclaré comme un sprite 16 × 16 (moitié gauche couleur 2, moitié droite couleur 5), et effacent les sprites (6,1) avant de rendre la main.

6,1 Sprite Reset Officiel

Efface tous les sprites et remet le système des sprites à zéro.

Entrées : aucune.

Sorties : aucune.

En NeoBASIC, sprite clear fait cet appel, de même que clear (donc run).

Mots-clés NeoBASIC qui font cet appel : clear, sprite

Texte de la documentation du firmware (anglais)

Reset the sprite system.

C (llvm-mos)
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

static void xy(uint8_t i, uint16_t x, uint16_t y) {   /* point (x, y) en Pi..Pi+3 */
    ControlPort.params[i] = x & 0xFF;
    ControlPort.params[i + 1] = x >> 8;
    ControlPort.params[i + 2] = y & 0xFF;
    ControlPort.params[i + 3] = y >> 8;
}

static uint8_t gfx[256 + 128];                  /* en-tête + une image 16x16, 4 bits par pixel */

static void images(uint8_t genre) {             /* 1 : une tuile ; 2 : un sprite 16x16 */
    gfx[0] = 1;
    gfx[genre] = 1;
    for (uint8_t y = 0; y < 16; y++)
        for (uint8_t i = 0; i < 8; i++)
            gfx[256 + y * 8 + i] = i < 4 ? 0x22 : 0x55;   /* gauche : couleur 2, droite : 5 */
    neo_file_store("img.gfx", gfx, sizeof gfx);
    neo_file_load("img.gfx", (void *)0xFFFF);   /* $FFFF : mémoire graphique */
}

static uint8_t sprite_pixel(uint16_t x, uint16_t y) {   /* 5,36 : couche des sprites */
    libre();
    xy(0, x, y);
    api(5, 36);
    return ControlPort.params[0];
}

/* Afficher un sprite, puis tout effacer (groupe 6, fonction 1). */
int main(void) {
    images(2);                                  /* un sprite 16x16 */
    neo_sprite_set(0, 200, 170, 0, 0, 0);       /* 6,2 : centré en (200, 170) */
    uint8_t a = sprite_pixel(200, 170);
    neo_sprite_reset();                         /* Sprite Reset */
    uint8_t b = sprite_pixel(200, 170);
    printf("Avant : %u, apres : %u\n", a, b);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Avant : 5, apres : 0
Mad Pascal
// Afficher un sprite, puis tout effacer (groupe 6, fonction 1).
program raz;
uses crt, neo6502;
var a, b: byte;
    gfx: array[0..383] of byte;                 // en-tête + une image 16x16

procedure images(genre: byte);                  // 1 : une tuile ; 2 : un sprite 16x16
var y, i: byte;
begin
  fillchar(@gfx, 384, 0);
  gfx[0] := 1;
  gfx[genre] := 1;
  for y := 0 to 15 do
    for i := 0 to 7 do
      if i < 4 then gfx[256 + y * 8 + i] := $22   // gauche : couleur 2
      else gfx[256 + y * 8 + i] := $55;         // droite : couleur 5
  NeoSave('img.gfx', word(@gfx), 384);
  NeoLoad('img.gfx', $FFFF);                    // $FFFF : mémoire graphique
end;

begin
  images(2);                                    // un sprite 16x16
  NeoUpdateSprite(0, 200, 170, 0, 0, 0);        // 6,2 : centré en (200, 170)
  a := NeoGetSpritePixel(200, 170);
  NeoResetSprites;                              // Sprite Reset
  b := NeoGetSpritePixel(200, 170);
  writeln('Avant : ', a, ', apres : ', b);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Avant : 5, apres : 0
NeoBASIC
10 ' Afficher un sprite, puis tout effacer (groupe 6, fonction 1)
20 d = alloc(384) : for i = 0 to 383 : poke d + i, 0 : next i : poke d, 1 : poke d + 2, 1 : ' en-tete
22 for y = 0 to 15 : for i = 0 to 7 : c = $22 : if i > 3 then c = $55
24 poke d + 256 + y * 8 + i, c : next i : next y : ' gauche : couleur 2, droite : 5
26 save "img.gfx", d, 384 : load "img.gfx", $ffff : ' $ffff : memoire graphique
30 sprite 0 image 0 to 200,170
40 a = spoint(200,170)
50 sprite clear
60 b = spoint(200,170)
70 print "Avant : ";a;", apres : ";b
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Avant : 5, apres : 0
Assembleur 65C02 (64tass)
; Afficher un sprite, puis tout effacer (groupe 6, fonction 1).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  jsr images                 ; un sprite 16x16
        stz ancre                  ; ancre 0 : centre
        lda #200
        ldy #170
        jsr place
        lda #200
        ldy #170
        jsr spixel
        sta a
        jsr efface                 ; fonction 1 : Sprite Reset
        lda #200
        ldy #170
        jsr spixel
        sta b
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda a
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda b
        jsr decimal
        lda #13
        jmp WriteCharacter

; Place le sprite 0 : X en A, Y en Y (inférieurs à 256), image 0, sans retournement, ancre dans ancre.
place:  pha
        jsr WaitMessage
        pla
        stz ControlPort+4          ; P0 : sprite 0
        sta ControlPort+5          ; P1-P2 : X
        stz ControlPort+6
        sty ControlPort+7          ; P3-P4 : Y
        stz ControlPort+8
        stz ControlPort+9          ; P5 : image 0
        stz ControlPort+10         ; P6 : pas de retournement
        lda ancre
        sta ControlPort+11         ; P7 : ancre
        lda #6
        ldx #2                     ; 6,2 : Sprite Set
        jmp appel
ancre:  .byte 7

; Couleur de la couche des sprites en (A, Y), lue par 5,36, rendue dans A.
spixel: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        stz ControlPort+5
        sty ControlPort+6
        stz ControlPort+7
        lda #5
        ldx #36                    ; 5,36 : Read Sprite Pixel
        jsr appel
        lda ControlPort+4
        rts

; Efface tous les sprites.
efface: jsr WaitMessage
        lda #6
        ldx #1                     ; 6,1 : Sprite Reset
        jmp appel

; Enregistre gfx dans img.gfx (3,3), puis le charge en mémoire graphique (3,2 vers $FFFF).
images: jsr WaitMessage
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #<gfx
        sta ControlPort+6
        lda #>gfx
        sta ControlPort+7
        lda #<384
        sta ControlPort+8
        lda #>384
        sta ControlPort+9
        lda #3
        ldx #3                     ; 3,3 : Save File
        jsr appel
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #$FF
        sta ControlPort+6          ; $FFFF : mémoire graphique
        sta ControlPort+7
        lda #3
        ldx #2                     ; 3,2 : Load File
        jmp appel
nomgfx: .byte 7
        .text "img.gfx"

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
diviseur: .byte 0

gfx:    .byte 1, 0, 1, 0             ; en-tête : un sprite 16x16
        .fill 252
        .rept 16                   ; 16 lignes : gauche couleur 2, droite couleur 5
        .byte $22, $22, $22, $22, $55, $55, $55, $55
        .endrept
a:      .byte 0
b:      .byte 0
t0:     .text "Avant : ", 0
t2:     .text ", apres : ", 0
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Avant : 5, apres : 0

6,2 Sprite Set Officiel

Place ou modifie un sprite : position, image, retournement, ancre.

Entrées
OctetsTypeRôle
P0uint8numéro du sprite
P1-P2uint16X ($8080 : inchangé)
P3-P4uint16Y
P5uint8image : 0-63 sprites 16×16, +$40 sprites 32×32 ; $80 : inchangée
P6uint8retournement : bit 0 horizontal, bit 1 vertical ; $80 : inchangé
P7uint8ancre 0 à 9 ; $80 : inchangée
Sorties
OctetsTypeRôle
erreuruint81 numéro de sprite ou ancre invalide, 2 image absente

L'exemple place le sprite avec l'ancre 7 (son coin haut gauche en (200, 160)), puis ne change que le retournement : position $8080, image $80 et ancre $80 restent telles quelles, et les deux moitiés s'échangent. En NeoBASIC, sprite n image i anchor a flip f to x,y fait cet appel.

Mots-clés NeoBASIC qui font cet appel : sprite

Texte de la documentation du firmware (anglais)

Set or update the sprite specified in Parameter:0.

The parameters are : Sprite Number, X Low, X High, Y Low, Y High, Image, Flip and Anchor and Flags

Bit 0 of flags specifies 32 bit sprites.

Values that are $80 or $8080 are not updated.

C (llvm-mos)
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

static void xy(uint8_t i, uint16_t x, uint16_t y) {   /* point (x, y) en Pi..Pi+3 */
    ControlPort.params[i] = x & 0xFF;
    ControlPort.params[i + 1] = x >> 8;
    ControlPort.params[i + 2] = y & 0xFF;
    ControlPort.params[i + 3] = y >> 8;
}

static uint8_t gfx[256 + 128];                  /* en-tête + une image 16x16, 4 bits par pixel */

static void images(uint8_t genre) {             /* 1 : une tuile ; 2 : un sprite 16x16 */
    gfx[0] = 1;
    gfx[genre] = 1;
    for (uint8_t y = 0; y < 16; y++)
        for (uint8_t i = 0; i < 8; i++)
            gfx[256 + y * 8 + i] = i < 4 ? 0x22 : 0x55;   /* gauche : couleur 2, droite : 5 */
    neo_file_store("img.gfx", gfx, sizeof gfx);
    neo_file_load("img.gfx", (void *)0xFFFF);   /* $FFFF : mémoire graphique */
}

static uint8_t sprite_pixel(uint16_t x, uint16_t y) {   /* 5,36 : couche des sprites */
    libre();
    xy(0, x, y);
    api(5, 36);
    return ControlPort.params[0];
}

static void modifier(uint16_t x, uint16_t y, uint8_t image, uint8_t flip, uint8_t ancre) {
    libre();
    ControlPort.params[0] = 0;                  /* P0 : sprite 0 */
    xy(1, x, y);                                /* P1-P4 : position */
    ControlPort.params[5] = image;
    ControlPort.params[6] = flip;
    ControlPort.params[7] = ancre;
    api(6, 2);                                  /* Sprite Set */
}

/* Placer un sprite, puis ne changer que son retournement (groupe 6, fonction 2). */
int main(void) {
    images(2);
    modifier(200, 160, 0, 0, 7);                /* image 0, ancre 7 : coin haut gauche */
    uint8_t a = sprite_pixel(202, 165), b = sprite_pixel(212, 165);
    modifier(0x8080, 0x8080, 0x80, 1, 0x80);    /* seul le retournement change */
    uint8_t c = sprite_pixel(202, 165), d = sprite_pixel(212, 165);
    neo_sprite_reset();
    printf("Image : %u %u, retournee : %u %u\n", a, b, c, d);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Image : 2 5, retournee : 5 2
Mad Pascal
// Placer un sprite, puis ne changer que son retournement (groupe 6, fonction 2).
program placer;
uses crt, neo6502;
var a, b, c, d: byte;
    gfx: array[0..383] of byte;                 // en-tête + une image 16x16

procedure images(genre: byte);                  // 1 : une tuile ; 2 : un sprite 16x16
var y, i: byte;
begin
  fillchar(@gfx, 384, 0);
  gfx[0] := 1;
  gfx[genre] := 1;
  for y := 0 to 15 do
    for i := 0 to 7 do
      if i < 4 then gfx[256 + y * 8 + i] := $22   // gauche : couleur 2
      else gfx[256 + y * 8 + i] := $55;         // droite : couleur 5
  NeoSave('img.gfx', word(@gfx), 384);
  NeoLoad('img.gfx', $FFFF);                    // $FFFF : mémoire graphique
end;

begin
  images(2);
  NeoUpdateSprite(0, 200, 160, 0, 0, 7);        // Sprite Set : image 0, ancre 7 (coin haut gauche)
  a := NeoGetSpritePixel(202, 165);
  b := NeoGetSpritePixel(212, 165);
  NeoUpdateSprite(0, $8080, $8080, $80, 1, $80);   // seul le retournement change
  c := NeoGetSpritePixel(202, 165);
  d := NeoGetSpritePixel(212, 165);
  NeoResetSprites;
  writeln('Image : ', a, ' ', b, ', retournee : ', c, ' ', d);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Image : 2 5, retournee : 5 2
NeoBASIC
10 ' Placer un sprite, puis ne changer que son retournement (groupe 6, fonction 2)
20 d = alloc(384) : for i = 0 to 383 : poke d + i, 0 : next i : poke d, 1 : poke d + 2, 1 : ' en-tete
22 for y = 0 to 15 : for i = 0 to 7 : c = $22 : if i > 3 then c = $55
24 poke d + 256 + y * 8 + i, c : next i : next y : ' gauche : couleur 2, droite : 5
26 save "img.gfx", d, 384 : load "img.gfx", $ffff : ' $ffff : memoire graphique
30 sprite 0 image 0 anchor 7 to 200,160
40 a = spoint(202,165) : b = spoint(212,165)
50 sprite 0 flip 1
60 c = spoint(202,165) : d = spoint(212,165)
70 sprite clear
80 print "Image : ";a;" ";b;", retournee : ";c;" ";d
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Image : 2 5, retournee : 5 2
Assembleur 65C02 (64tass)
; Placer un sprite, puis ne changer que son retournement (groupe 6, fonction 2).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  jsr images                 ; un sprite 16x16
        lda #200                   ; ancre 7 : coin haut gauche en (200, 160)
        ldy #160
        jsr place
        jsr lire2
        stx a
        sty b
        jsr WaitMessage
        stz ControlPort+4          ; P0 : sprite 0
        lda #$80
        ldx #6
_inch:  sta ControlPort+5,x        ; P1-P7 : $80, rien ne change...
        dex
        bpl _inch
        lda #1
        sta ControlPort+10         ; ... sauf P6 : retournement horizontal
        lda #6
        ldx #2                     ; fonction 2 : Sprite Set
        jsr appel
        jsr lire2
        stx c
        sty d
        jsr efface
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda a
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda b
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda c
        jsr decimal
        lda #<t6
        ldx #>t6
        jsr ecrire
        lda d
        jsr decimal
        lda #13
        jmp WriteCharacter

; Place le sprite 0 : X en A, Y en Y (inférieurs à 256), image 0, sans retournement, ancre dans ancre.
place:  pha
        jsr WaitMessage
        pla
        stz ControlPort+4          ; P0 : sprite 0
        sta ControlPort+5          ; P1-P2 : X
        stz ControlPort+6
        sty ControlPort+7          ; P3-P4 : Y
        stz ControlPort+8
        stz ControlPort+9          ; P5 : image 0
        stz ControlPort+10         ; P6 : pas de retournement
        lda ancre
        sta ControlPort+11         ; P7 : ancre
        lda #6
        ldx #2                     ; 6,2 : Sprite Set
        jmp appel
ancre:  .byte 7

; Couleur de la couche des sprites en (A, Y), lue par 5,36, rendue dans A.
spixel: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        stz ControlPort+5
        sty ControlPort+6
        stz ControlPort+7
        lda #5
        ldx #36                    ; 5,36 : Read Sprite Pixel
        jsr appel
        lda ControlPort+4
        rts

; Efface tous les sprites.
efface: jsr WaitMessage
        lda #6
        ldx #1                     ; 6,1 : Sprite Reset
        jmp appel

; Enregistre gfx dans img.gfx (3,3), puis le charge en mémoire graphique (3,2 vers $FFFF).
images: jsr WaitMessage
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #<gfx
        sta ControlPort+6
        lda #>gfx
        sta ControlPort+7
        lda #<384
        sta ControlPort+8
        lda #>384
        sta ControlPort+9
        lda #3
        ldx #3                     ; 3,3 : Save File
        jsr appel
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #$FF
        sta ControlPort+6          ; $FFFF : mémoire graphique
        sta ControlPort+7
        lda #3
        ldx #2                     ; 3,2 : Load File
        jmp appel
nomgfx: .byte 7
        .text "img.gfx"

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
diviseur: .byte 0

gfx:    .byte 1, 0, 1, 0             ; en-tête : un sprite 16x16
        .fill 252
        .rept 16                   ; 16 lignes : gauche couleur 2, droite couleur 5
        .byte $22, $22, $22, $22, $55, $55, $55, $55
        .endrept
a:      .byte 0
b:      .byte 0
c:      .byte 0
d:      .byte 0

; Couche des sprites en (202, 165) dans X et en (212, 165) dans Y.
lire2:  lda #202
        ldy #165
        jsr spixel
        pha
        lda #212
        ldy #165
        jsr spixel
        tay
        plx
        rts
t0:     .text "Image : ", 0
t2:     .text " ", 0
t4:     .text ", retournee : ", 0
t6:     .text " ", 0
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Image : 2 5, retournee : 5 2

6,3 Sprite Hide Officiel

Cache un sprite sans oublier ses réglages.

Entrées
OctetsTypeRôle
P0uint8numéro du sprite

Sorties : aucune.

Le sprite réapparaît au prochain 6,2 qui change sa position. Un sprite caché ne compte plus dans les collisions (6,4). En NeoBASIC, sprite n hide fait cet appel.

Mots-clés NeoBASIC qui font cet appel : sprite

Texte de la documentation du firmware (anglais)

Hide the sprite specified in Parameter:0.

C (llvm-mos)
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

static void xy(uint8_t i, uint16_t x, uint16_t y) {   /* point (x, y) en Pi..Pi+3 */
    ControlPort.params[i] = x & 0xFF;
    ControlPort.params[i + 1] = x >> 8;
    ControlPort.params[i + 2] = y & 0xFF;
    ControlPort.params[i + 3] = y >> 8;
}

static uint8_t gfx[256 + 128];                  /* en-tête + une image 16x16, 4 bits par pixel */

static void images(uint8_t genre) {             /* 1 : une tuile ; 2 : un sprite 16x16 */
    gfx[0] = 1;
    gfx[genre] = 1;
    for (uint8_t y = 0; y < 16; y++)
        for (uint8_t i = 0; i < 8; i++)
            gfx[256 + y * 8 + i] = i < 4 ? 0x22 : 0x55;   /* gauche : couleur 2, droite : 5 */
    neo_file_store("img.gfx", gfx, sizeof gfx);
    neo_file_load("img.gfx", (void *)0xFFFF);   /* $FFFF : mémoire graphique */
}

static uint8_t sprite_pixel(uint16_t x, uint16_t y) {   /* 5,36 : couche des sprites */
    libre();
    xy(0, x, y);
    api(5, 36);
    return ControlPort.params[0];
}

/* Cacher un sprite (groupe 6, fonction 3). */
int main(void) {
    images(2);
    neo_sprite_set(0, 200, 170, 0, 0, 0);       /* 6,2 */
    uint8_t a = sprite_pixel(200, 170);
    neo_sprite_hide(0);                         /* Sprite Hide */
    uint8_t b = sprite_pixel(200, 170);
    neo_sprite_reset();
    printf("Visible : %u, cache : %u\n", a, b);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Visible : 5, cache : 0
Mad Pascal
// Cacher un sprite (groupe 6, fonction 3).
program cacher;
uses crt, neo6502;
var a, b: byte;
    gfx: array[0..383] of byte;                 // en-tête + une image 16x16

procedure images(genre: byte);                  // 1 : une tuile ; 2 : un sprite 16x16
var y, i: byte;
begin
  fillchar(@gfx, 384, 0);
  gfx[0] := 1;
  gfx[genre] := 1;
  for y := 0 to 15 do
    for i := 0 to 7 do
      if i < 4 then gfx[256 + y * 8 + i] := $22   // gauche : couleur 2
      else gfx[256 + y * 8 + i] := $55;         // droite : couleur 5
  NeoSave('img.gfx', word(@gfx), 384);
  NeoLoad('img.gfx', $FFFF);                    // $FFFF : mémoire graphique
end;

begin
  images(2);
  NeoUpdateSprite(0, 200, 170, 0, 0, 0);        // 6,2
  a := NeoGetSpritePixel(200, 170);
  NeoHideSprite(0);                             // Sprite Hide
  b := NeoGetSpritePixel(200, 170);
  NeoResetSprites;
  writeln('Visible : ', a, ', cache : ', b);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Visible : 5, cache : 0
NeoBASIC
10 ' Cacher un sprite (groupe 6, fonction 3)
20 d = alloc(384) : for i = 0 to 383 : poke d + i, 0 : next i : poke d, 1 : poke d + 2, 1 : ' en-tete
22 for y = 0 to 15 : for i = 0 to 7 : c = $22 : if i > 3 then c = $55
24 poke d + 256 + y * 8 + i, c : next i : next y : ' gauche : couleur 2, droite : 5
26 save "img.gfx", d, 384 : load "img.gfx", $ffff : ' $ffff : memoire graphique
30 sprite 0 image 0 to 200,170
40 a = spoint(200,170)
50 sprite 0 hide
60 b = spoint(200,170)
70 sprite clear
80 print "Visible : ";a;", cache : ";b
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Visible : 5, cache : 0
Assembleur 65C02 (64tass)
; Cacher un sprite (groupe 6, fonction 3).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  jsr images                 ; un sprite 16x16
        stz ancre                  ; ancre 0 : centre
        lda #200
        ldy #170
        jsr place
        lda #200
        ldy #170
        jsr spixel
        sta a
        jsr WaitMessage
        stz ControlPort+4          ; P0 : sprite 0
        lda #6
        ldx #3                     ; fonction 3 : Sprite Hide
        jsr appel
        lda #200
        ldy #170
        jsr spixel
        sta b
        jsr efface
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda a
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda b
        jsr decimal
        lda #13
        jmp WriteCharacter

; Place le sprite 0 : X en A, Y en Y (inférieurs à 256), image 0, sans retournement, ancre dans ancre.
place:  pha
        jsr WaitMessage
        pla
        stz ControlPort+4          ; P0 : sprite 0
        sta ControlPort+5          ; P1-P2 : X
        stz ControlPort+6
        sty ControlPort+7          ; P3-P4 : Y
        stz ControlPort+8
        stz ControlPort+9          ; P5 : image 0
        stz ControlPort+10         ; P6 : pas de retournement
        lda ancre
        sta ControlPort+11         ; P7 : ancre
        lda #6
        ldx #2                     ; 6,2 : Sprite Set
        jmp appel
ancre:  .byte 7

; Couleur de la couche des sprites en (A, Y), lue par 5,36, rendue dans A.
spixel: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        stz ControlPort+5
        sty ControlPort+6
        stz ControlPort+7
        lda #5
        ldx #36                    ; 5,36 : Read Sprite Pixel
        jsr appel
        lda ControlPort+4
        rts

; Efface tous les sprites.
efface: jsr WaitMessage
        lda #6
        ldx #1                     ; 6,1 : Sprite Reset
        jmp appel

; Enregistre gfx dans img.gfx (3,3), puis le charge en mémoire graphique (3,2 vers $FFFF).
images: jsr WaitMessage
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #<gfx
        sta ControlPort+6
        lda #>gfx
        sta ControlPort+7
        lda #<384
        sta ControlPort+8
        lda #>384
        sta ControlPort+9
        lda #3
        ldx #3                     ; 3,3 : Save File
        jsr appel
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #$FF
        sta ControlPort+6          ; $FFFF : mémoire graphique
        sta ControlPort+7
        lda #3
        ldx #2                     ; 3,2 : Load File
        jmp appel
nomgfx: .byte 7
        .text "img.gfx"

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
diviseur: .byte 0

gfx:    .byte 1, 0, 1, 0             ; en-tête : un sprite 16x16
        .fill 252
        .rept 16                   ; 16 lignes : gauche couleur 2, droite couleur 5
        .byte $22, $22, $22, $22, $55, $55, $55, $55
        .endrept
a:      .byte 0
b:      .byte 0
t0:     .text "Visible : ", 0
t2:     .text ", cache : ", 0
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Visible : 5, cache : 0

6,4 Sprite Collision Officiel

Dit si les centres de deux sprites sont à une distance au plus égale à une valeur donnée.

Entrées
OctetsTypeRôle
P0uint8premier sprite
P1uint8second sprite
P2uint8distance maximale, en pixels
Sorties
OctetsTypeRôle
P0uint8non nul si la distance est inférieure ou égale
erreuruint81 si un numéro de sprite est invalide

Le firmware compare le carré de la distance entre les points d'ancrage (les coordonnées données à 6,2) au carré de P2. Deux sprites doivent être visibles, sinon la réponse est 0. Les sprites de l'exemple sont à 20 pixels. En NeoBASIC, hit(a, b, d) fait cet appel.

Mots-clés NeoBASIC qui font cet appel : hit()

Texte de la documentation du firmware (anglais)

Parameter:0 is non-zero if the distance is less than or equal to Parameter:2 between the center of the sprite with index specified in Parameter:0 and the center of the sprite with index specified in Parameter:1 .

C (llvm-mos)
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

static void xy(uint8_t i, uint16_t x, uint16_t y) {   /* point (x, y) en Pi..Pi+3 */
    ControlPort.params[i] = x & 0xFF;
    ControlPort.params[i + 1] = x >> 8;
    ControlPort.params[i + 2] = y & 0xFF;
    ControlPort.params[i + 3] = y >> 8;
}

static uint8_t gfx[256 + 128];                  /* en-tête + une image 16x16, 4 bits par pixel */

static void images(uint8_t genre) {             /* 1 : une tuile ; 2 : un sprite 16x16 */
    gfx[0] = 1;
    gfx[genre] = 1;
    for (uint8_t y = 0; y < 16; y++)
        for (uint8_t i = 0; i < 8; i++)
            gfx[256 + y * 8 + i] = i < 4 ? 0x22 : 0x55;   /* gauche : couleur 2, droite : 5 */
    neo_file_store("img.gfx", gfx, sizeof gfx);
    neo_file_load("img.gfx", (void *)0xFFFF);   /* $FFFF : mémoire graphique */
}

static uint8_t sprite_pixel(uint16_t x, uint16_t y) {   /* 5,36 : couche des sprites */
    libre();
    xy(0, x, y);
    api(5, 36);
    return ControlPort.params[0];
}

/* Deux sprites à 20 pixels : se touchent-ils ? (groupe 6, fonction 4) */
int main(void) {
    images(2);
    neo_sprite_set(0, 100, 170, 0, 0, 0);
    neo_sprite_set(1, 120, 170, 0, 0, 0);
    uint8_t a = neo_sprite_collision(0, 1, 10);   /* Sprite Collision */
    uint8_t b = neo_sprite_collision(0, 1, 20);
    neo_sprite_reset();
    printf("Distance 10 : %u, distance 20 : %u\n", a, b);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Distance 10 : 0, distance 20 : 1
Mad Pascal
// Deux sprites à 20 pixels : se touchent-ils ? (groupe 6, fonction 4)
program collision;
uses crt, neo6502;
var a, b: byte;
    gfx: array[0..383] of byte;                 // en-tête + une image 16x16

procedure images(genre: byte);                  // 1 : une tuile ; 2 : un sprite 16x16
var y, i: byte;
begin
  fillchar(@gfx, 384, 0);
  gfx[0] := 1;
  gfx[genre] := 1;
  for y := 0 to 15 do
    for i := 0 to 7 do
      if i < 4 then gfx[256 + y * 8 + i] := $22   // gauche : couleur 2
      else gfx[256 + y * 8 + i] := $55;         // droite : couleur 5
  NeoSave('img.gfx', word(@gfx), 384);
  NeoLoad('img.gfx', $FFFF);                    // $FFFF : mémoire graphique
end;

begin
  images(2);
  NeoUpdateSprite(0, 100, 170, 0, 0, 0);
  NeoUpdateSprite(1, 120, 170, 0, 0, 0);
  a := NeoInRange(0, 1, 10);                    // Sprite Collision
  b := NeoInRange(0, 1, 20);
  NeoResetSprites;
  writeln('Distance 10 : ', a, ', distance 20 : ', b);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Distance 10 : 0, distance 20 : 1
NeoBASIC
10 ' Deux sprites a 20 pixels : se touchent-ils ? (groupe 6, fonction 4)
20 d = alloc(384) : for i = 0 to 383 : poke d + i, 0 : next i : poke d, 1 : poke d + 2, 1 : ' en-tete
22 for y = 0 to 15 : for i = 0 to 7 : c = $22 : if i > 3 then c = $55
24 poke d + 256 + y * 8 + i, c : next i : next y : ' gauche : couleur 2, droite : 5
26 save "img.gfx", d, 384 : load "img.gfx", $ffff : ' $ffff : memoire graphique
30 sprite 0 image 0 to 100,170
40 sprite 1 image 0 to 120,170
50 a = hit(0,1,10) : b = hit(0,1,20)
60 sprite clear
70 print "Distance 10 : ";a;", distance 20 : ";b
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Distance 10 : 0, distance 20 : -1
Assembleur 65C02 (64tass)
; Deux sprites à 20 pixels : se touchent-ils ? (groupe 6, fonction 4).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  jsr images                 ; un sprite 16x16
        stz ancre
        lda #100
        ldy #170
        jsr place                  ; sprite 0 en (100, 170)
        jsr WaitMessage
        lda #1
        sta ControlPort+4          ; sprite 1 en (120, 170) : mêmes réglages
        lda #120
        sta ControlPort+5
        lda #6
        ldx #2
        jsr appel
        lda #10
        jsr touche
        sta a
        lda #20
        jsr touche
        sta b
        jsr efface
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda a
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda b
        jsr decimal
        lda #13
        jmp WriteCharacter

; Place le sprite 0 : X en A, Y en Y (inférieurs à 256), image 0, sans retournement, ancre dans ancre.
place:  pha
        jsr WaitMessage
        pla
        stz ControlPort+4          ; P0 : sprite 0
        sta ControlPort+5          ; P1-P2 : X
        stz ControlPort+6
        sty ControlPort+7          ; P3-P4 : Y
        stz ControlPort+8
        stz ControlPort+9          ; P5 : image 0
        stz ControlPort+10         ; P6 : pas de retournement
        lda ancre
        sta ControlPort+11         ; P7 : ancre
        lda #6
        ldx #2                     ; 6,2 : Sprite Set
        jmp appel
ancre:  .byte 7

; Couleur de la couche des sprites en (A, Y), lue par 5,36, rendue dans A.
spixel: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        stz ControlPort+5
        sty ControlPort+6
        stz ControlPort+7
        lda #5
        ldx #36                    ; 5,36 : Read Sprite Pixel
        jsr appel
        lda ControlPort+4
        rts

; Efface tous les sprites.
efface: jsr WaitMessage
        lda #6
        ldx #1                     ; 6,1 : Sprite Reset
        jmp appel

; Enregistre gfx dans img.gfx (3,3), puis le charge en mémoire graphique (3,2 vers $FFFF).
images: jsr WaitMessage
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #<gfx
        sta ControlPort+6
        lda #>gfx
        sta ControlPort+7
        lda #<384
        sta ControlPort+8
        lda #>384
        sta ControlPort+9
        lda #3
        ldx #3                     ; 3,3 : Save File
        jsr appel
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #$FF
        sta ControlPort+6          ; $FFFF : mémoire graphique
        sta ControlPort+7
        lda #3
        ldx #2                     ; 3,2 : Load File
        jmp appel
nomgfx: .byte 7
        .text "img.gfx"

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
diviseur: .byte 0

gfx:    .byte 1, 0, 1, 0             ; en-tête : un sprite 16x16
        .fill 252
        .rept 16                   ; 16 lignes : gauche couleur 2, droite couleur 5
        .byte $22, $22, $22, $22, $55, $55, $55, $55
        .endrept
a:      .byte 0
b:      .byte 0

; Sprites 0 et 1 à une distance d'au plus A ? Réponse dans A.
touche: pha
        jsr WaitMessage
        pla
        sta ControlPort+6          ; P2 : distance
        stz ControlPort+4          ; P0 : sprite 0
        lda #1
        sta ControlPort+5          ; P1 : sprite 1
        lda #6
        ldx #4                     ; fonction 4 : Sprite Collision
        jsr appel
        lda ControlPort+4
        rts
t0:     .text "Distance 10 : ", 0
t2:     .text ", distance 20 : ", 0
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Distance 10 : 0, distance 20 : 1

6,5 Sprite Position Officiel

Donne la position d'un sprite : les coordonnées de son point d'ancrage.

Entrées
OctetsTypeRôle
P0uint8numéro du sprite
Sorties
OctetsTypeRôle
P1-P2uint16X
P3-P4uint16Y
erreuruint81 si le numéro de sprite est invalide

Ce sont les coordonnées données au dernier 6,2 (le point d'ancrage), pas le coin du sprite : l'exemple place le sprite avec l'ancre 7 et relit (150, 160). En NeoBASIC, spritex(n) et spritey(n) font cet appel.

Mots-clés NeoBASIC qui font cet appel : spritey()

Texte de la documentation du firmware (anglais)

Deposit into Parameters:1..4, the screen coordinates of the sprite with the index specified in Parameter:0.

C (llvm-mos)
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

static void xy(uint8_t i, uint16_t x, uint16_t y) {   /* point (x, y) en Pi..Pi+3 */
    ControlPort.params[i] = x & 0xFF;
    ControlPort.params[i + 1] = x >> 8;
    ControlPort.params[i + 2] = y & 0xFF;
    ControlPort.params[i + 3] = y >> 8;
}

static uint8_t gfx[256 + 128];                  /* en-tête + une image 16x16, 4 bits par pixel */

static void images(uint8_t genre) {             /* 1 : une tuile ; 2 : un sprite 16x16 */
    gfx[0] = 1;
    gfx[genre] = 1;
    for (uint8_t y = 0; y < 16; y++)
        for (uint8_t i = 0; i < 8; i++)
            gfx[256 + y * 8 + i] = i < 4 ? 0x22 : 0x55;   /* gauche : couleur 2, droite : 5 */
    neo_file_store("img.gfx", gfx, sizeof gfx);
    neo_file_load("img.gfx", (void *)0xFFFF);   /* $FFFF : mémoire graphique */
}

static uint8_t sprite_pixel(uint16_t x, uint16_t y) {   /* 5,36 : couche des sprites */
    libre();
    xy(0, x, y);
    api(5, 36);
    return ControlPort.params[0];
}

/* Relire la position d'un sprite (groupe 6, fonction 5). */
int main(void) {
    uint16_t x, y;
    images(2);
    neo_sprite_set(0, 150, 160, 0, 0, 7);       /* ancre 7 : coin haut gauche */
    neo_sprite_position(0, &x, &y);             /* Sprite Position */
    neo_sprite_reset();
    printf("Sprite 0 : %u, %u\n", x, y);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Sprite 0 : 150, 160
Mad Pascal
// Relire la position d'un sprite (groupe 6, fonction 5).
program position;
uses crt, neo6502;
var x, y: word;
    gfx: array[0..383] of byte;                 // en-tête + une image 16x16

procedure images(genre: byte);                  // 1 : une tuile ; 2 : un sprite 16x16
var y, i: byte;
begin
  fillchar(@gfx, 384, 0);
  gfx[0] := 1;
  gfx[genre] := 1;
  for y := 0 to 15 do
    for i := 0 to 7 do
      if i < 4 then gfx[256 + y * 8 + i] := $22   // gauche : couleur 2
      else gfx[256 + y * 8 + i] := $55;         // droite : couleur 5
  NeoSave('img.gfx', word(@gfx), 384);
  NeoLoad('img.gfx', $FFFF);                    // $FFFF : mémoire graphique
end;

begin
  images(2);
  NeoUpdateSprite(0, 150, 160, 0, 0, 7);        // ancre 7 : coin haut gauche
  NeoGetSpriteXY(0, x, y);                      // Sprite Position
  NeoResetSprites;
  writeln('Sprite 0 : ', x, ', ', y);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Sprite 0 : 150, 160
NeoBASIC
10 ' Relire la position d'un sprite (groupe 6, fonction 5)
20 d = alloc(384) : for i = 0 to 383 : poke d + i, 0 : next i : poke d, 1 : poke d + 2, 1 : ' en-tete
22 for y = 0 to 15 : for i = 0 to 7 : c = $22 : if i > 3 then c = $55
24 poke d + 256 + y * 8 + i, c : next i : next y : ' gauche : couleur 2, droite : 5
26 save "img.gfx", d, 384 : load "img.gfx", $ffff : ' $ffff : memoire graphique
30 sprite 0 image 0 anchor 7 to 150,160
40 x = spritex(0) : y = spritey(0)
50 sprite clear
60 print "Sprite 0 : ";x;", ";y
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Sprite 0 : 150, 160
Assembleur 65C02 (64tass)
; Relire la position d'un sprite (groupe 6, fonction 5).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  jsr images                 ; un sprite 16x16
        lda #150                   ; ancre 7 : coin haut gauche
        ldy #160
        jsr place
        jsr WaitMessage
        stz ControlPort+4          ; P0 : sprite 0
        lda #6
        ldx #5                     ; fonction 5 : Sprite Position
        jsr appel
        lda ControlPort+5          ; P1 : X (poids faible, < 256 ici)
        sta x
        lda ControlPort+7          ; P3 : Y
        sta y
        jsr efface
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda x
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda y
        jsr decimal
        lda #13
        jmp WriteCharacter

; Place le sprite 0 : X en A, Y en Y (inférieurs à 256), image 0, sans retournement, ancre dans ancre.
place:  pha
        jsr WaitMessage
        pla
        stz ControlPort+4          ; P0 : sprite 0
        sta ControlPort+5          ; P1-P2 : X
        stz ControlPort+6
        sty ControlPort+7          ; P3-P4 : Y
        stz ControlPort+8
        stz ControlPort+9          ; P5 : image 0
        stz ControlPort+10         ; P6 : pas de retournement
        lda ancre
        sta ControlPort+11         ; P7 : ancre
        lda #6
        ldx #2                     ; 6,2 : Sprite Set
        jmp appel
ancre:  .byte 7

; Couleur de la couche des sprites en (A, Y), lue par 5,36, rendue dans A.
spixel: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        stz ControlPort+5
        sty ControlPort+6
        stz ControlPort+7
        lda #5
        ldx #36                    ; 5,36 : Read Sprite Pixel
        jsr appel
        lda ControlPort+4
        rts

; Efface tous les sprites.
efface: jsr WaitMessage
        lda #6
        ldx #1                     ; 6,1 : Sprite Reset
        jmp appel

; Enregistre gfx dans img.gfx (3,3), puis le charge en mémoire graphique (3,2 vers $FFFF).
images: jsr WaitMessage
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #<gfx
        sta ControlPort+6
        lda #>gfx
        sta ControlPort+7
        lda #<384
        sta ControlPort+8
        lda #>384
        sta ControlPort+9
        lda #3
        ldx #3                     ; 3,3 : Save File
        jsr appel
        lda #<nomgfx
        sta ControlPort+4
        lda #>nomgfx
        sta ControlPort+5
        lda #$FF
        sta ControlPort+6          ; $FFFF : mémoire graphique
        sta ControlPort+7
        lda #3
        ldx #2                     ; 3,2 : Load File
        jmp appel
nomgfx: .byte 7
        .text "img.gfx"

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
diviseur: .byte 0

gfx:    .byte 1, 0, 1, 0             ; en-tête : un sprite 16x16
        .fill 252
        .rept 16                   ; 16 lignes : gauche couleur 2, droite couleur 5
        .byte $22, $22, $22, $22, $55, $55, $55, $55
        .endrept
x:      .byte 0
y:      .byte 0
t0:     .text "Sprite 0 : ", 0
t2:     .text ", ", 0
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Sprite 0 : 150, 160

← L'API du Neo6502