Group 8 — Sound Official
8,1 Reset Sound · 8,2 Reset Channel · 8,3 Beep · 8,4 Queue Sound · 8,5 Play Sound · 8,6 Sound Status · 8,7 Queue Sound Extended · 8,8 Get Channel Count · 8,9 Set Channel Volume · 8,10 Get Channel Volume · 8,11 Stream Start · 8,12 Stream Stop · 8,13 Stream Status · 8,14 Stream Filled · 8,15 Get Master Volume · 8,16 Set Master Volume
Sound has 4 channels (0 to 3). Each channel has a queue of at most 32 notes: a queued note (8,4 or 8,7) plays as soon as the previous one ends, and the program goes on without waiting. 8,6 tells how many notes are left, including the one playing. A note beyond 32 is ignored, without an error.
A note has a frequency in Hz, a duration in hundredths of a second (counted in steps of 2 cs, as the firmware steps at 50 Hz: 25 cs last 24), a slide in Hz per hundredth, a waveform (0 square, 1 noise) and a volume from 0 to 100. While sliding, the frequency stays between 100 and 1200 Hz: past that, it wraps 1100 Hz lower or higher. A frequency of 0 makes a silence of the given duration.
Trinity adds the volume of a channel while it plays (8,9, 8,10), a stream of PCM samples mixed with the 4 channels (8,11 to 8,14) and a master volume (8,15, 8,16). Volumes 0-100 are stored as 0 to 127: a value read back may lose one unit (25 comes back as 24).
On the bench, the emulator's sound is recorded and checked: it must be heard for the examples that play, and stay silent when the sound is muted (8,16). The examples do not just play: they print what the firmware reports (queued notes, volumes, stream state).
8,1 Reset Sound Official
Silences all sound: empties the queues of the 4 channels and stops the sample stream.
Inputs: none.
Outputs: none.
The example queues 3 notes on channel 0 and 2 on channel 1, then clears them all at once. The master volume (8,16) is left alone. In NeoBASIC, sound clear makes this call.
NeoBASIC keywords that make this call: sound
Firmware documentation text
Reset the sound system. This empties all channel queues and silences all channels immediately.
C (llvm-mos)
/* Vider d'un coup les files de tous les canaux (groupe 8, fonction 1). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_sound_queue(0, 262, 50, 0, 0); /* canal 0 : trois notes de 50 cs */
neo_sound_queue(0, 330, 50, 0, 0);
neo_sound_queue(0, 392, 50, 0, 0);
neo_sound_queue(1, 131, 50, 0, 0); /* canal 1 : deux notes */
neo_sound_queue(1, 196, 50, 0, 0);
uint8_t a = neo_sound_status(0), b = neo_sound_status(1);
neo_sound_reset(); /* Reset Sound : silence et files vides partout */
printf("Avant : %u et %u, apres : %u et %u\n", a, b, neo_sound_status(0), neo_sound_status(1));
return 0;
}Avant : 3 et 2, apres : 0 et 0Mad Pascal
// Vider d'un coup les files de tous les canaux (groupe 8, fonction 1).
program resetsound;
uses crt, neo6502;
var a, b: byte;
begin
NeoQueueNote(0, 262, 50, 0, 0); // canal 0 : trois notes de 50 cs
NeoQueueNote(0, 330, 50, 0, 0);
NeoQueueNote(0, 392, 50, 0, 0);
NeoQueueNote(1, 131, 50, 0, 0); // canal 1 : deux notes
NeoQueueNote(1, 196, 50, 0, 0);
a := NeoGetQueueLen(0);
b := NeoGetQueueLen(1);
NeoMute; // Reset Sound : silence et files vides partout
writeln('Avant : ', a, ' et ', b, ', apres : ', NeoGetQueueLen(0), ' et ', NeoGetQueueLen(1));
end.Avant : 3 et 2, apres : 0 et 0NeoBASIC
10 ' Vider d'un coup les files de tous les canaux (groupe 8, fonction 1)
20 sound 0,262,50 : sound 0,330,50 : sound 0,392,50 : ' canal 0 : trois notes de 50 cs
30 sound 1,131,50 : sound 1,196,50 : ' canal 1 : deux notes
40 a = notes(0) : b = notes(1)
50 sound clear : ' Reset Sound : silence et files vides partout
60 print "Avant : ";a;" et ";b;", apres : ";notes(0);" et ";notes(1)Avant : 3 et 2, apres : 0 et 065C02 assembly (64tass)
; Vider d'un coup les files de tous les canaux (groupe 8, 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: lda #<n1
ldx #>n1
jsr envoi
lda #<n2
ldx #>n2
jsr envoi
lda #<n3
ldx #>n3
jsr envoi
lda #<n4
ldx #>n4
jsr envoi
lda #<n5
ldx #>n5
jsr envoi
lda #0
jsr statut
sta a
lda #1
jsr statut
sta b
lda #<raz
ldx #>raz
jsr envoi
; Reset Sound : silence et files vides partout
lda #<t0
ldx #>t0
jsr ecrire
lda a
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
lda b
jsr decimal
lda #<t2
ldx #>t2
jsr ecrire
lda #0
jsr statut
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
lda #1
jsr statut
jsr decimal
lda #13
jsr WriteCharacter
rts
n1: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : trois notes de 50 cs
.word 262, 50, 0
.byte 0
n2: .byte 8, 4, 8, 0 ; 8,4 : canal 0
.word 330, 50, 0
.byte 0
n3: .byte 8, 4, 8, 0 ; 8,4 : canal 0
.word 392, 50, 0
.byte 0
n4: .byte 8, 4, 8, 1 ; 8,4 : canal 1 : deux notes
.word 131, 50, 0
.byte 0
n5: .byte 8, 4, 8, 1 ; 8,4 : canal 1
.word 196, 50, 0
.byte 0
raz: .byte 8, 1, 0 ; 8,1 : Reset Sound
t0: .text "Avant : ", 0
t1: .text " et ", 0
t2: .text ", apres : ", 0
a: .byte 0
b: .byte 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Avant : 3 et 2, apres : 0 et 08,2 Reset Channel Official
Silences one channel: empties its queue and stops it, leaving the others alone.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | channel (0 to 3) |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the channel does not exist |
The two notes of channel 1 stay queued. In NeoBASIC, sound n clear makes this call; sfx makes it too, before the effect.
NeoBASIC keywords that make this call: sfx, sound
Firmware documentation text
Reset the sound channel specified in Parameter:0.
C (llvm-mos)
/* Vider la file d'un seul canal (groupe 8, fonction 2). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_sound_queue(0, 262, 50, 0, 0); /* canal 0 : trois notes de 50 cs */
neo_sound_queue(0, 330, 50, 0, 0);
neo_sound_queue(0, 392, 50, 0, 0);
neo_sound_queue(1, 131, 50, 0, 0); /* canal 1 : deux notes */
neo_sound_queue(1, 196, 50, 0, 0);
neo_sound_reset_channel(0); /* Reset Channel : le canal 0 seulement */
printf("Canal 0 : %u, canal 1 : %u\n", neo_sound_status(0), neo_sound_status(1));
neo_sound_reset(); /* 8,1 : faire taire le canal 1 aussi */
return 0;
}Canal 0 : 0, canal 1 : 2Mad Pascal
// Vider la file d'un seul canal (groupe 8, fonction 2).
program resetchannel;
uses crt, neo6502;
begin
NeoQueueNote(0, 262, 50, 0, 0); // canal 0 : trois notes de 50 cs
NeoQueueNote(0, 330, 50, 0, 0);
NeoQueueNote(0, 392, 50, 0, 0);
NeoQueueNote(1, 131, 50, 0, 0); // canal 1 : deux notes
NeoQueueNote(1, 196, 50, 0, 0);
NeoMute(0); // Reset Channel : le canal 0 seulement
writeln('Canal 0 : ', NeoGetQueueLen(0), ', canal 1 : ', NeoGetQueueLen(1));
NeoMute; // 8,1 : faire taire le canal 1 aussi
end.Canal 0 : 0, canal 1 : 2NeoBASIC
10 ' Vider la file d'un seul canal (groupe 8, fonction 2)
20 sound 0,262,50 : sound 0,330,50 : sound 0,392,50 : ' canal 0 : trois notes de 50 cs
30 sound 1,131,50 : sound 1,196,50 : ' canal 1 : deux notes
40 sound 0 clear : ' Reset Channel : le canal 0 seulement
50 print "Canal 0 : ";notes(0);", canal 1 : ";notes(1)
60 sound clear : ' 8,1 : faire taire le canal 1 aussiCanal 0 : 0, canal 1 : 265C02 assembly (64tass)
; Vider la file d'un seul canal (groupe 8, 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: lda #<n1
ldx #>n1
jsr envoi
lda #<n2
ldx #>n2
jsr envoi
lda #<n3
ldx #>n3
jsr envoi
lda #<n4
ldx #>n4
jsr envoi
lda #<n5
ldx #>n5
jsr envoi
lda #<raz0
ldx #>raz0
jsr envoi
; Reset Channel : le canal 0 seulement
lda #<t0
ldx #>t0
jsr ecrire
lda #0
jsr statut
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
lda #1
jsr statut
jsr decimal
lda #13
jsr WriteCharacter
lda #<raz
ldx #>raz
jsr envoi
; 8,1 : faire taire le canal 1 aussi
rts
n1: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : trois notes de 50 cs
.word 262, 50, 0
.byte 0
n2: .byte 8, 4, 8, 0 ; 8,4 : canal 0
.word 330, 50, 0
.byte 0
n3: .byte 8, 4, 8, 0 ; 8,4 : canal 0
.word 392, 50, 0
.byte 0
n4: .byte 8, 4, 8, 1 ; 8,4 : canal 1 : deux notes
.word 131, 50, 0
.byte 0
n5: .byte 8, 4, 8, 1 ; 8,4 : canal 1
.word 196, 50, 0
.byte 0
raz0: .byte 8, 2, 1, 0 ; 8,2 : Reset Channel, P0 = canal 0
raz: .byte 8, 1, 0 ; 8,1 : Reset Sound
t0: .text "Canal 0 : ", 0
t1: .text ", canal 1 : ", 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Canal 0 : 0, canal 1 : 28,3 Beep Official
Plays the startup beep: A 220 Hz for 0.5 s then A 440 Hz for 0.25 s, on channel 0.
Inputs: none.
Outputs: none.
The two notes are queued on channel 0, behind those already there. The firmware plays this beep on the board only (code reserved for the Pico, in the official firmware as in Trinity): in the emulator, the call does nothing and the queue stays empty, hence the immediate observed output. NeoBASIC has no keyword for this call.
Firmware documentation text
Play the startup beep immediately.
C (llvm-mos)
/* Jouer le bip du démarrage (groupe 8, fonction 3). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_sound_beep(); /* Beep : deux notes sur le canal 0 */
while (neo_sound_status(0) != 0) /* 8,6 : attendre la fin du bip */
;
printf("Bip termine\n");
return 0;
}Bip termineMad Pascal
// Jouer le bip du démarrage (groupe 8, fonction 3).
program beep;
uses crt, neo6502;
begin
NeoBeep; // Beep : deux notes sur le canal 0
repeat until NeoGetQueueLen(0) = 0; // 8,6 : attendre la fin du bip
writeln('Bip termine');
end.Bip termineNeoBASIC
10 ' Jouer le bip du demarrage (groupe 8, fonction 3)
20 while peek($ff00) : wend : poke $ff01, 3 : poke $ff00, 8 : ' Beep : deux notes sur le canal 0
30 repeat : until notes(0) = 0 : ' 8,6 : attendre la fin du bip
40 print "Bip termine"Bip termine65C02 assembly (64tass)
; Jouer le bip du démarrage (groupe 8, 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: lda #<bip
ldx #>bip
jsr envoi
jsr attendre ; 8,6 : attendre la fin du bip
lda #<t0
ldx #>t0
jsr ecrire
rts
bip: .byte 8, 3, 0 ; 8,3 : Beep, deux notes sur le canal 0
t0: .text "Bip termine", 13, 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Bip termine8,4 Queue Sound Official
Queues a note on a channel: square wave, volume 100.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | channel (0 to 3) |
P1-P2 | uint16 | frequency in Hz (0: silence) |
P3-P4 | uint16 | duration in hundredths of a second |
P5-P6 | int16 | slide in Hz per hundredth |
P7 | uint8 | "source" in the documentation: the firmware does not read it |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the channel does not exist |
The example plays C, E, G, then a high C rising by 20 Hz per hundredth; past 1200 Hz, the slide wraps 1100 Hz lower. NeoBASIC has no keyword for 8,4: sound and noise go through 8,7.
Firmware documentation text
Queue a sound. Refer to Section \#\ref{sound} "Sound" for details.
The parameters are : Channel, Frequency Low, Frequency High, Duration Low, Duration High, Slide Low, Slide High and Source.
C (llvm-mos)
/* Mettre en file un petit arpège et un glissando (groupe 8, fonction 4). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_sound_queue(0, 262, 25, 0, 0); /* Queue Sound : do 262 Hz, 25 cs */
neo_sound_queue(0, 330, 25, 0, 0); /* mi */
neo_sound_queue(0, 392, 25, 0, 0); /* sol */
neo_sound_queue(0, 523, 50, 20, 0); /* do aigu qui monte de 20 Hz par cs */
printf("En file : %u notes\n", neo_sound_status(0));
while (neo_sound_status(0) != 0) /* 8,6 : attendre la fin */
;
printf("Fini\n");
return 0;
}En file : 4 notes
FiniMad Pascal
// Mettre en file un petit arpège et un glissando (groupe 8, fonction 4).
program queuesound;
uses crt, neo6502;
begin
NeoQueueNote(0, 262, 25, 0, 0); // Queue Sound : do 262 Hz, 25 cs
NeoQueueNote(0, 330, 25, 0, 0); // mi
NeoQueueNote(0, 392, 25, 0, 0); // sol
NeoQueueNote(0, 523, 50, 20, 0); // do aigu qui monte de 20 Hz par cs
writeln('En file : ', NeoGetQueueLen(0), ' notes');
repeat until NeoGetQueueLen(0) = 0; // 8,6 : attendre la fin
writeln('Fini');
end.En file : 4 notes
FiniNeoBASIC
10 ' Mettre en file un petit arpege et un glissando (groupe 8, fonction 4)
20 f = 262 : t = 25 : g = 0 : gosub 1000 : ' do 262 Hz, 25 cs
30 f = 330 : gosub 1000 : ' mi
40 f = 392 : gosub 1000 : ' sol
50 f = 523 : t = 50 : g = 20 : gosub 1000 : ' do aigu qui monte de 20 Hz par cs
60 print "En file : ";notes(0);" notes"
70 repeat : until notes(0) = 0 : ' 8,6 : attendre la fin
80 print "Fini"
90 end
1000 ' Queue Sound sur le canal 0 : frequence f, duree t (cs), glissement g
1010 while peek($ff00) : wend
1020 poke $ff04, 0 : doke $ff05, f : doke $ff07, t : doke $ff09, g : poke $ff0b, 0
1030 poke $ff01, 4 : poke $ff00, 8
1040 while peek($ff00) : wend
1050 returnEn file : 4 notes
Fini65C02 assembly (64tass)
; Mettre en file un petit arpège et un glissando (groupe 8, 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: lda #<n1
ldx #>n1
jsr envoi
lda #<n2
ldx #>n2
jsr envoi
lda #<n3
ldx #>n3
jsr envoi
lda #<n4
ldx #>n4
jsr envoi
lda #<t0
ldx #>t0
jsr ecrire
lda #0
jsr statut
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
jsr attendre ; 8,6 : attendre la fin
lda #<t2
ldx #>t2
jsr ecrire
rts
n1: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : do 262 Hz, 25 cs
.word 262, 25, 0
.byte 0
n2: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : mi
.word 330, 25, 0
.byte 0
n3: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : sol
.word 392, 25, 0
.byte 0
n4: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : do aigu qui monte de 20 Hz par cs
.word 523, 50, 20
.byte 0
t0: .text "En file : ", 0
t1: .text " notes", 13, 0
t2: .text "Fini", 13, 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0En file : 4 notes
Fini8,5 Play Sound Official
Queues one of the 24 sound effects of the firmware (0 to 23) on a channel.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | channel (0 to 3) |
P1 | uint8 | effect: 0 positive, 1 negative, 2 error, 3 confirm, 4 reject, 5 sweep, 6 coin, 7 las70, 8 powerup, 9 victory, 10 defeat, 11 fanfare, 12-14 alarm, 15-17 ringtone, 18 danger, 19-21 explosions, 22-23 lasers |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 missing channel, 2 missing effect |
The firmware documentation, llvm-mos and Mad Pascal say the effect plays "immediately, clearing the queue". The code, official as well as Trinity, does not clear the queue: it adds the notes of the effect behind those waiting. Here, a one-second note then effect 6 (three notes) make 4 queued notes; for an immediate effect, call 8,2 first. That is what sfx does in NeoBASIC (8,2 then 8,5), hence only 3 notes.
NeoBASIC keywords that make this call: sfx
Firmware documentation text
Play the sound effect specified in Parameter:1 on the channel specified in Parameter:0 immediately, clearing the channel queue.
C (llvm-mos)
/* Jouer un effet sonore derrière une note déjà en file (groupe 8, fonction 5). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_sound_queue(0, 440, 100, 0, 0); /* 8,4 : une note d'une seconde */
neo_sound_play_effect(0, 6); /* Play Sound : effet 6, « coin », trois notes */
printf("Notes en file : %u\n", neo_sound_status(0));
while (neo_sound_status(0) != 0) /* 8,6 : attendre la fin */
;
return 0;
}Notes en file : 4Mad Pascal
// Jouer un effet sonore derrière une note déjà en file (groupe 8, fonction 5).
program playsound;
uses crt, neo6502;
begin
NeoQueueNote(0, 440, 100, 0, 0); // 8,4 : une note d'une seconde
NeoSoundFx(0, 6); // Play Sound : effet 6, « coin », trois notes
writeln('Notes en file : ', NeoGetQueueLen(0));
repeat until NeoGetQueueLen(0) = 0; // 8,6 : attendre la fin
end.Notes en file : 4NeoBASIC
10 ' Jouer un effet sonore derriere une note deja en file (groupe 8, fonction 5)
20 sound 0,440,100 : ' une note d'une seconde
30 sfx 0,6 : ' Play Sound : effet 6, coin, trois notes (sfx vide d'abord le canal)
40 print "Notes en file : ";notes(0)
50 repeat : until notes(0) = 0 : ' 8,6 : attendre la finNotes en file : 365C02 assembly (64tass)
; Jouer un effet sonore derrière une note déjà en file (groupe 8, 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: lda #<n1
ldx #>n1
jsr envoi
lda #<fx
ldx #>fx
jsr envoi
lda #<t0
ldx #>t0
jsr ecrire
lda #0
jsr statut
jsr decimal
lda #13
jsr WriteCharacter
jmp attendre ; 8,6 : attendre la fin
n1: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : une note d'une seconde
.word 440, 100, 0
.byte 0
fx: .byte 8, 5, 2, 0, 6 ; 8,5 : Play Sound, canal 0, effet 6 (« coin », trois notes)
t0: .text "Notes en file : ", 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Notes en file : 48,6 Sound Status Official
Gives the number of notes left on a channel, including the one playing; 0 once it is silent.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | channel (0 to 3) |
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | notes left |
erreur | uint8 | 1 if the channel does not exist |
The example prints the count at each change, until silence. Waiting for 0 is the way to know a sound has ended. In NeoBASIC, notes(n) makes this call.
NeoBASIC keywords that make this call: notes()
Firmware documentation text
Deposit in Parameter:0 the number of notes outstanding before silence in the queue of the channel specified in Parameter:0, including the current playing sound, if any.
C (llvm-mos)
/* Suivre la file d'un canal pendant qu'elle se vide (groupe 8, fonction 6). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_sound_reset(); /* 8,1 : files vides */
neo_sound_queue(0, 440, 20, 0, 0); /* canal 0 : la 440 Hz, 20 cs */
neo_sound_queue(0, 660, 20, 0, 0); /* puis mi 660 Hz, 20 cs */
uint8_t avant = 255, n;
printf("Notes :");
do {
n = neo_sound_status(0); /* Sound Status : notes restantes, celle qui joue comprise */
if (n != avant)
printf(" %u", n);
avant = n;
} while (n != 0);
printf("\n");
return 0;
}Notes : 2 1 0Mad Pascal
// Suivre la file d'un canal pendant qu'elle se vide (groupe 8, fonction 6).
program soundstatus;
uses crt, neo6502;
var avant, n: byte;
begin
NeoMute; // 8,1 : files vides
NeoQueueNote(0, 440, 20, 0, 0); // canal 0 : la 440 Hz, 20 cs
NeoQueueNote(0, 660, 20, 0, 0); // puis mi 660 Hz, 20 cs
avant := 255;
write('Notes :');
repeat
n := NeoGetQueueLen(0); // Sound Status : notes restantes, celle qui joue comprise
if n <> avant then write(' ', n);
avant := n;
until n = 0;
writeln;
end.Notes : 2 1 0NeoBASIC
10 ' Suivre la file d'un canal pendant qu'elle se vide (groupe 8, fonction 6)
20 sound clear : ' 8,1 : files vides
30 sound 0,440,20 : sound 0,660,20 : ' la 440 Hz puis mi 660 Hz, 20 cs chacun
40 avant = 255 : print "Notes :";
50 repeat
60 n = notes(0) : ' Sound Status : notes restantes, celle qui joue comprise
70 if n <> avant then print " ";n;
80 avant = n
90 until n = 0
100 printNotes : 2 1 065C02 assembly (64tass)
; Suivre la file d'un canal pendant qu'elle se vide (groupe 8, fonction 6).
.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: lda #<raz
ldx #>raz
jsr envoi
lda #<n1
ldx #>n1
jsr envoi
lda #<n2
ldx #>n2
jsr envoi
lda #<t0
ldx #>t0
jsr ecrire
lda #255
sta avant
_boucle:
lda #0
jsr statut ; Sound Status : notes restantes, celle qui joue comprise
cmp avant
beq _meme
sta avant
lda #' '
jsr WriteCharacter
lda avant
jsr decimal
_meme: lda avant
bne _boucle
lda #13
jsr WriteCharacter
rts
raz: .byte 8, 1, 0 ; 8,1 : Reset Sound
n1: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : la 440 Hz, 20 cs
.word 440, 20, 0
.byte 0
n2: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : puis mi 660 Hz
.word 660, 20, 0
.byte 0
t0: .text "Notes :", 0
avant: .byte 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Notes : 2 1 08,7 Queue Sound Extended Official
Queues a note as 8,4 does, choosing the waveform and the volume.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | channel (0 to 3) |
P1-P2 | uint16 | frequency in Hz (0: silence) |
P3-P4 | uint16 | duration in hundredths of a second |
P5-P6 | int16 | slide in Hz per hundredth |
P7 | uint8 | waveform: 0 square, 1 noise |
P8 | uint8 | volume, 0 to 100 |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the channel does not exist |
The example queues a noise at half volume and reads its volume back with 8,10. In NeoBASIC, sound c,f,d[,s] (square wave) and noise c,f,d[,s] (noise) make this call, always at volume 100; the NeoBASIC example therefore writes the mailbox itself.
NeoBASIC keywords that make this call: sound
Firmware documentation text
Queue a sound. Refer to Section \#\ref{sound} "Sound" for details. This is an extension of call 4 to support different waveform types and volumes. The source parameter is no longer used.
The parameters are : Channel, Frequency Low, Frequency High, Duration Low, Duration High, Slide Low, Slide High, Sound Type and Sound Volume. All these are 16 bit parameters except the sound type and volume, and the channel number.
C (llvm-mos)
/* Mettre en file un bruit à mi-volume (groupe 8, fonction 7). */
#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)
;
}
/* 8,7 : note en file sur un canal, forme d'onde (0 carré, 1 bruit) et volume (0-100). */
static void note(uint8_t canal, uint16_t freq, uint16_t duree, uint8_t onde, uint8_t volume) {
libre();
ControlPort.params[0] = canal;
ControlPort.params[1] = freq & 0xFF; /* P1-P2 : fréquence en Hz */
ControlPort.params[2] = freq >> 8;
ControlPort.params[3] = duree & 0xFF; /* P3-P4 : durée en centièmes de seconde */
ControlPort.params[4] = duree >> 8;
ControlPort.params[5] = 0; /* P5-P6 : pas de glissement */
ControlPort.params[6] = 0;
ControlPort.params[7] = onde;
ControlPort.params[8] = volume;
api(8, 7); /* Queue Sound Extended */
}
static uint8_t volume(uint8_t canal) { /* 8,10 : volume courant ; 255 si erreur */
libre();
ControlPort.params[0] = canal;
api(8, 10);
return ControlPort.error ? 255 : ControlPort.params[1];
}
int main(void) {
note(0, 2000, 50, 1, 50); /* bruit, 50 cs, volume 50 */
printf("Bruit en file, volume lu : %u\n", volume(0));
while (neo_sound_status(0) != 0) /* 8,6 : attendre la fin */
;
return 0;
}Bruit en file, volume lu : 50Mad Pascal
// Mettre en file un bruit à mi-volume (groupe 8, fonction 7).
program queueextended;
uses crt, neo6502;
function volume(canal: byte): byte; // 8,10 : volume courant ; 255 si erreur
begin
NeoMessage.params[0] := canal;
NeoSendMessage(8, 10);
NeoWaitMessage;
if NeoMessage.error <> 0 then result := 255
else result := NeoMessage.params[1];
end;
begin
NeoQueueNoteX(0, 2000, 50, 0, 1, 50); // Queue Sound Extended : bruit, 50 cs, volume 50
writeln('Bruit en file, volume lu : ', volume(0));
repeat until NeoGetQueueLen(0) = 0; // 8,6 : attendre la fin
end.Bruit en file, volume lu : 50NeoBASIC
10 ' Mettre en file un bruit a mi-volume (groupe 8, fonction 7)
20 c = 0 : f = 2000 : t = 50 : o = 1 : v = 50 : gosub 1000 : ' Queue Sound Extended : bruit, 50 cs, volume 50
30 gosub 1100 : print "Bruit en file, volume lu : ";v
40 repeat : until notes(0) = 0 : ' 8,6 : attendre la fin
50 end
1000 ' 8,7 : note sur le canal c, frequence f, duree t (cs), onde o (0 carre, 1 bruit), volume v
1010 while peek($ff00) : wend
1020 poke $ff04, c : doke $ff05, f : doke $ff07, t : doke $ff09, 0 : poke $ff0b, o : poke $ff0c, v
1030 poke $ff01, 7 : poke $ff00, 8
1040 while peek($ff00) : wend
1050 return
1100 ' 8,10 : volume du canal c dans v, erreur dans e
1110 while peek($ff00) : wend
1120 poke $ff04, c : poke $ff01, 10 : poke $ff00, 8
1130 while peek($ff00) : wend
1140 e = peek($ff02) : v = peek($ff05)
1150 returnBruit en file, volume lu : 5065C02 assembly (64tass)
; Mettre en file un bruit à mi-volume (groupe 8, fonction 7).
.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: lda #<n1
ldx #>n1
jsr envoi
lda #<t0
ldx #>t0
jsr ecrire
lda #0
jsr volume
jsr decimal
lda #13
jsr WriteCharacter
jmp attendre ; 8,6 : attendre la fin
; Volume courant du canal A (8,10), rendu dans A ; C = 1 si erreur.
volume: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #10 ; 8,10 : Get Channel Volume
jsr appel
lda ControlPort+2
cmp #1 ; C = 1 si erreur
lda ControlPort+5
rts
n1: .byte 8, 7, 9, 0 ; 8,7 : canal 0 : bruit (onde 1), 50 cs, volume 50
.word 2000, 50, 0
.byte 1, 50
t0: .text "Bruit en file, volume lu : ", 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Bruit en file, volume lu : 508,8 Get Channel Count Official
Gives the number of sound channels: 4.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | number of channels |
The sample stream (8,11) comes on top without being counted. NeoBASIC has no keyword for this call.
Firmware documentation text
This returns the number of channels in Parameter #0
C (llvm-mos)
/* Demander le nombre de canaux sonores (groupe 8, fonction 8). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
printf("Canaux : %u\n", neo_sound_channel_count()); /* Get Channel Count */
return 0;
}Canaux : 4Mad Pascal
// Demander le nombre de canaux sonores (groupe 8, fonction 8).
program channelcount;
uses crt, neo6502;
begin
writeln('Canaux : ', NeoGetChannels); // Get Channel Count
end.Canaux : 4NeoBASIC
10 ' Demander le nombre de canaux sonores (groupe 8, fonction 8)
20 while peek($ff00) : wend : poke $ff01, 8 : poke $ff00, 8 : ' Get Channel Count
30 while peek($ff00) : wend : n = peek($ff04)
40 print "Canaux : ";nCanaux : 465C02 assembly (64tass)
; Demander le nombre de canaux sonores (groupe 8, fonction 8).
.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: lda #<nb
ldx #>nb
jsr envoi
lda ControlPort+4 ; P0 : nombre de canaux
sta n
lda #<t0
ldx #>t0
jsr ecrire
lda n
jsr decimal
lda #13
jsr WriteCharacter
rts
nb: .byte 8, 8, 0 ; 8,8 : Get Channel Count
t0: .text "Canaux : ", 0
n: .byte 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Canaux : 48,9 Set Channel Volume Trinity
Changes the volume of the note playing on a channel, at once or as a fade, without restarting the wave.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | channel (0 to 3) |
P1 | uint8 | target volume, 0 to 100 |
P2-P3 | uint16 | fade time in hundredths (0: at once) |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the channel does not exist or plays nothing |
Only the current note changes: the notes still queued keep their volume, and the next note cancels an unfinished fade. The fade moves by a whole step every 50th of a second and may end a little later than asked (63 to 0 in 50 cs: 2 per step, 32 steps). The example causes an error on channel 1, which is silent.
Firmware documentation text
Sets the volume of the note currently playing on the channel in Parameter:0 to Parameter:1 (0-100), reached in Parameters:2,3 hundredths of a second (0 = at once) by a linear ramp (Trinity T-22, F-12 of the fork) : fade in, fade out or release without restarting the wave. The notes still queued keep the volume given when they were queued, and a new note cancels the ramp. Sets the error flag if the channel does not exist or is silent.
C (llvm-mos)
/* Baisser le volume d'une note qui joue, d'un coup puis en fondu (groupe 8, fonction 9). */
#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)
;
}
/* 8,7 : note en file sur un canal, forme d'onde (0 carré, 1 bruit) et volume (0-100). */
static void note(uint8_t canal, uint16_t freq, uint16_t duree, uint8_t onde, uint8_t volume) {
libre();
ControlPort.params[0] = canal;
ControlPort.params[1] = freq & 0xFF; /* P1-P2 : fréquence en Hz */
ControlPort.params[2] = freq >> 8;
ControlPort.params[3] = duree & 0xFF; /* P3-P4 : durée en centièmes de seconde */
ControlPort.params[4] = duree >> 8;
ControlPort.params[5] = 0; /* P5-P6 : pas de glissement */
ControlPort.params[6] = 0;
ControlPort.params[7] = onde;
ControlPort.params[8] = volume;
api(8, 7); /* Queue Sound Extended */
}
static uint8_t volume(uint8_t canal) { /* 8,10 : volume courant ; 255 si erreur */
libre();
ControlPort.params[0] = canal;
api(8, 10);
return ControlPort.error ? 255 : ControlPort.params[1];
}
static uint8_t regler(uint8_t canal, uint8_t vol, uint16_t cs) { /* 8,9 : rend l'indicateur d'erreur */
libre();
ControlPort.params[0] = canal;
ControlPort.params[1] = vol; /* P1 : volume visé, 0-100 */
ControlPort.params[2] = cs & 0xFF; /* P2-P3 : durée du fondu en cs (0 : tout de suite) */
ControlPort.params[3] = cs >> 8;
api(8, 9); /* Set Channel Volume */
return ControlPort.error;
}
int main(void) {
note(0, 440, 200, 0, 100); /* la 440 Hz, 2 s, volume 100 */
uint8_t v0 = volume(0);
regler(0, 50, 0); /* 50, tout de suite */
uint8_t v1 = volume(0);
regler(0, 0, 50); /* 0 en 50 cs */
while (volume(0) != 0) /* le fondu avance à 50 Hz */
;
printf("Volume : %u, puis %u, puis 0 en fondu\n", v0, v1);
printf("Canal 1 muet : erreur %u\n", regler(1, 50, 0));
neo_sound_reset(); /* 8,1 : fin de la note */
return 0;
}Volume : 100, puis 50, puis 0 en fondu
Canal 1 muet : erreur 1Mad Pascal
// Baisser le volume d'une note qui joue, d'un coup puis en fondu (groupe 8, fonction 9).
program setvolume;
uses crt, neo6502;
var v0, v1, e: byte;
function volume(canal: byte): byte; // 8,10 : volume courant ; 255 si erreur
begin
NeoMessage.params[0] := canal;
NeoSendMessage(8, 10);
NeoWaitMessage;
if NeoMessage.error <> 0 then result := 255
else result := NeoMessage.params[1];
end;
function regler(canal, vol: byte; cs: word): byte; // 8,9 : rend l'indicateur d'erreur
begin
NeoMessage.params[0] := canal;
NeoMessage.params[1] := vol; // P1 : volume visé, 0-100
NeoMessage.params[2] := lo(cs); // P2-P3 : durée du fondu en cs (0 : tout de suite)
NeoMessage.params[3] := hi(cs);
NeoSendMessage(8, 9); // Set Channel Volume
NeoWaitMessage;
result := NeoMessage.error;
end;
begin
NeoQueueNoteX(0, 440, 200, 0, 0, 100); // la 440 Hz, 2 s, volume 100
v0 := volume(0);
regler(0, 50, 0); // 50, tout de suite
v1 := volume(0);
regler(0, 0, 50); // 0 en 50 cs
repeat until volume(0) = 0; // le fondu avance à 50 Hz
writeln('Volume : ', v0, ', puis ', v1, ', puis 0 en fondu');
e := regler(1, 50, 0);
writeln('Canal 1 muet : erreur ', e);
NeoMute; // 8,1 : fin de la note
end.Volume : 100, puis 50, puis 0 en fondu
Canal 1 muet : erreur 1NeoBASIC
10 ' Baisser le volume d'une note qui joue, d'un coup puis en fondu (groupe 8, fonction 9)
20 c = 0 : f = 440 : t = 200 : o = 0 : v = 100 : gosub 1000 : ' la 440 Hz, 2 s, volume 100
30 gosub 1100 : v0 = v
40 v = 50 : d = 0 : gosub 1200 : ' 50, tout de suite
50 gosub 1100 : v1 = v
60 v = 0 : d = 50 : gosub 1200 : ' 0 en 50 cs
70 repeat : gosub 1100 : until v = 0 : ' le fondu avance a 50 Hz
80 print "Volume : ";v0;", puis ";v1;", puis 0 en fondu"
90 c = 1 : v = 50 : d = 0 : gosub 1200 : print "Canal 1 muet : erreur ";e
100 sound clear : ' 8,1 : fin de la note
110 end
1000 ' 8,7 : note sur le canal c, frequence f, duree t (cs), onde o (0 carre, 1 bruit), volume v
1010 while peek($ff00) : wend
1020 poke $ff04, c : doke $ff05, f : doke $ff07, t : doke $ff09, 0 : poke $ff0b, o : poke $ff0c, v
1030 poke $ff01, 7 : poke $ff00, 8
1040 while peek($ff00) : wend
1050 return
1100 ' 8,10 : volume du canal c dans v, erreur dans e
1110 while peek($ff00) : wend
1120 poke $ff04, c : poke $ff01, 10 : poke $ff00, 8
1130 while peek($ff00) : wend
1140 e = peek($ff02) : v = peek($ff05)
1150 return
1200 ' 8,9 : volume v (0-100) du canal c, atteint en d cs (0 : tout de suite) ; erreur dans e
1210 while peek($ff00) : wend
1220 poke $ff04, c : poke $ff05, v : doke $ff06, d
1230 poke $ff01, 9 : poke $ff00, 8
1240 while peek($ff00) : wend
1250 e = peek($ff02)
1260 returnVolume : 100, puis 50, puis 0 en fondu
Canal 1 muet : erreur 165C02 assembly (64tass)
; Baisser le volume d'une note qui joue, d'un coup puis en fondu (groupe 8, fonction 9).
.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: lda #<n1
ldx #>n1
jsr envoi
lda #0
jsr volume
sta v0
lda #<v50
ldx #>v50
jsr envoi
lda #0
jsr volume
sta v1
lda #<fondu
ldx #>fondu
jsr envoi
_fondu: lda #0 ; le fondu avance à 50 Hz
jsr volume
bne _fondu
lda #<t0
ldx #>t0
jsr ecrire
lda v0
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
lda v1
jsr decimal
lda #<t2
ldx #>t2
jsr ecrire
lda #<muet
ldx #>muet
jsr envoi
lda ControlPort+2 ; erreur : le canal 1 ne joue rien
sta e
lda #<t3
ldx #>t3
jsr ecrire
lda e
jsr decimal
lda #13
jsr WriteCharacter
lda #<raz
ldx #>raz
jsr envoi
rts
; Volume courant du canal A (8,10), rendu dans A ; C = 1 si erreur.
volume: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #10 ; 8,10 : Get Channel Volume
jsr appel
lda ControlPort+2
cmp #1 ; C = 1 si erreur
lda ControlPort+5
rts
n1: .byte 8, 7, 9, 0 ; 8,7 : canal 0 : la 440 Hz, 2 s, volume 100
.word 440, 200, 0
.byte 0, 100
v50: .byte 8, 9, 4, 0, 50 ; 8,9 : canal 0, volume 50...
.word 0 ; ... tout de suite
fondu: .byte 8, 9, 4, 0, 0 ; 8,9 : canal 0, volume 0...
.word 50 ; ... en 50 cs
muet: .byte 8, 9, 4, 1, 50 ; 8,9 : canal 1, qui ne joue rien
.word 0
raz: .byte 8, 1, 0 ; 8,1 : Reset Sound
t0: .text "Volume : ", 0
t1: .text ", puis ", 0
t2: .text ", puis 0 en fondu", 13, 0
t3: .text "Canal 1 muet : erreur ", 0
v0: .byte 0
v1: .byte 0
e: .byte 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Volume : 100, puis 50, puis 0 en fondu
Canal 1 muet : erreur 18,10 Get Channel Volume Trinity
Gives the current volume of a channel, 0 when it is silent.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | channel (0 to 3) |
| Bytes | Type | Meaning |
|---|---|---|
P1 | uint8 | volume, 0 to 100 |
erreur | uint8 | 1 if the channel does not exist |
The result is in P1, not P0. The volume, stored as 0 to 127, is converted back to 0-100: the note asked at 25 reads back as 24. During a fade (8,9), the value changes every 50th of a second.
Firmware documentation text
Returns in Parameter:1 the current volume (0-100) of the channel in Parameter:0, 0 when silent (Trinity T-22, F-12 of the fork). Sets the error flag if the channel does not exist.
C (llvm-mos)
/* Lire le volume de trois canaux (groupe 8, fonction 10). */
#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)
;
}
/* 8,7 : note en file sur un canal, forme d'onde (0 carré, 1 bruit) et volume (0-100). */
static void note(uint8_t canal, uint16_t freq, uint16_t duree, uint8_t onde, uint8_t volume) {
libre();
ControlPort.params[0] = canal;
ControlPort.params[1] = freq & 0xFF; /* P1-P2 : fréquence en Hz */
ControlPort.params[2] = freq >> 8;
ControlPort.params[3] = duree & 0xFF; /* P3-P4 : durée en centièmes de seconde */
ControlPort.params[4] = duree >> 8;
ControlPort.params[5] = 0; /* P5-P6 : pas de glissement */
ControlPort.params[6] = 0;
ControlPort.params[7] = onde;
ControlPort.params[8] = volume;
api(8, 7); /* Queue Sound Extended */
}
static uint8_t volume(uint8_t canal) { /* 8,10 : volume courant ; 255 si erreur */
libre();
ControlPort.params[0] = canal;
api(8, 10);
return ControlPort.error ? 255 : ControlPort.params[1];
}
int main(void) {
note(0, 440, 100, 0, 25); /* canal 0 : une note au volume 25 */
printf("Canal 0 : %u\n", volume(0)); /* Get Channel Volume */
printf("Canal 1 : %u\n", volume(1)); /* rien ne joue : 0 */
printf("Canal 4 : %s\n", volume(4) == 255 ? "erreur" : "?"); /* 4 canaux, 0 à 3 */
neo_sound_reset(); /* 8,1 : fin de la note */
return 0;
}Canal 0 : 24
Canal 1 : 0
Canal 4 : erreurMad Pascal
// Lire le volume de trois canaux (groupe 8, fonction 10).
program getvolume;
uses crt, neo6502;
function volume(canal: byte): byte; // 8,10 : volume courant ; 255 si erreur
begin
NeoMessage.params[0] := canal;
NeoSendMessage(8, 10);
NeoWaitMessage;
if NeoMessage.error <> 0 then result := 255
else result := NeoMessage.params[1];
end;
begin
NeoQueueNoteX(0, 440, 100, 0, 0, 25); // canal 0 : une note au volume 25
writeln('Canal 0 : ', volume(0)); // Get Channel Volume
writeln('Canal 1 : ', volume(1)); // rien ne joue : 0
if volume(4) = 255 then writeln('Canal 4 : erreur'); // 4 canaux, 0 à 3
NeoMute; // 8,1 : fin de la note
end.Canal 0 : 24
Canal 1 : 0
Canal 4 : erreurNeoBASIC
10 ' Lire le volume de trois canaux (groupe 8, fonction 10)
20 c = 0 : f = 440 : t = 100 : o = 0 : v = 25 : gosub 1000 : ' canal 0 : une note au volume 25
30 c = 0 : gosub 1100 : print "Canal 0 : ";v : ' Get Channel Volume
40 c = 1 : gosub 1100 : print "Canal 1 : ";v : ' rien ne joue : 0
50 c = 4 : gosub 1100 : if e then print "Canal 4 : erreur" : ' 4 canaux, 0 a 3
60 sound clear : ' 8,1 : fin de la note
70 end
1000 ' 8,7 : note sur le canal c, frequence f, duree t (cs), onde o (0 carre, 1 bruit), volume v
1010 while peek($ff00) : wend
1020 poke $ff04, c : doke $ff05, f : doke $ff07, t : doke $ff09, 0 : poke $ff0b, o : poke $ff0c, v
1030 poke $ff01, 7 : poke $ff00, 8
1040 while peek($ff00) : wend
1050 return
1100 ' 8,10 : volume du canal c dans v, erreur dans e
1110 while peek($ff00) : wend
1120 poke $ff04, c : poke $ff01, 10 : poke $ff00, 8
1130 while peek($ff00) : wend
1140 e = peek($ff02) : v = peek($ff05)
1150 returnCanal 0 : 24
Canal 1 : 0
Canal 4 : erreur65C02 assembly (64tass)
; Lire le volume de trois canaux (groupe 8, fonction 10).
.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: lda #<n1
ldx #>n1
jsr envoi
lda #<t0
ldx #>t0
jsr ecrire
lda #0
jsr volume ; Get Channel Volume
jsr decimal
lda #13
jsr WriteCharacter
lda #<t1
ldx #>t1
jsr ecrire
lda #1
jsr volume ; rien ne joue : 0
jsr decimal
lda #13
jsr WriteCharacter
lda #4 ; 4 canaux, 0 à 3
jsr volume
bcc _fin
lda #<t2
ldx #>t2
jsr ecrire
_fin: lda #<raz
ldx #>raz
jsr envoi
rts
; Volume courant du canal A (8,10), rendu dans A ; C = 1 si erreur.
volume: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #10 ; 8,10 : Get Channel Volume
jsr appel
lda ControlPort+2
cmp #1 ; C = 1 si erreur
lda ControlPort+5
rts
n1: .byte 8, 7, 9, 0 ; 8,7 : canal 0 : une note au volume 25
.word 440, 100, 0
.byte 0, 25
raz: .byte 8, 1, 0 ; 8,1 : Reset Sound
t0: .text "Canal 0 : ", 0
t1: .text "Canal 1 : ", 0
t2: .text "Canal 4 : erreur", 13, 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Canal 0 : 24
Canal 1 : 0
Canal 4 : erreur8,11 Stream Start Trinity
Starts a stream of 8 bit PCM samples read from a double buffer in 6502 memory.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | uint16 | buffer address (two halves one after the other) |
P2-P3 | uint16 | size of one half in bytes |
P4-P5 | uint16 | samples per second (11025, 22050...) |
P6 | uint8 | volume, 0 to 100 |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if a size or the rate is 0, the volume is above 100, or the buffer does not end below $FF00 |
The samples are unsigned, $80 being silence. Both halves must be filled before the call: they count as full. The firmware plays half 0, then 1, then 0..., and hands each one back once played (8,13); the program refills it and marks it full (8,14). The example plays a square wave of 256 samples per half at 11025 Hz, waits until half 0 has been played (state 129 = $81), then shows the error of a buffer that would overlap $FF00. NeoBASIC has no keyword for the stream.
Firmware documentation text
Starts playing a stream of 8 bit unsigned PCM samples ($80 = silence) mixed with the four channels (Trinity T-79). The buffer is in 6502 RAM at Parameters:0,1 and made of two halves of Parameters:2,3 bytes each, one after the other ; Parameters:4,5 is the rate of the samples in Hz (11025 or 22050 for instance, the firmware follows it whatever the output rate of the board) and Parameter:6 the volume (0-100, as Function 8,9). Fill both halves before the call : they are taken as full. The firmware plays half 0 then half 1 then half 0 again..., handing each half back when it has played it (Function 8,13) ; the program refills it and marks it full (Function 8,14). A half not refilled in time is not replayed : the stream is silent until it is marked full, and the underrun is counted. The stream counts as one more channel for the volume scaling. Sets the error flag if a size or the rate is 0, the volume above 100, or the buffer does not end below $FF00. Function 8,1 stops the stream as well.
C (llvm-mos)
/* Lancer un flux d'échantillons (groupe 8, fonction 11). */
#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 uint8_t tampon[2 * 256]; /* deux moitiés de 256 échantillons */
static uint16_t manques; /* sous-alimentations, lues par etat() */
static void creneau(uint8_t moitie) { /* écrit un créneau : 16 échantillons hauts, 16 bas */
uint8_t *p = tampon + moitie * 256;
for (uint16_t i = 0; i < 256; i++)
p[i] = (i & 16) ? 0xC0 : 0x40; /* $80 = silence */
}
static uint8_t lancer(uint16_t adresse) { /* 8,11 : 256 octets par moitié, 11025 Hz, volume 100 */
libre();
ControlPort.params[0] = adresse & 0xFF; /* P0-P1 : adresse du tampon */
ControlPort.params[1] = adresse >> 8;
ControlPort.params[2] = 0; /* P2-P3 : taille d'une moitié */
ControlPort.params[3] = 1;
ControlPort.params[4] = 11025 & 0xFF; /* P4-P5 : échantillons par seconde */
ControlPort.params[5] = 11025 >> 8;
ControlPort.params[6] = 100; /* P6 : volume */
api(8, 11); /* Stream Start */
return ControlPort.error;
}
static uint8_t etat(void) { /* 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir */
libre();
api(8, 13);
manques = ControlPort.params[1] | ControlPort.params[2] << 8;
return ControlPort.params[0];
}
static void arreter(void) { /* 8,12 */
libre();
api(8, 12);
}
int main(void) {
creneau(0); /* remplir les deux moitiés avant de lancer */
creneau(1);
lancer((uint16_t)tampon); /* Stream Start */
printf("Lance : %u\n", etat());
while (etat() != 129) /* 128 + 1 : la moitié 0 a été jouée */
;
printf("Moitie 0 jouee : %u\n", etat());
arreter();
printf("Tampon trop haut : erreur %u\n", lancer(0xFE80)); /* $FE80 + 512 dépasse $FF00 */
return 0;
}Lance : 128
Moitie 0 jouee : 129
Tampon trop haut : erreur 1Mad Pascal
// Lancer un flux d'échantillons (groupe 8, fonction 11).
program streamstart;
uses crt, neo6502;
var tampon: array[0..511] of byte; // deux moitiés de 256 échantillons
manques: word; // sous-alimentations, lues par etat
procedure creneau(moitie: byte); // écrit un créneau : 16 échantillons hauts, 16 bas
var i: word;
begin
for i := 0 to 255 do
if i and 16 <> 0 then tampon[moitie * 256 + i] := $C0
else tampon[moitie * 256 + i] := $40; // $80 = silence
end;
function lancer(adresse: word): byte; // 8,11 : 256 octets par moitié, 11025 Hz, volume 100
begin
NeoMessage.params[0] := lo(adresse); // P0-P1 : adresse du tampon
NeoMessage.params[1] := hi(adresse);
NeoMessage.params[2] := 0; // P2-P3 : taille d'une moitié
NeoMessage.params[3] := 1;
NeoMessage.params[4] := lo(11025); // P4-P5 : échantillons par seconde
NeoMessage.params[5] := hi(11025);
NeoMessage.params[6] := 100; // P6 : volume
NeoSendMessage(8, 11); // Stream Start
NeoWaitMessage;
result := NeoMessage.error;
end;
function etat: byte; // 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir
begin
NeoSendMessage(8, 13);
NeoWaitMessage;
result := NeoMessage.params[0];
manques := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
procedure arreter; // 8,12
begin
NeoSendMessage(8, 12);
NeoWaitMessage;
end;
var e: byte;
begin
creneau(0); // remplir les deux moitiés avant de lancer
creneau(1);
lancer(word(@tampon)); // Stream Start
writeln('Lance : ', etat);
repeat until etat = 129; // 128 + 1 : la moitié 0 a été jouée
writeln('Moitie 0 jouee : ', etat);
arreter;
e := lancer($FE80); // $FE80 + 512 dépasse $FF00
writeln('Tampon trop haut : erreur ', e);
end.Lance : 128
Moitie 0 jouee : 129
Tampon trop haut : erreur 1NeoBASIC
10 ' Lancer un flux d'echantillons (groupe 8, fonction 11)
20 a = alloc(512) : b = a : gosub 1000 : b = a + 256 : gosub 1000 : ' remplir les deux moities avant
30 gosub 1100 : ' Stream Start
40 gosub 1200 : print "Lance : ";s
50 repeat : gosub 1200 : until s = 129 : ' 128 + 1 : la moitie 0 a ete jouee
60 print "Moitie 0 jouee : ";s
70 gosub 1300
80 a = $fe80 : gosub 1100 : print "Tampon trop haut : erreur ";e : ' $fe80 + 512 depasse $ff00
90 end
1000 ' creneau dans la moitie qui commence en b : 16 echantillons hauts, 16 bas ($80 = silence)
1010 for i = 0 to 255 : x = $40 : if i & 16 then x = $c0
1020 poke b + i, x : next i
1030 return
1100 ' 8,11 : tampon en a, 256 octets par moitie, 11025 Hz, volume 100 ; erreur dans e
1110 while peek($ff00) : wend
1120 doke $ff04, a : doke $ff06, 256 : doke $ff08, 11025 : poke $ff0a, 100
1130 poke $ff01, 11 : poke $ff00, 8
1140 while peek($ff00) : wend
1150 e = peek($ff02)
1160 return
1200 ' 8,13 : etat dans s (bit 7 joue, bits 0 et 1 moities a remplir), sous-alimentations dans u
1210 while peek($ff00) : wend
1220 poke $ff01, 13 : poke $ff00, 8
1230 while peek($ff00) : wend
1240 s = peek($ff04) : u = deek($ff05)
1250 return
1300 ' 8,12 : arret du flux
1310 while peek($ff00) : wend
1320 poke $ff01, 12 : poke $ff00, 8
1330 while peek($ff00) : wend
1340 returnLance : 128
Moitie 0 jouee : 129
Tampon trop haut : erreur 165C02 assembly (64tass)
; Lancer un flux d'échantillons (groupe 8, fonction 11).
.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: lda #0 ; remplir les deux moitiés avant de lancer
jsr creneau
lda #1
jsr creneau
lda #<lance
ldx #>lance
jsr envoi
jsr etat
sta e0
lda #<t0
ldx #>t0
jsr ecrire
lda e0
jsr decimal
lda #13
jsr WriteCharacter
_joue: jsr etat ; 128 + 1 : la moitié 0 a été jouée
cmp #129
bne _joue
lda #<t1
ldx #>t1
jsr ecrire
lda etat_
jsr decimal
lda #13
jsr WriteCharacter
lda #<arret
ldx #>arret
jsr envoi
lda #<haut
ldx #>haut
jsr envoi
lda ControlPort+2
sta e
lda #<t2
ldx #>t2
jsr ecrire
lda e
jsr decimal
lda #13
jsr WriteCharacter
rts
; Écrit un créneau dans la moitié A (0 ou 1) : 16 échantillons hauts, 16 bas ($80 = silence).
creneau:
clc
adc #>tampon
sta ptr+1
lda #<tampon
sta ptr
ldy #0
_r: tya
and #16
beq _bas
lda #$C0
bra _ecr
_bas: lda #$40
_ecr: sta (ptr),y
iny
bne _r
rts
; État du flux (8,13) dans A et etat_ ; sous-alimentations (octet bas) dans manques.
etat: jsr WaitMessage
lda #8
ldx #13 ; 8,13 : Stream Status
jsr appel
lda ControlPort+5 ; P1-P2 : sous-alimentations
sta manques
lda ControlPort+4 ; P0 : bit 7 joue, bits 0 et 1 moitiés à remplir
sta etat_
rts
etat_: .byte 0
manques: .byte 0
lance: .byte 8, 11, 7 ; 8,11 : Stream Start
.word tampon, 256, 11025 ; adresse, taille d'une moitié, échantillons par seconde
.byte 100 ; volume
haut: .byte 8, 11, 7
.word $FE80, 256, 11025 ; $FE80 + 512 dépasse $FF00
.byte 100
arret: .byte 8, 12, 0 ; 8,12 : Stream Stop
t0: .text "Lance : ", 0
t1: .text "Moitie 0 jouee : ", 0
t2: .text "Tampon trop haut : erreur ", 0
e0: .byte 0
e: .byte 0
.align 256
tampon: .fill 512 ; deux moitiés de 256 échantillons
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Lance : 128
Moitie 0 jouee : 129
Tampon trop haut : erreur 18,12 Stream Stop Trinity
Stops the sample stream at once; the buffer may be reused.
Inputs: none.
Outputs: none.
The state (8,13) goes from 128 ($80, the stream plays) to 0. 8,1 stops the stream too.
Firmware documentation text
Stops the stream at once (Trinity T-79). The buffer is no longer read and may be reused.
C (llvm-mos)
/* Arrêter un flux d'échantillons (groupe 8, fonction 12). */
#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 uint8_t tampon[2 * 256]; /* deux moitiés de 256 échantillons */
static uint16_t manques; /* sous-alimentations, lues par etat() */
static void creneau(uint8_t moitie) { /* écrit un créneau : 16 échantillons hauts, 16 bas */
uint8_t *p = tampon + moitie * 256;
for (uint16_t i = 0; i < 256; i++)
p[i] = (i & 16) ? 0xC0 : 0x40; /* $80 = silence */
}
static uint8_t lancer(uint16_t adresse) { /* 8,11 : 256 octets par moitié, 11025 Hz, volume 100 */
libre();
ControlPort.params[0] = adresse & 0xFF; /* P0-P1 : adresse du tampon */
ControlPort.params[1] = adresse >> 8;
ControlPort.params[2] = 0; /* P2-P3 : taille d'une moitié */
ControlPort.params[3] = 1;
ControlPort.params[4] = 11025 & 0xFF; /* P4-P5 : échantillons par seconde */
ControlPort.params[5] = 11025 >> 8;
ControlPort.params[6] = 100; /* P6 : volume */
api(8, 11); /* Stream Start */
return ControlPort.error;
}
static uint8_t etat(void) { /* 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir */
libre();
api(8, 13);
manques = ControlPort.params[1] | ControlPort.params[2] << 8;
return ControlPort.params[0];
}
static void arreter(void) { /* 8,12 */
libre();
api(8, 12);
}
int main(void) {
creneau(0);
creneau(1);
lancer((uint16_t)tampon); /* 8,11 */
uint8_t avant = etat();
arreter(); /* Stream Stop : le tampon est libre */
printf("Avant : %u, apres : %u\n", avant, etat());
return 0;
}Avant : 128, apres : 0Mad Pascal
// Arrêter un flux d'échantillons (groupe 8, fonction 12).
program streamstop;
uses crt, neo6502;
var tampon: array[0..511] of byte; // deux moitiés de 256 échantillons
manques: word; // sous-alimentations, lues par etat
procedure creneau(moitie: byte); // écrit un créneau : 16 échantillons hauts, 16 bas
var i: word;
begin
for i := 0 to 255 do
if i and 16 <> 0 then tampon[moitie * 256 + i] := $C0
else tampon[moitie * 256 + i] := $40; // $80 = silence
end;
function lancer(adresse: word): byte; // 8,11 : 256 octets par moitié, 11025 Hz, volume 100
begin
NeoMessage.params[0] := lo(adresse); // P0-P1 : adresse du tampon
NeoMessage.params[1] := hi(adresse);
NeoMessage.params[2] := 0; // P2-P3 : taille d'une moitié
NeoMessage.params[3] := 1;
NeoMessage.params[4] := lo(11025); // P4-P5 : échantillons par seconde
NeoMessage.params[5] := hi(11025);
NeoMessage.params[6] := 100; // P6 : volume
NeoSendMessage(8, 11); // Stream Start
NeoWaitMessage;
result := NeoMessage.error;
end;
function etat: byte; // 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir
begin
NeoSendMessage(8, 13);
NeoWaitMessage;
result := NeoMessage.params[0];
manques := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
procedure arreter; // 8,12
begin
NeoSendMessage(8, 12);
NeoWaitMessage;
end;
var avant: byte;
begin
creneau(0);
creneau(1);
lancer(word(@tampon)); // 8,11
avant := etat;
arreter; // Stream Stop : le tampon est libre
writeln('Avant : ', avant, ', apres : ', etat);
end.Avant : 128, apres : 0NeoBASIC
10 ' Arreter un flux d'echantillons (groupe 8, fonction 12)
20 a = alloc(512) : b = a : gosub 1000 : b = a + 256 : gosub 1000
30 gosub 1100 : ' 8,11
40 gosub 1200 : avant = s
50 gosub 1300 : ' Stream Stop : le tampon est libre
60 gosub 1200 : print "Avant : ";avant;", apres : ";s
70 end
1000 ' creneau dans la moitie qui commence en b : 16 echantillons hauts, 16 bas ($80 = silence)
1010 for i = 0 to 255 : x = $40 : if i & 16 then x = $c0
1020 poke b + i, x : next i
1030 return
1100 ' 8,11 : tampon en a, 256 octets par moitie, 11025 Hz, volume 100 ; erreur dans e
1110 while peek($ff00) : wend
1120 doke $ff04, a : doke $ff06, 256 : doke $ff08, 11025 : poke $ff0a, 100
1130 poke $ff01, 11 : poke $ff00, 8
1140 while peek($ff00) : wend
1150 e = peek($ff02)
1160 return
1200 ' 8,13 : etat dans s (bit 7 joue, bits 0 et 1 moities a remplir), sous-alimentations dans u
1210 while peek($ff00) : wend
1220 poke $ff01, 13 : poke $ff00, 8
1230 while peek($ff00) : wend
1240 s = peek($ff04) : u = deek($ff05)
1250 return
1300 ' 8,12 : arret du flux
1310 while peek($ff00) : wend
1320 poke $ff01, 12 : poke $ff00, 8
1330 while peek($ff00) : wend
1340 returnAvant : 128, apres : 065C02 assembly (64tass)
; Arrêter un flux d'échantillons (groupe 8, fonction 12).
.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: lda #0 ; remplir les deux moitiés avant de lancer
jsr creneau
lda #1
jsr creneau
lda #<lance
ldx #>lance
jsr envoi
jsr etat
sta avant
lda #<arret
ldx #>arret
jsr envoi
; Stream Stop : le tampon est libre
lda #<t0
ldx #>t0
jsr ecrire
lda avant
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
jsr etat
jsr decimal
lda #13
jsr WriteCharacter
rts
; Écrit un créneau dans la moitié A (0 ou 1) : 16 échantillons hauts, 16 bas ($80 = silence).
creneau:
clc
adc #>tampon
sta ptr+1
lda #<tampon
sta ptr
ldy #0
_r: tya
and #16
beq _bas
lda #$C0
bra _ecr
_bas: lda #$40
_ecr: sta (ptr),y
iny
bne _r
rts
; État du flux (8,13) dans A et etat_ ; sous-alimentations (octet bas) dans manques.
etat: jsr WaitMessage
lda #8
ldx #13 ; 8,13 : Stream Status
jsr appel
lda ControlPort+5 ; P1-P2 : sous-alimentations
sta manques
lda ControlPort+4 ; P0 : bit 7 joue, bits 0 et 1 moitiés à remplir
sta etat_
rts
etat_: .byte 0
manques: .byte 0
lance: .byte 8, 11, 7 ; 8,11 : Stream Start
.word tampon, 256, 11025 ; adresse, taille d'une moitié, échantillons par seconde
.byte 100 ; volume
arret: .byte 8, 12, 0 ; 8,12 : Stream Stop
t0: .text "Avant : ", 0
t1: .text ", apres : ", 0
avant: .byte 0
.align 256
tampon: .fill 512 ; deux moitiés de 256 échantillons
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Avant : 128, apres : 08,13 Stream Status Trinity
Gives the state of the stream: whether it plays, which halves wait to be refilled, and how many times it ran short of samples.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | bit 7: the stream plays; bit 0: half 0 to refill; bit 1: half 1 to refill; 0: stopped |
P1-P2 | uint16 | underruns since the start (65535 at most) |
An underrun is a half reached before it was marked full: the stream then goes silent until it is, without replaying it. The example refills nothing: after both halves, the state is 131 ($83) and the counter 1, then it stays there.
Firmware documentation text
Returns in Parameter:0 the state of the stream (Trinity T-79) : bit 7 set while it plays, bit 0 set when half 0 has been played and waits to be refilled, bit 1 the same for half 1 ; 0 when stopped. Parameters:1,2 count the underruns since the start (a half reached before it was marked full, 65535 at most).
C (llvm-mos)
/* Laisser un flux se tarir et lire son état (groupe 8, fonction 13). */
#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 uint8_t tampon[2 * 256]; /* deux moitiés de 256 échantillons */
static uint16_t manques; /* sous-alimentations, lues par etat() */
static void creneau(uint8_t moitie) { /* écrit un créneau : 16 échantillons hauts, 16 bas */
uint8_t *p = tampon + moitie * 256;
for (uint16_t i = 0; i < 256; i++)
p[i] = (i & 16) ? 0xC0 : 0x40; /* $80 = silence */
}
static uint8_t lancer(uint16_t adresse) { /* 8,11 : 256 octets par moitié, 11025 Hz, volume 100 */
libre();
ControlPort.params[0] = adresse & 0xFF; /* P0-P1 : adresse du tampon */
ControlPort.params[1] = adresse >> 8;
ControlPort.params[2] = 0; /* P2-P3 : taille d'une moitié */
ControlPort.params[3] = 1;
ControlPort.params[4] = 11025 & 0xFF; /* P4-P5 : échantillons par seconde */
ControlPort.params[5] = 11025 >> 8;
ControlPort.params[6] = 100; /* P6 : volume */
api(8, 11); /* Stream Start */
return ControlPort.error;
}
static uint8_t etat(void) { /* 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir */
libre();
api(8, 13);
manques = ControlPort.params[1] | ControlPort.params[2] << 8;
return ControlPort.params[0];
}
static void arreter(void) { /* 8,12 */
libre();
api(8, 12);
}
int main(void) {
creneau(0);
creneau(1);
lancer((uint16_t)tampon); /* 8,11 : les deux moitiés comptent comme pleines */
uint8_t e0 = etat(); /* Stream Status */
while (etat() != 131) /* rien n'est rempli à nouveau : 128 + 1 + 2 */
;
printf("Au depart : %u, tari : %u, sous-alimentations : %u\n", e0, etat(), manques);
arreter();
printf("Arrete : %u\n", etat());
return 0;
}Au depart : 128, tari : 131, sous-alimentations : 1
Arrete : 0Mad Pascal
// Laisser un flux se tarir et lire son état (groupe 8, fonction 13).
program streamstatus;
uses crt, neo6502;
var tampon: array[0..511] of byte; // deux moitiés de 256 échantillons
manques: word; // sous-alimentations, lues par etat
procedure creneau(moitie: byte); // écrit un créneau : 16 échantillons hauts, 16 bas
var i: word;
begin
for i := 0 to 255 do
if i and 16 <> 0 then tampon[moitie * 256 + i] := $C0
else tampon[moitie * 256 + i] := $40; // $80 = silence
end;
function lancer(adresse: word): byte; // 8,11 : 256 octets par moitié, 11025 Hz, volume 100
begin
NeoMessage.params[0] := lo(adresse); // P0-P1 : adresse du tampon
NeoMessage.params[1] := hi(adresse);
NeoMessage.params[2] := 0; // P2-P3 : taille d'une moitié
NeoMessage.params[3] := 1;
NeoMessage.params[4] := lo(11025); // P4-P5 : échantillons par seconde
NeoMessage.params[5] := hi(11025);
NeoMessage.params[6] := 100; // P6 : volume
NeoSendMessage(8, 11); // Stream Start
NeoWaitMessage;
result := NeoMessage.error;
end;
function etat: byte; // 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir
begin
NeoSendMessage(8, 13);
NeoWaitMessage;
result := NeoMessage.params[0];
manques := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
procedure arreter; // 8,12
begin
NeoSendMessage(8, 12);
NeoWaitMessage;
end;
var e0, e: byte;
begin
creneau(0);
creneau(1);
lancer(word(@tampon)); // 8,11 : les deux moitiés comptent comme pleines
e0 := etat; // Stream Status
repeat until etat = 131; // rien n'est rempli à nouveau : 128 + 1 + 2
e := etat;
writeln('Au depart : ', e0, ', tari : ', e, ', sous-alimentations : ', manques);
arreter;
writeln('Arrete : ', etat);
end.Au depart : 128, tari : 131, sous-alimentations : 1
Arrete : 0NeoBASIC
10 ' Laisser un flux se tarir et lire son etat (groupe 8, fonction 13)
20 a = alloc(512) : b = a : gosub 1000 : b = a + 256 : gosub 1000
30 gosub 1100 : ' 8,11 : les deux moities comptent comme pleines
40 gosub 1200 : e0 = s : ' Stream Status
50 repeat : gosub 1200 : until s = 131 : ' rien n'est rempli a nouveau : 128 + 1 + 2
60 print "Au depart : ";e0;", tari : ";s;", sous-alimentations : ";u
70 gosub 1300 : gosub 1200 : print "Arrete : ";s
80 end
1000 ' creneau dans la moitie qui commence en b : 16 echantillons hauts, 16 bas ($80 = silence)
1010 for i = 0 to 255 : x = $40 : if i & 16 then x = $c0
1020 poke b + i, x : next i
1030 return
1100 ' 8,11 : tampon en a, 256 octets par moitie, 11025 Hz, volume 100 ; erreur dans e
1110 while peek($ff00) : wend
1120 doke $ff04, a : doke $ff06, 256 : doke $ff08, 11025 : poke $ff0a, 100
1130 poke $ff01, 11 : poke $ff00, 8
1140 while peek($ff00) : wend
1150 e = peek($ff02)
1160 return
1200 ' 8,13 : etat dans s (bit 7 joue, bits 0 et 1 moities a remplir), sous-alimentations dans u
1210 while peek($ff00) : wend
1220 poke $ff01, 13 : poke $ff00, 8
1230 while peek($ff00) : wend
1240 s = peek($ff04) : u = deek($ff05)
1250 return
1300 ' 8,12 : arret du flux
1310 while peek($ff00) : wend
1320 poke $ff01, 12 : poke $ff00, 8
1330 while peek($ff00) : wend
1340 returnAu depart : 128, tari : 131, sous-alimentations : 1
Arrete : 065C02 assembly (64tass)
; Laisser un flux se tarir et lire son état (groupe 8, fonction 13).
.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: lda #0 ; remplir les deux moitiés avant de lancer
jsr creneau
lda #1
jsr creneau
lda #<lance
ldx #>lance
jsr envoi
jsr etat ; Stream Status
sta e0
_tari: jsr etat ; rien n'est rempli à nouveau : 128 + 1 + 2
cmp #131
bne _tari
lda #<t0
ldx #>t0
jsr ecrire
lda e0
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
lda etat_
jsr decimal
lda #<t2
ldx #>t2
jsr ecrire
lda manques
jsr decimal
lda #13
jsr WriteCharacter
lda #<arret
ldx #>arret
jsr envoi
lda #<t3
ldx #>t3
jsr ecrire
jsr etat
jsr decimal
lda #13
jsr WriteCharacter
rts
; Écrit un créneau dans la moitié A (0 ou 1) : 16 échantillons hauts, 16 bas ($80 = silence).
creneau:
clc
adc #>tampon
sta ptr+1
lda #<tampon
sta ptr
ldy #0
_r: tya
and #16
beq _bas
lda #$C0
bra _ecr
_bas: lda #$40
_ecr: sta (ptr),y
iny
bne _r
rts
; État du flux (8,13) dans A et etat_ ; sous-alimentations (octet bas) dans manques.
etat: jsr WaitMessage
lda #8
ldx #13 ; 8,13 : Stream Status
jsr appel
lda ControlPort+5 ; P1-P2 : sous-alimentations
sta manques
lda ControlPort+4 ; P0 : bit 7 joue, bits 0 et 1 moitiés à remplir
sta etat_
rts
etat_: .byte 0
manques: .byte 0
lance: .byte 8, 11, 7 ; 8,11 : Stream Start
.word tampon, 256, 11025 ; adresse, taille d'une moitié, échantillons par seconde
.byte 100 ; volume
arret: .byte 8, 12, 0 ; 8,12 : Stream Stop
t0: .text "Au depart : ", 0
t1: .text ", tari : ", 0
t2: .text ", sous-alimentations : ", 0
t3: .text "Arrete : ", 0
e0: .byte 0
.align 256
tampon: .fill 512 ; deux moitiés de 256 échantillons
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Au depart : 128, tari : 131, sous-alimentations : 1
Arrete : 08,14 Stream Filled Trinity
Hands a half of the buffer back to the stream, once its new samples are written.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | half (0 or 1) |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the half is neither 0 nor 1, or the stream is stopped |
The loop reads the state (8,13), rewrites each played half then hands it back, six times, without an underrun. A half of 256 samples lasts 23 ms at 11025 Hz: NeoBASIC is too slow to rewrite it in time (six underruns on the bench), so the NeoBASIC example hands it back without rewriting it, and the same sound loops.
Firmware documentation text
Marks half Parameter:0 (0 or 1) of the stream as refilled (Trinity T-79), after the program has written its samples. Sets the error flag if the half is not 0 or 1 or the stream is stopped.
C (llvm-mos)
/* Entretenir un flux en remplissant chaque moitié rendue (groupe 8, fonction 14). */
#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 uint8_t tampon[2 * 256]; /* deux moitiés de 256 échantillons */
static uint16_t manques; /* sous-alimentations, lues par etat() */
static void creneau(uint8_t moitie) { /* écrit un créneau : 16 échantillons hauts, 16 bas */
uint8_t *p = tampon + moitie * 256;
for (uint16_t i = 0; i < 256; i++)
p[i] = (i & 16) ? 0xC0 : 0x40; /* $80 = silence */
}
static uint8_t lancer(uint16_t adresse) { /* 8,11 : 256 octets par moitié, 11025 Hz, volume 100 */
libre();
ControlPort.params[0] = adresse & 0xFF; /* P0-P1 : adresse du tampon */
ControlPort.params[1] = adresse >> 8;
ControlPort.params[2] = 0; /* P2-P3 : taille d'une moitié */
ControlPort.params[3] = 1;
ControlPort.params[4] = 11025 & 0xFF; /* P4-P5 : échantillons par seconde */
ControlPort.params[5] = 11025 >> 8;
ControlPort.params[6] = 100; /* P6 : volume */
api(8, 11); /* Stream Start */
return ControlPort.error;
}
static uint8_t etat(void) { /* 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir */
libre();
api(8, 13);
manques = ControlPort.params[1] | ControlPort.params[2] << 8;
return ControlPort.params[0];
}
static void arreter(void) { /* 8,12 */
libre();
api(8, 12);
}
int main(void) {
creneau(0);
creneau(1);
lancer((uint16_t)tampon); /* 8,11 */
uint8_t remplies = 0;
while (remplies < 6) {
uint8_t e = etat();
for (uint8_t m = 0; m < 2; m++)
if (e & (1 << m)) { /* moitié m jouée : la remplir, la rendre */
creneau(m);
libre();
ControlPort.params[0] = m;
api(8, 14); /* Stream Filled */
remplies++;
}
}
etat();
arreter();
printf("Moities rendues : %u, sous-alimentations : %u\n", remplies, manques);
return 0;
}Moities rendues : 6, sous-alimentations : 0Mad Pascal
// Entretenir un flux en remplissant chaque moitié rendue (groupe 8, fonction 14).
program streamfilled;
uses crt, neo6502;
var tampon: array[0..511] of byte; // deux moitiés de 256 échantillons
manques: word; // sous-alimentations, lues par etat
procedure creneau(moitie: byte); // écrit un créneau : 16 échantillons hauts, 16 bas
var i: word;
begin
for i := 0 to 255 do
if i and 16 <> 0 then tampon[moitie * 256 + i] := $C0
else tampon[moitie * 256 + i] := $40; // $80 = silence
end;
function lancer(adresse: word): byte; // 8,11 : 256 octets par moitié, 11025 Hz, volume 100
begin
NeoMessage.params[0] := lo(adresse); // P0-P1 : adresse du tampon
NeoMessage.params[1] := hi(adresse);
NeoMessage.params[2] := 0; // P2-P3 : taille d'une moitié
NeoMessage.params[3] := 1;
NeoMessage.params[4] := lo(11025); // P4-P5 : échantillons par seconde
NeoMessage.params[5] := hi(11025);
NeoMessage.params[6] := 100; // P6 : volume
NeoSendMessage(8, 11); // Stream Start
NeoWaitMessage;
result := NeoMessage.error;
end;
function etat: byte; // 8,13 : bit 7 joue, bits 0 et 1 moitiés à remplir
begin
NeoSendMessage(8, 13);
NeoWaitMessage;
result := NeoMessage.params[0];
manques := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
procedure arreter; // 8,12
begin
NeoSendMessage(8, 12);
NeoWaitMessage;
end;
var remplies, e, m: byte;
begin
creneau(0);
creneau(1);
lancer(word(@tampon)); // 8,11
remplies := 0;
while remplies < 6 do begin
e := etat;
for m := 0 to 1 do
if e and (1 shl m) <> 0 then begin // moitié m jouée : la remplir, la rendre
creneau(m);
NeoMessage.params[0] := m;
NeoSendMessage(8, 14); // Stream Filled
NeoWaitMessage;
inc(remplies);
end;
end;
etat;
arreter;
writeln('Moities rendues : ', remplies, ', sous-alimentations : ', manques);
end.Moities rendues : 6, sous-alimentations : 0NeoBASIC
10 ' Entretenir un flux en rendant chaque moitie jouee (groupe 8, fonction 14)
20 a = alloc(512) : b = a : gosub 1000 : b = a + 256 : gosub 1000
30 gosub 1100 : ' 8,11
40 r = 0
50 repeat
60 gosub 1200
70 if s & 1 then m = 0 : gosub 400 : ' moitie 0 jouee : la rendre
80 if s & 2 then m = 1 : gosub 400
90 until r >= 6
100 gosub 1200 : gosub 1300
110 print "Moities rendues : ";r;", sous-alimentations : ";u
120 end
400 ' meme son en boucle : trop lent en BASIC pour reecrire 256 echantillons a temps
410 while peek($ff00) : wend
420 poke $ff04, m : poke $ff01, 14 : poke $ff00, 8 : ' Stream Filled
430 while peek($ff00) : wend
440 r = r + 1
450 return
1000 ' creneau dans la moitie qui commence en b : 16 echantillons hauts, 16 bas ($80 = silence)
1010 for i = 0 to 255 : x = $40 : if i & 16 then x = $c0
1020 poke b + i, x : next i
1030 return
1100 ' 8,11 : tampon en a, 256 octets par moitie, 11025 Hz, volume 100 ; erreur dans e
1110 while peek($ff00) : wend
1120 doke $ff04, a : doke $ff06, 256 : doke $ff08, 11025 : poke $ff0a, 100
1130 poke $ff01, 11 : poke $ff00, 8
1140 while peek($ff00) : wend
1150 e = peek($ff02)
1160 return
1200 ' 8,13 : etat dans s (bit 7 joue, bits 0 et 1 moities a remplir), sous-alimentations dans u
1210 while peek($ff00) : wend
1220 poke $ff01, 13 : poke $ff00, 8
1230 while peek($ff00) : wend
1240 s = peek($ff04) : u = deek($ff05)
1250 return
1300 ' 8,12 : arret du flux
1310 while peek($ff00) : wend
1320 poke $ff01, 12 : poke $ff00, 8
1330 while peek($ff00) : wend
1340 returnMoities rendues : 6, sous-alimentations : 065C02 assembly (64tass)
; Entretenir un flux en remplissant chaque moitié rendue (groupe 8, fonction 14).
.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: lda #0 ; remplir les deux moitiés avant de lancer
jsr creneau
lda #1
jsr creneau
lda #<lance
ldx #>lance
jsr envoi
stz rendues
_boucle:
jsr etat
lsr a ; bit 0 : moitié 0 jouée
bcc _m1
lda #0
jsr rendre
_m1: lda etat_
and #2 ; bit 1 : moitié 1 jouée
beq _suite
lda #1
jsr rendre
_suite: lda rendues
cmp #6
bcc _boucle
jsr etat
lda #<arret
ldx #>arret
jsr envoi
lda #<t0
ldx #>t0
jsr ecrire
lda rendues
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
lda manques
jsr decimal
lda #13
jsr WriteCharacter
rts
; Remplit la moitié A et la rend au firmware (8,14).
rendre: pha
jsr creneau
jsr WaitMessage
pla
sta ControlPort+4 ; P0 : moitié
lda #8
ldx #14 ; 8,14 : Stream Filled
jsr appel
inc rendues
rts
; Écrit un créneau dans la moitié A (0 ou 1) : 16 échantillons hauts, 16 bas ($80 = silence).
creneau:
clc
adc #>tampon
sta ptr+1
lda #<tampon
sta ptr
ldy #0
_r: tya
and #16
beq _bas
lda #$C0
bra _ecr
_bas: lda #$40
_ecr: sta (ptr),y
iny
bne _r
rts
; État du flux (8,13) dans A et etat_ ; sous-alimentations (octet bas) dans manques.
etat: jsr WaitMessage
lda #8
ldx #13 ; 8,13 : Stream Status
jsr appel
lda ControlPort+5 ; P1-P2 : sous-alimentations
sta manques
lda ControlPort+4 ; P0 : bit 7 joue, bits 0 et 1 moitiés à remplir
sta etat_
rts
etat_: .byte 0
manques: .byte 0
lance: .byte 8, 11, 7 ; 8,11 : Stream Start
.word tampon, 256, 11025 ; adresse, taille d'une moitié, échantillons par seconde
.byte 100 ; volume
arret: .byte 8, 12, 0 ; 8,12 : Stream Stop
t0: .text "Moities rendues : ", 0
t1: .text ", sous-alimentations : ", 0
rendues: .byte 0
.align 256
tampon: .fill 512 ; deux moitiés de 256 échantillons
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Moities rendues : 6, sous-alimentations : 08,15 Get Master Volume Trinity
Gives the master volume (0 to 16) and tells whether the sound is muted.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | master volume, 0 to 16 (16 at start-up) |
P1 | uint8 | 1 if the sound is muted |
The master volume applies to everything that goes out: the 4 channels and the stream. The media keys of a USB keyboard change it: Mute toggles muting, Volume - and Volume + move it by 2 and unmute. These keys can still be read with 2,22.
Firmware documentation text
Returns the master volume (0-16, 16 at reset) in Parameter:0 and 1 in Parameter:1 if the sound is muted (Trinity T-86). The master volume scales everything the sound system outputs, the four channels and the stream. The Mute, Volume - and Volume + media keys of a USB keyboard change it : Mute toggles, each Volume key moves the volume by 2 and unmutes. The keys are still returned by Function 2,22.
C (llvm-mos)
/* Lire le volume général (groupe 8, fonction 15). */
#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 uint8_t maitre, coupe; /* 8,15 : volume général (0-16) et coupure */
static void lire_maitre(void) {
libre();
api(8, 15); /* Get Master Volume */
maitre = ControlPort.params[0];
coupe = ControlPort.params[1];
}
int main(void) {
lire_maitre();
printf("Volume general : %u, coupe : %u\n", maitre, coupe);
return 0;
}Volume general : 16, coupe : 0Mad Pascal
// Lire le volume général (groupe 8, fonction 15).
program getmaster;
uses crt, neo6502;
var maitre, coupe: byte; // 8,15 : volume général (0-16) et coupure
procedure lire_maitre;
begin
NeoSendMessage(8, 15); // Get Master Volume
NeoWaitMessage;
maitre := NeoMessage.params[0];
coupe := NeoMessage.params[1];
end;
begin
lire_maitre;
writeln('Volume general : ', maitre, ', coupe : ', coupe);
end.Volume general : 16, coupe : 0NeoBASIC
10 ' Lire le volume general (groupe 8, fonction 15)
20 gosub 1000 : ' Get Master Volume
30 print "Volume general : ";g;", coupe : ";k
40 end
1000 ' 8,15 : volume general (0-16) dans g, coupure dans k
1010 while peek($ff00) : wend
1020 poke $ff01, 15 : poke $ff00, 8
1030 while peek($ff00) : wend
1040 g = peek($ff04) : k = peek($ff05)
1050 returnVolume general : 16, coupe : 065C02 assembly (64tass)
; Lire le volume général (groupe 8, fonction 15).
.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 lire
lda #<t0
ldx #>t0
jsr ecrire
jmp montrer
; Volume général (8,15) dans maitre, coupure dans coupe.
lire: jsr WaitMessage
lda #8
ldx #15 ; 8,15 : Get Master Volume
jsr appel
lda ControlPort+4
sta maitre
lda ControlPort+5
sta coupe
rts
maitre: .byte 0
coupe: .byte 0
; Affiche « maitre, coupe : coupe » et un retour à la ligne.
montrer:
lda maitre
jsr decimal
lda #<tc
ldx #>tc
jsr ecrire
lda coupe
jsr decimal
lda #13
jmp WriteCharacter
tc: .text ", coupe : ", 0
t0: .text "Volume general : ", 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Volume general : 16, coupe : 08,16 Set Master Volume Trinity
Sets the master volume (0 to 16) and mutes or unmutes the sound.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | master volume, 0 to 16 |
P1 | uint8 | not zero: sound muted |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the volume is above 16 (nothing changes) |
The setting survives 8,1 and the end of the program: the example restores 16 and unmutes before returning. While muted, notes play (the queue empties) but nothing comes out: on the bench, the recorded sound stays silent. The example causes an error by asking for 17.
Firmware documentation text
Sets the master volume to Parameter:0 (0-16) and mutes the sound if Parameter:1 is not zero (Trinity T-86). Sets the error flag if the volume is above 16.
C (llvm-mos)
/* Couper le son, jouer une note, puis rétablir (groupe 8, fonction 16). */
#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 uint8_t maitre, coupe; /* 8,15 : volume général (0-16) et coupure */
static void lire_maitre(void) {
libre();
api(8, 15); /* Get Master Volume */
maitre = ControlPort.params[0];
coupe = ControlPort.params[1];
}
static uint8_t regler(uint8_t vol, uint8_t muet) { /* 8,16 : rend l'indicateur d'erreur */
libre();
ControlPort.params[0] = vol; /* P0 : 0-16 */
ControlPort.params[1] = muet; /* P1 : 1 coupe le son */
api(8, 16); /* Set Master Volume */
return ControlPort.error;
}
int main(void) {
regler(8, 1); /* volume 8, coupé */
lire_maitre();
printf("Regle : %u, coupe : %u\n", maitre, coupe);
neo_sound_queue(0, 440, 20, 0, 0); /* 8,4 : cette note ne s'entend pas */
while (neo_sound_status(0) != 0)
;
printf("17 : erreur %u\n", regler(17, 0)); /* au-delà de 16 : refusé */
regler(16, 0); /* rétablir : volume 16, son ouvert */
lire_maitre();
printf("Retabli : %u, coupe : %u\n", maitre, coupe);
return 0;
}Regle : 8, coupe : 1
17 : erreur 1
Retabli : 16, coupe : 0Mad Pascal
// Couper le son, jouer une note, puis rétablir (groupe 8, fonction 16).
program setmaster;
uses crt, neo6502;
var maitre, coupe: byte; // 8,15 : volume général (0-16) et coupure
procedure lire_maitre;
begin
NeoSendMessage(8, 15); // Get Master Volume
NeoWaitMessage;
maitre := NeoMessage.params[0];
coupe := NeoMessage.params[1];
end;
var e: byte;
function regler(vol, muet: byte): byte; // 8,16 : rend l'indicateur d'erreur
begin
NeoMessage.params[0] := vol; // P0 : 0-16
NeoMessage.params[1] := muet; // P1 : 1 coupe le son
NeoSendMessage(8, 16); // Set Master Volume
NeoWaitMessage;
result := NeoMessage.error;
end;
begin
regler(8, 1); // volume 8, coupé
lire_maitre;
writeln('Regle : ', maitre, ', coupe : ', coupe);
NeoQueueNote(0, 440, 20, 0, 0); // 8,4 : cette note ne s'entend pas
repeat until NeoGetQueueLen(0) = 0;
e := regler(17, 0); // au-delà de 16 : refusé
writeln('17 : erreur ', e);
regler(16, 0); // rétablir : volume 16, son ouvert
lire_maitre;
writeln('Retabli : ', maitre, ', coupe : ', coupe);
end.Regle : 8, coupe : 1
17 : erreur 1
Retabli : 16, coupe : 0NeoBASIC
10 ' Couper le son, jouer une note, puis retablir (groupe 8, fonction 16)
20 v = 8 : m = 1 : gosub 1100 : ' volume 8, coupe
30 gosub 1000 : print "Regle : ";g;", coupe : ";k
40 sound 0,440,20 : ' cette note ne s'entend pas
50 repeat : until notes(0) = 0
60 v = 17 : m = 0 : gosub 1100 : print "17 : erreur ";e : ' au-dela de 16 : refuse
70 v = 16 : m = 0 : gosub 1100 : ' retablir : volume 16, son ouvert
80 gosub 1000 : print "Retabli : ";g;", coupe : ";k
90 end
1000 ' 8,15 : volume general (0-16) dans g, coupure dans k
1010 while peek($ff00) : wend
1020 poke $ff01, 15 : poke $ff00, 8
1030 while peek($ff00) : wend
1040 g = peek($ff04) : k = peek($ff05)
1050 return
1100 ' 8,16 : volume general v (0-16), m = 1 coupe le son ; erreur dans e
1110 while peek($ff00) : wend
1120 poke $ff04, v : poke $ff05, m : poke $ff01, 16 : poke $ff00, 8
1130 while peek($ff00) : wend
1140 e = peek($ff02)
1150 returnRegle : 8, coupe : 1
17 : erreur 1
Retabli : 16, coupe : 065C02 assembly (64tass)
; Couper le son, jouer une note, puis rétablir (groupe 8, fonction 16).
.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: lda #<coupe8
ldx #>coupe8
jsr envoi
jsr lire
lda #<t0
ldx #>t0
jsr ecrire
jsr montrer
lda #<n1
ldx #>n1
jsr envoi
jsr attendre
lda #<trop
ldx #>trop
jsr envoi
lda ControlPort+2 ; au-delà de 16 : refusé
sta e
lda #<t1
ldx #>t1
jsr ecrire
lda e
jsr decimal
lda #13
jsr WriteCharacter
lda #<ouvre
ldx #>ouvre
jsr envoi
jsr lire
lda #<t2
ldx #>t2
jsr ecrire
jmp montrer
; Volume général (8,15) dans maitre, coupure dans coupe.
lire: jsr WaitMessage
lda #8
ldx #15 ; 8,15 : Get Master Volume
jsr appel
lda ControlPort+4
sta maitre
lda ControlPort+5
sta coupe
rts
maitre: .byte 0
coupe: .byte 0
; Affiche « maitre, coupe : coupe » et un retour à la ligne.
montrer:
lda maitre
jsr decimal
lda #<tc
ldx #>tc
jsr ecrire
lda coupe
jsr decimal
lda #13
jmp WriteCharacter
tc: .text ", coupe : ", 0
coupe8: .byte 8, 16, 2, 8, 1 ; 8,16 : Set Master Volume, volume 8, coupé
trop: .byte 8, 16, 2, 17, 0 ; volume 17
ouvre: .byte 8, 16, 2, 16, 0 ; rétablir : volume 16, son ouvert
n1: .byte 8, 4, 8, 0 ; 8,4 : canal 0 : cette note ne s'entend pas
.word 440, 20, 0
.byte 0
t0: .text "Regle : ", 0
t1: .text "17 : erreur ", 0
t2: .text "Retabli : ", 0
e: .byte 0
; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi: sta blk
stx blk+1
jsr WaitMessage
ldy #2
lda (blk),y ; nombre de paramètres
clc
adc #3
sta fin
ldy #3
_copie: cpy fin
beq _lance
lda (blk),y
sta ControlPort+1,y ; octet 3 du bloc -> P0 ($FF04)...
iny
bra _copie
_lance: ldy #1
lda (blk),y
tax
lda (blk)
jmp appel
; Nombre de notes en file sur le canal A (8,6), rendu dans A.
statut: pha
jsr WaitMessage
pla
sta ControlPort+4
lda #8
ldx #6 ; 8,6 : Sound Status
jsr appel
lda ControlPort+4
rts
; Attend que le canal 0 se taise.
attendre:
lda #0
jsr statut
bne attendre
rts
; 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
blk = $82
diviseur: .byte 0
fin: .byte 0Regle : 8, coupe : 1
17 : erreur 1
Retabli : 16, coupe : 0