Musique 1bit pour exelvision
L’exl100 possède un générateur de synthèse vocale, intéressant mais pas vraiment musical. Il possède aussi une sortie 1bit servant de générateur pour le magnétocassette, cette sortie est câblée vers le haut parleur de la télé ou du moniteur ce qui permet de l’utiliser comme générateur musical primitif.
La machine de référence pour la musique 1bit est le sinclair ZX spectrum qui possède une scène musicale riche (cf le blog de shiru).
L’idée est de repartir d’un des players de référence sur cette machine pour le transposer à l’exelvision.
Le player le plus simple est un 2 voies de signaux carrés, format dit musicBox.
Beepola est un éditeur avancé tournant sur PC permet de composer et de générer vers ce format.
La chaine de création de musique sera donc:
- BEEPOLA : pour l’édition des musiques, exportées au format binaire pour music BOX;
- Outil de conversion MusicBox vers player EXL (conversion vers un format source ASM);
- Player pour exl100;
- TASM pour la compilation finale;
- EXL100 ou DCEXEL pour l’écoute.
Beepola
Beepola est un projet récent, un éditeur permettant de créer sur PC des mélodies pour certains players 1bit du ZX spectrum, c’est l’outil idéal pour tout projet de musique 1bit…
Lancement de beepola:

Export vers format binaire:

L’export au format ASM permet de comprendre les subtilités des players du spectrum, le format MusicBox est le plus simple et celui qui demande le moins de ressources… c’est un bon choix de départ pour l’exl100 qui n’est pas spécialement rapide…
Conversion BEEPOLA vers player exelvision
Le format ZX spectrum n’est pas directement utilisable sur exl. D’une part les adresses doivent être décodées pour revenir à un code source symbolique, et d’autre part, les patterns doivent être inversées pour pouvoir utiliser le seul opcode 16bit du TMS7xxx, à savoir DECD, un décrément sur 16bits.
La conversion se fait en mode commande, en indiquant simplement en premier argument le nom du fichier binaire ZX et en sortie le nom du fichier source pour EXL
#include #include unsigned char raw_music[0x10000]; int raw_music_size; int song[256]; int patterns_size[256]; int offsets[256]; int patterns_tempo[256]; int pattern_start; int song_size; int where; int nb_pat; int main(int argc, char *argv[]) { FILE *fin; FILE *fout; int i,j; if (argc<3) { fprintf(stderr,"Bad args"); return EXIT_SUCCESS; } fin=fopen(argv[1],"rb"); raw_music_size=fread(raw_music,1,0x10000,fin); fprintf(stderr,"Size :%d\n",raw_music_size); fclose(fin); fout=fopen(argv[2],"w"); fprintf(fout,"SONG:\n"); fprintf(fout,".msfirst\n"); where=0; where++; song_size=(raw_music[where++]/2); for (i=0;i
for (i=0;i=0;j--) { int v1,v2; v1=raw_music[pattern_start+offsets[i]+j*2+1];if (v1==1) v1=0; v2=raw_music[pattern_start+offsets[i]+j*2+2];if (v2==1) v2=0; fprintf(fout,"\t.byte\t%d,%d\n",v1,v2); } fprintf(fout,"\t.byte\t%d\n",patterns_tempo[i]); fprintf(fout,"\t.byte\t%d\n",patterns_size[i]); fprintf(fout,"PAT%d\n",i); } fclose(fout); return EXIT_SUCCESS; }
Le résultat de la conversion est de la forme:
SONG: .msfirst .word PAT0 .word PAT1 .word PAT0 .word PAT2 .word PAT3 .word PAT4 .word PAT3 .word PAT5 .byte 16,0 .byte 16,64 .byte 20,0 .byte 20,0 .byte 16,0 .byte 16,96 .byte 18,0 .byte 18,0 .byte 20,0 .byte 20,64 .byte 21,0 .byte 21,0 .byte 24,0 .byte 24,96 .byte 238 .byte 14 PAT0 .byte 18,0 .byte 18,0 .byte 18,0 .byte 18,64 .byte 23,0 .byte 23,0 .byte 18,0 .byte 18,96 .byte 17,0 .byte 17,0 .byte 17,0 .byte 17,64 .byte 21,0 .byte 21,0 .byte 17,0 .byte 17,96 .byte 16,0 .byte 16,0 .byte 238 .byte 18
Le player pour exelvision
On utilise un timer en temps fixe et 2 compteurs pour simuler les 2 générateurs carrés. Tous les N cycles (tempo) on change de note, puis de pattern tous les x changement de note (selon la longueur de la pattern).
Le code est le suivant (en standalone pour une ROM de 8ko):
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Conversion EXL100 du player 2 canaux MusicBox (speccy) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
R0 .equ 0 A .equ 0 R1 .equ 1 B .equ 1 R2 .equ 2 R3 .equ 3 R7 .equ 7 R12 .equ 12 R13 .equ 13
R14 .equ 14 TEMP1 .equ 14
R16 .equ 16 TEMP2 .equ 16
R18 .equ 18 TEMP3 .equ 18
R20 .equ 20 TEMP4 .equ 20
R22 .equ 22 TEMP5 .equ 22
TEMP6 .equ 24
TEMP7 .equ 26
R28 .equ 28 TEMP8 .equ 28 R127 .equ 127
CH1 .equ 39 CH1_COUNT .equ 40 CH2 .equ 41 CH2_COUNT .equ 42 TEMPO .equ 44 TEMPO_COUNT .equ 46 PAT_PTR .equ 48 PAT_COUNT .equ 49 PAT_NUM .equ 50 PAT_NB .equ 51 PAT_LEN .equ 52
P2 .equ 2 P3 .equ 3 P6 .equ 6 P7 .equ 7 VDP_REG .equ 45 W_VDP .equ $2E
; this is a 8kb exl100 rom .org $6000 start: MOV %$60,B LDSP CLR B loop_vdp: LDA @vdp[B] MOVP A,VDP_REG INC B CMP B,vdp_init_size JL loop_vdp EINT MOVD %$0000,TEMP1 TRAP 8 MOVD %$8000,TEMP1 CLR A cls: MOVP A,W_VDP DECD TEMP1 JNZ cls MOVD %$7000,TEMP8 TRAP 13 ; chargement generateur de char MOVD %$0000,TEMP1 TRAP 8 MOV %$00,TEMP3 ; couleurs et attributs... MOVD %message,TEMP2 TRAP 20
movd %timer,B sta @$C004 mov B,A sta @$C005 movd %2000,TEMPO movd TEMPO,TEMPO_COUNT MOV %0,PAT_NUM MOV %8,PAT_NB
CALL @new_pat movp %$07,P2 movp %$81,P3 ; lance le timer
end: JMP end
new_pat MOV PAT_NUM,B INC PAT_NUM CMP PAT_NB,PAT_NUM JNZ not_last CLR PAT_NUM not_last RL B LDA @SONG[B] MOV A,PAT_PTR-1 LDA @(SONG+1)[B] MOV A,PAT_PTR DECD PAT_PTR LDA *PAT_PTR MOV A,PAT_COUNT ; lecture taille pattern DECD PAT_PTR LDA *PAT_PTR ; lecture tempo (inutilisé) ; on decode les 2 premieres notes... DECD PAT_PTR LDA *PAT_PTR MOV A,CH1 MOV CH1,CH1_COUNT DECD PAT_PTR LDA *PAT_PTR MOV A,CH2 MOV CH2,CH2_COUNT rets
timer MOV CH1,A JZ ch1_muted ; mute sur chanel1 DJNZ CH1_COUNT,ch1_no_phase xorp %08,P6 MOV CH1,CH1_COUNT ch1_muted ch1_no_phase
MOV CH2,A JZ ch2_muted ; mute sur chanel2 DJNZ CH2_COUNT,ch2_no_phase xorp %08,P6 MOV CH2,CH2_COUNT
ch2_muted ch2_no_phase
DECD TEMPO_COUNT JC end_timer MOVD TEMPO,TEMPO_COUNT
DECD PAT_PTR LDA *PAT_PTR CMP A,CH1 JZ skip_ch1 MOV A,CH1 MOV CH1,CH1_COUNT skip_ch1 DECD PAT_PTR LDA *PAT_PTR CMP A,CH2 JZ skip_ch2 MOV A,CH2 MOV CH2,CH2_COUNT skip_ch2
DJNZ PAT_COUNT,skip_pat_reset CALL @new_pat skip_pat_reset end_timer rets message: .byte "Welcome to EXL MusicBox player" .byte 0
Compilation de la chanson
Il suffit d’assembler le player et la chanson dans un même fichier.
exemple de ligne de compilation (à mettre dans un fichier .bat)
TASM\TASM.EXE -t70 MUSIC2CH.ASM -b -o music2ch.rom
Ecoute…
Dans un émulateur en chargeant la ROM.
Pour passer sur machine réelle, une K7 a été crée à l’aide des outils présents sur la page outils K7.
Le rendu sur machine réelle est assez proche des émulateurs, aucun des 2 n’était parfaitement juste au moment du test.
Pour aller plus loin…

Le blog de shiru …