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:

beepola

Export vers format binaire:

export song

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…

1-bit music news blog

Le blog de shiru …