Salut, quand je suis en vacances je perds toujours mon temps à jouer à factorio ou à construire des ordinateurs dans minecraft, cette fois c'était les ordinateurs.
100Hz ???? Mais dans minecraft la plus petite durée est le tick, et il y en a 20 par seconde et ça fait 20Hz au max !!! Comment c'est possible ??!!
Une chaîne de command blocks s’exécute en un tick, peu importe sa longueur. Mais un command block ne peut être exécuté qu'une seule fois par tick, sauf si on le débride en modifiant le tag "UpdateLastExecution". On peut alors faire autant de choses qu'on veut (enfin presque) dans le même tick avec un peu de magie et une utilisation abusive de la commande /clone.
En gros, à chaque tick l'unité de contrôle charge l'instruction qu'il faut exécuter mais aussi les 4 ou 5 suivantes (ou autant qu'on veut c'est juste qu'à partir de 4 mon jeu commence à ramer). Elles sont alors toutes exécutées dans le même tick mais quand il y a un saut les instructions chargées ne sont plus les bonnes, on passe au tick suivant pour recharger les bonnes (ça a les mêmes limitations qu'une pipeline pour ceux qui connaissent). Donc si on fait exécuter les instructions 5 par 5 et qu'il n'y a pas de sauts dans le programme, on arrive à 100Hz ! J'ai des pistes pour un système plus efficace, je suis en train d’expérimenter, faire de la branch prediction dans minecraft ce serait classe quand même...
Bref ça va très vite, ça prend que quelques secondes pour overflow 16bits avec la suite de fibonacci.
La mémoire du programme est séparée de la mémoire principale pour permettre de stocker des instructions déjà décodées, ce qui accélère beaucoup les choses, mais cela empêche la modification du programme par l'ordinateur lui-même. J'ai écrit un petit script pour compiler de l'assembleur et d'exporter dans la mémoire.
Il y a 256 lignes pour le programme et 1K de RAM, mais on peut monter à 64K pour chaque, après c'est un problème de place. Les 128 dernières adresses de la RAM sont réservées à l'écran.
Il y a une vingtaine d'instructions, grâce au stack le processeur supporte l'appel de fonctions et les interruptions.
Les 8 registres 16bits qui peuvent tous être utilisés de la même manières dans des opérations arithmétiques ou logiques :
Les interfaces (c'est pas compliqué d'en brancher d'autres sur les ports I/O) :
Documentation (disponible dans le téléchargement) :
Je publierai une vidéo.. un jour.
Nan c'est juste pour montrer qu'on peut afficher des couleurs sur l'écran. J'aurais pu faire un arc-en-ciel avec les 16 couleurs mais bon.
Je n'ai pas encore remis le système de overclock magique, ça va me prendre un peu de temps. x)
Pour l'instant c'est limité à 20Hz. La doc, l'assembleur et d'autres exemples de programmes sont inclus dans le téléchargement.
100Hz ???? Mais dans minecraft la plus petite durée est le tick, et il y en a 20 par seconde et ça fait 20Hz au max !!! Comment c'est possible ??!!
Une chaîne de command blocks s’exécute en un tick, peu importe sa longueur. Mais un command block ne peut être exécuté qu'une seule fois par tick, sauf si on le débride en modifiant le tag "UpdateLastExecution". On peut alors faire autant de choses qu'on veut (enfin presque) dans le même tick avec un peu de magie et une utilisation abusive de la commande /clone.
En gros, à chaque tick l'unité de contrôle charge l'instruction qu'il faut exécuter mais aussi les 4 ou 5 suivantes (ou autant qu'on veut c'est juste qu'à partir de 4 mon jeu commence à ramer). Elles sont alors toutes exécutées dans le même tick mais quand il y a un saut les instructions chargées ne sont plus les bonnes, on passe au tick suivant pour recharger les bonnes (ça a les mêmes limitations qu'une pipeline pour ceux qui connaissent). Donc si on fait exécuter les instructions 5 par 5 et qu'il n'y a pas de sauts dans le programme, on arrive à 100Hz ! J'ai des pistes pour un système plus efficace, je suis en train d’expérimenter, faire de la branch prediction dans minecraft ce serait classe quand même...
Bref ça va très vite, ça prend que quelques secondes pour overflow 16bits avec la suite de fibonacci.



La mémoire du programme est séparée de la mémoire principale pour permettre de stocker des instructions déjà décodées, ce qui accélère beaucoup les choses, mais cela empêche la modification du programme par l'ordinateur lui-même. J'ai écrit un petit script pour compiler de l'assembleur et d'exporter dans la mémoire.
Il y a 256 lignes pour le programme et 1K de RAM, mais on peut monter à 64K pour chaque, après c'est un problème de place. Les 128 dernières adresses de la RAM sont réservées à l'écran.
Il y a une vingtaine d'instructions, grâce au stack le processeur supporte l'appel de fonctions et les interruptions.
Les 8 registres 16bits qui peuvent tous être utilisés de la même manières dans des opérations arithmétiques ou logiques :
- 3 registres de données (A, B, C)
- registre d'adresse
- registre de segment
- compteur ordinal
- stack pointer
- registre des flags (zéro, négatif et overflow, + statut)
Les interfaces (c'est pas compliqué d'en brancher d'autres sur les ports I/O) :
- écran 16*8 caractères (qui supporte un genre d'ASCII du pauvre)
- entrée/sortie hexadécimale
- entrée/sortie binaire
Documentation (disponible dans le téléchargement) :
Code:
Schéma d'une instruction :
Code de fonction || op1 || op2 || op3
ou bien :
Code de saut || op1
ou bien :
Code de saut || Code de fonction || op1 || op2 || op3
Ici le résultat de la fonction sert d'adresse pour le saut.
-> surtout pratique pour gagner une ligne avec IRET, sinon c'est gadget.
Opérandes possibles dans les instructions ('op') :
registres (A,B,C,ADR,CS,SP,PC,FLAGS)
nombre immédiat ('x') qui peut être décimal, binaire ('0bXXX') ou hexadécimale ('0xXXX')
adresse ram indirecte (via ADR)
adresse ram immédiate ('[x]')
port I/O indirect (via ADR)
port I/O immédiat ('{x}')
PUSH et POP (offset possible avec POP)
'VOID' si l'on ne veut pas garder le résultat d'une opération (ou alors pour générer un zéro)
adresse immédiate relative au stack pointer ('(x)') :
(0) est l'adresse pointée par le stack (qui est normalement vide)
(1) est l'adresse qui précède celle pointée par le stack
(2) est celle encore avant etc...
-> on ne peut accéder qu'à des adresses "dans le passé"
note : On peut donner autant de valeurs immédiates que l'on veut par instruction,
on peut donc faire des opérations sur la mémoire sans passer par les registres.
-> les valeurs immédiates prennent beaucoup de mémoire
Registre des flags :
0-8 non utilisés
9 overflow
10 négatif
11 zéro
12 halt
13 ne pas incrémenter PC (utilisé par les sauts)
14 interruption
15 flush de la pipeline
'FL' peut être placé à la fin de n'importe quelle instruction
pour mettre à jour les flags zéro, overflow et négatif
codes de saut :
IRET (pas d'opérande) retour d'interruption restaure les registres
INT appel d'interruption, sauvegarde tous les registres, saut vers un autre segment (via CS)
JS saut sans condition vers un autre segment (via CS)
CALL appel de fonction (push l'adresse du compteur programme)
JMP saut sans condition
JNZ saut si flag zéro = 0
JNO saut si flag retenue = 0
JNN saut si flag négatif = 0
JZ saut si flag zéro = 1
JO saut si flag retenue = 1
JN saut si flag négatif = 1
op1 :
saute à l'adresse indiquée par op1
Codes de fonction :
MOV copie / f(x) = x
NOT fonction NOT
SHL décalage logique vers la gauche
RTL rotatation vers la gauche
SHR décalage logique vers la droite
RTR rotation vers la droite
NEG opposé
INC incrémentation
DEC décrémentation
op1,op2 :
stocke le résultat de f(op2) dans op1
AND ET logique
XOR OU exclusif logique
OR OU logique
ADD addition
SUB soustration
op1,op2,op3 :
stocke f(op2,op3) dans op1
Code:
Assemblage :
Il faut d'abord lancer l'exe ou glisser-déposer dessus le fichier qui contient le code.
On indique ensuite le segment : l'adresse à laquelle le programme commence.
Deux fichiers sont créés : "log_bin.txt" avec le programme en langage machine et "import.mcfunction" qui sert pour
importer le programme dans minecraft.
Importation :
Le fichier "import_*programme*.mcfunction" doit être placé dans le datapack de la map :
donc dans C:\Users\Utilisateur\AppData\Roaming\.minecraft\saves\CB 1642\datapacks\import\data\import\functions
Il faut ensuite recharger le datapack avec "/reload", puis importer avec "/function import:import.mcfunction"
Il n'est pas nécessaire de reset la RAM, on peut bien sûr importer plusieurs programmes, mais il faut leur donner des adresses bien différentes :
la gestion de la mémoire est manuelle.
Fonctions de l'assembleur :
Ce ne sont pas des instructions qui seront exécutées par le processeur.
Elles aident à la programmation et commencent par un point.
.const NOM VALEUR
Défini une constante
.data NOM VALEUR
La valeur peut être un nombre, un string (avec des "") -> les caractères doivent être dans la table
(attention aux majuscules et aux espaces), ou un array de nombres (avec des {} et des virgules pour séparer)
Des constantes sont alors définies :
*NOM qui renvoie la valeur de la variable oar adressage ram immédiat,
ex : 'MOV *NOM 10' change la valeur de NOM
&NOM qui donne l'adresse de NOM en tant que nombre immédiat
NOM.LEN qui donne la longueur si la valeur est un string ou un array
.label NOM
Crée une constante qui renvoie vers une adresse relative à l'origine du programme,
-> intéressant pour les sauts
.labelabs NOM
Idem .label mais l'adresse est absolue (par rapport à toute la ram)
-> intéressant pour le saut JS.
.segment ADRESSE
Permet de dire où se trouve le programme dans la mémoire, demandé toute façon par le complilateur.
-> intéressant quand on a deux programmes dans le même fichier et que l'on veut les importer en même temps (risqué quand même)
.offset ADRESSE
Permet de définir où se trouve le code par rapport à l'origine du programme
-> intéressant pour gérer les adresses quand on a beaucoup de fonctions dans le programme
Code:
;affiche "HELLO WORLD !"
;nécessite CBCOS
MOV {3} 2 ;on efface l'écran
MOV A 896 ;on met l'adresse du début de la ram vidéo dans A
MOV B &TEXT_HELLO ;on met l'adresse du début du text dans B
MOV C TEXT_HELLO.LEN ;la longueur du text dans C
INT 20 ;interruption vers une fonction de CBCOS, copie de mémoire (paramètres dans A, B et C)
MOV {3} 1 ;on rafraichit l'écran
INT 8
;définition de la variable "TEXT_HELLO"
.data TEXT_HELLO "HELLO WORLD !"
Code:
Ecran 16*8 caractères :
Pour rafraichir l'écran : écrire un 1 au port 3
Pour effacer l'écran : écrire un 2 au port 3
Adresses de l'écran : 896 à 1023 (0x380 à 0x3FF)
Table des caractères:
0 : SPACE ('_' dans les déclarations de strings)
1 : BLANK
2 : 0
3 : 1
4 : 2
5 : 3
6 : 4
7 : 5
8 : 6
9 : 7
10 : 8
11 : 9
12 : A
13 : B
14 : C
15 : D
16 : E
17 : F
18 : G
19 : H
20 : I
21 : J
22 : K
23 : L
24 : M
25 : N
26 : O
27 : P
28 : Q
29 : R
30 : S
31 : T
32 : U
33 : V
34 : W
35 : X
36 : Y
37 : Z
38 : +
39 : -
40 : *
41 : /
42 : ^
43 : =
44 : >
45 : <
46 : .
47 : ,
48 : :
49 : ;
50 : !
51 : ?
52 : '
53 : "
54 : (
55 : )
Code:
Adresse 0x0 à 0x6F : CBCOS --> au démarrage il affiche un message à l'écran et demande le programme qu'il faut executer via l'entrée hexadécimale.
Adresses 0x100 à 0x111 : fibonacci
Adresses 0x140 à 0x15A : hello world
Adresses 0x180 à 0x19C : test de l'écran
Adresses 0x200 à --- : mémoire stack, allouée par CBCOS, attention à ne pas mettre des programmes trop près après 0x200
Adresses 0x380 à 0x3FF : mémoire vidéo, peut être utilisée normalement si l'écran n'est pas utilisé
Je publierai une vidéo.. un jour.

Nan c'est juste pour montrer qu'on peut afficher des couleurs sur l'écran. J'aurais pu faire un arc-en-ciel avec les 16 couleurs mais bon.
Je n'ai pas encore remis le système de overclock magique, ça va me prendre un peu de temps. x)
Pour l'instant c'est limité à 20Hz. La doc, l'assembleur et d'autres exemples de programmes sont inclus dans le téléchargement.
Fichiers joints
Dernière édition: