Group 6 — Sprites Official
6,1 Sprite Reset · 6,2 Sprite Set · 6,3 Sprite Hide · 6,4 Sprite Collision · 6,5 Sprite Position
A sprite is an image from graphics memory (see group 5) shown over the screen without damaging it: sprites use the high 4 bits of each pixel, the background the low 4 bits. 5,36 reads the sprite layer, 5,33 the background.
6,2 places a sprite. Its image number is the index of the image (0 to 63) among the 16 × 16 sprites, or among the 32 × 32 sprites when $40 is added. The value $80 means "unchanged", as do $8080 for the position and $80 for flip and anchor: a single setting can thus be changed. NeoBASIC clears bit 7 of the image number, so sprite n image $80 (the 5,7 numbering) means image 0 there; through the direct call, $80 on a new sprite draws nothing (checked on the bench).
The anchor tells which point of the sprite lies at the given coordinates: 0 the centre, then 1 to 9 like the numeric keypad (7 top left, 5 centre, 3 bottom right). The firmware documentation mentions a flags byte (32 bits) in P8; the code does not read it, the size comes from bit 6 of the image number.
The examples load the group 5 graphics file declared as one 16 × 16 sprite (left half colour 2, right half colour 5), and clear the sprites (6,1) before returning.
6,1 Sprite Reset Official
Removes all sprites and resets the sprite system.
Inputs: none.
Outputs: none.
In NeoBASIC, sprite clear makes this call, as does clear (and so run).
NeoBASIC keywords that make this call: clear, sprite
Firmware documentation text
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;
}Avant : 5, apres : 0Mad 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.Avant : 5, apres : 0NeoBASIC
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 : ";bAvant : 5, apres : 065C02 assembly (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 : ", 0Avant : 5, apres : 06,2 Sprite Set Official
Places or updates a sprite: position, image, flip, anchor.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | sprite number |
P1-P2 | uint16 | X ($8080: unchanged) |
P3-P4 | uint16 | Y |
P5 | uint8 | image: 0-63 16×16 sprites, +$40 32×32 sprites; $80: unchanged |
P6 | uint8 | flip: bit 0 horizontal, bit 1 vertical; $80: unchanged |
P7 | uint8 | anchor 0 to 9; $80: unchanged |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 invalid sprite number or anchor, 2 missing image |
The example places the sprite with anchor 7 (its top left corner at (200, 160)), then only changes the flip: position $8080, image $80 and anchor $80 are left as they are, and the two halves swap. In NeoBASIC, sprite n image i anchor a flip f to x,y makes this call.
NeoBASIC keywords that make this call: sprite
Firmware documentation text
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;
}Image : 2 5, retournee : 5 2Mad 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.Image : 2 5, retournee : 5 2NeoBASIC
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;" ";dImage : 2 5, retournee : 5 265C02 assembly (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 " ", 0Image : 2 5, retournee : 5 26,3 Sprite Hide Official
Hides a sprite while keeping its settings.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | sprite number |
Outputs: none.
The sprite comes back at the next 6,2 that changes its position. A hidden sprite no longer counts in collisions (6,4). In NeoBASIC, sprite n hide makes this call.
NeoBASIC keywords that make this call: sprite
Firmware documentation text
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;
}Visible : 5, cache : 0Mad 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.Visible : 5, cache : 0NeoBASIC
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 : ";bVisible : 5, cache : 065C02 assembly (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 : ", 0Visible : 5, cache : 06,4 Sprite Collision Official
Tells whether the centres of two sprites are at most a given distance apart.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | first sprite |
P1 | uint8 | second sprite |
P2 | uint8 | maximum distance, in pixels |
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | non zero if the distance is less than or equal |
erreur | uint8 | 1 if a sprite number is invalid |
The firmware compares the square of the distance between the anchor points (the coordinates given to 6,2) with the square of P2. Both sprites must be visible, otherwise the answer is 0. The example sprites are 20 pixels apart. In NeoBASIC, hit(a, b, d) makes this call.
NeoBASIC keywords that make this call: hit()
Firmware documentation text
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;
}Distance 10 : 0, distance 20 : 1Mad 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.Distance 10 : 0, distance 20 : 1NeoBASIC
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 : ";bDistance 10 : 0, distance 20 : -165C02 assembly (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 : ", 0Distance 10 : 0, distance 20 : 16,5 Sprite Position Official
Gives the position of a sprite: the coordinates of its anchor point.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | sprite number |
| Bytes | Type | Meaning |
|---|---|---|
P1-P2 | uint16 | X |
P3-P4 | uint16 | Y |
erreur | uint8 | 1 if the sprite number is invalid |
These are the coordinates given to the last 6,2 (the anchor point), not the sprite corner: the example places the sprite with anchor 7 and reads (150, 160) back. In NeoBASIC, spritex(n) and spritey(n) make this call.
NeoBASIC keywords that make this call: spritey()
Firmware documentation text
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;
}Sprite 0 : 150, 160Mad 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.Sprite 0 : 150, 160NeoBASIC
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;", ";ySprite 0 : 150, 16065C02 assembly (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 ", ", 0Sprite 0 : 150, 160