Les custom blocks :
Bon, pour arriver à concocter quelques custom blocs, il faut d’abord se munir de blocktool, de translhexion, du bloc-notes, de spritetool, bien sur de Lunar Magic et d’une rom de Super Mario World et enfin du fichier d’aide qui se trouve ici. Note : ayez sous les yeux la partie RAM Map de smw central là.
Comme ce tuto risque d’être un peu long, je vais le décomposer en 3 parties : les bases de l’ASM, créer son bloc et enfin des exemples de custom blocs.
1. Les bases de l’ASM (ou étude théorique)
D’abord, ‘y faut dire 65C816 ASM pour la snes (tuh…). Pour ceux qui ne connaissent pas cela veut dire ASseMbly(eur). C’est le langage informatique qu’utilise la snes. En réalité il existe plusieurs versions d'assembleur, une pour chaque processeur. Ici je vais vous apprendre l'ASM 65C816 car c'est celui qui nous intéresse. Quand vous ouvrez la rom avec Translhexion, vous voyez la forme hexadécimal de l’ASM (pour ceux qui ne savent pas comment utiliser translhexion ni blocktool, reportez vous aux tutos de cette discussion ; je suppose que vous savez ce qu’est l’hexadécimal puisque vous savez utiliser Lunar Magic…). On appelle cet ensemble de chiffre le "machine code" in english ou en français le langage machine. C'est le langage compréhensible par la snes. Pour pouvoir traduire l'ASM en fichier executable ou ici en fichier.bin qui contiendra le langage machine, il faut un compilateur, un logiciel qui traduira le fichier source dans lequel se trouve le code source (l'ASM est utilisé dans le code source). On dit que l'assembleur est un langage compilé car il est traduit une fois pour toute. Let's go !
Bon. Commençons par le commencement.
L’ASM est fait d’opcodes et de valeurs. Les opcodes correspondent à des actions à effectuer. Les valeurs, bah… ce sont des valeurs. Les valeurs peuvent être directes : un nombre, là comme ça, au hasard, 007 ^^ ; ou indirectes (nous allons voir ça après). Sachez qu’en faisant de l’ASM, vous avez à disposition des registers(res) : l’accumulator(eur), les X et Y registers, le stack, le processor status ou flag register (celui-là est particulier). Le plus important est l’accumulator : il vous permet de stocker 1 valeur mais peut être utilisé indéfiniment. Ca fait beaucoup de notion, nan ? un petit exemple :
LDA #$07
STA $7E0DBF
‘paniquez pas j’vous explique. LDA et STA sont des opcodes. #$07 est une valeur directe, $0DBF indirecte. Traduction : LDA veut dire LoaD Accumulator (je noterai accumulator A par la suite) et STA STore A. Les valeurs directes en hexadécimal sont précédées de #$ et en binaire de #%, et les valeurs indirectes sont simplement précédées de $ puisqu’elle viennent de la RAM.
Ce qui veut dire que ce code met dans l’A la valeur en héxa 07 et restitue la valeur de l’A à l’adresse 7E0DBF. Si on regarde dans la RAM Map de smwcentral, il s’agit du compteur de pièces. Si vous faites un bloc avec ce code (on verra ça après), le bloc remet le compteur des pièces à 7. Note pour les valeurs indirectes, le 7E n’est pas obligatoire, ainsi que les 2 zéros s’il y en a après : $7E0019 devient $19. Attention : on ne supprime pas les deux derniers zéros. Ex : $7E1900 ne sera jamais $19.
Eh ben là vous avez déjà appris...le tiers de cette partie ! on continue avec d’autres opcodes :
STZ : STore Zero to memory. STZ $0DBF -> met 0 à l’adresse 0DBF (Mario a plus de pièces ^^)
INC : INCrement A or memory. INC $0DBF -> augmente de 1 la valeur indirecte de l’adresse 0DBF (Mario gagne une pièce)
DEC : DECrement A or memory. DEC $0DBF -> diminue de 1 la valeur indirecte de l’adresse 0DBF (Mario perd une pièce)
Ca continue... En réalité il me semble qu’il y en a 92. Vous pouvez avoir une vue d’ensemble en ouvrant le fichier d’aide dont j’ai parlé au début (en anglais)
ASL : Left Shift A or memory. Multiplie par 2 l’A. Ex : A=15, ASL, A=30
LSR : Right Shift A or memory. Divise par 2 l’A. Note : si le résultat est avec une virgule (en gros si ça tombe en xx,5), on garde la partie entière (on garde xx).
TAX : Transfer A to X. Transfert la valeur de l’A au register X (j’expliquerai après)
TAX, TAY, TXY, TYA,... même principe
XBA : eXchange B and A. Echange le "high byte" (dans un nombre avec xxyy, xx est le high byte) avec le "low byte"(yy). Ex : A=1523, XBA, A=2315
Note sur les bits : 1 byte = 8bits = xx, 2 bytes = 16bits = xxxx, 3 bytes = 24bits = xxxxxx, etc...
REP : Reset Processor Status et SEP: SEt Processor status flag. Quand vous avez une adresse en 2 byte, les manips ne marcheront pas. Vous devez donc utiliser :
REP #$20 -> A passe en 16 bits
REP #$30 -> A, X et Y passent en 16 bits
REP #$10 -> X et Y passent en 16bits
SEP #$20 -> A retourne en 8 bits
SEP #$30 -> A, X et Y retournent en 8bits
SEP #$10 -> X et Y retournent en 8 bits
J'explique ce qu'est le processor status ou flag register : c'est un ensemble de 8 bits (ex : 01000101) dont chaque bit se met sur 0 ou 1 en fonction des opérations effectuées. On a :
N V M X D I Z C
N : negative flag V : overflow flag
M : "m" (?) flag X : "x" (?) flag
D : decimal flag I : interrupt flag
Z : zero flag C : carry flag
Bon je vous explique quand même X et Y : ils fonctionnent à peu près pareil que l'A, mais ils ont d'autres propriétés, notamment une très importante : l'indexing (le fait de pouvoir stocker plusieurs valeur à la fois (je ne vous l'expliquerais pas car je ne le comprend pas, mais sachez qu'il est SUPER utile dans les custom sprites)). Préférez l'A ^^. Cela dit cela peut être utile si vous voulez ne pas utilisez l'A pour telle raison. Après il y a le stack : il vous permet de stocker une valeur un moment, puis de la remettre ensuite. C'est comme une pile de livres : vous mettez un livre sur cette pile, vous effectuez n'importe quoi, mais après vous reprenez le livre.
Opcodes liés au stack :
PHA : PusH A. Prend un "livre" ou valeur de l'accumulator et le met sur le stack.
PLA : PuLl A. Remet à sa place le "livre" ou valeur déposée précédemment.
Voici maintenant 2 opcodes tout à fait inutile xD :
NOP : NO oPération. Celui ci ne fera strictement rien
STP : StoP the Clock. Celui la "freeze" le jeu : la partie se bloque.
Et maintenant des très importants :
CMP : CoMPare A with memory et BEQ : Branch if EQual, BNE : Branch if Not Equal
Un petit exemple va vous permettre de comprendre :
LDA $0DBF met le nb de pièces de mario dans l'A...
CMP #$20 ...compare cette valeur avec le nombre 20 (en décimal, 32)...
BNE retour ...si ces 2 valeurs ne sont pas égales, aller à l'étiquette retour...
INC $0DBF ...sinon, augmenter les pièces de 1...
retour :
RTS ...retourner au jeu.
Donc si vous n'avez pas 32 pièces, vous n'aurez pas une pièce en plus.
CMP associé à BEQ, BNE,... sont utilisé pour les branchements.
Quand vous utilisez CMP, on soustrait la valeur de l'A à la valeur derrière l'opcode : si les deux valeurs sont identiques, le zero flag est mis sur ON. BEQ branche alors si le zero flag = ON. C'est pourquoi vous pouvez voir des BEQ ou BNE tout seul : ils utilisent le zero flag directement.
Il existe aussi :
BCS (BGE) : Branch if GrEater than -> si valeur > A, carry flag = ON donc BCS branche si carry flag = ON
BCC (BLT) : Branch if Less Than -> branche si carry flag = OFF
BPL : Branch if Plus -> branche si negative flag = OFF
BMI : Branch if Minus -> branche si negative flag = ON
Les deux dépendent du negative flag : une valeur directe est dite positive si <#$80, et négative si >=#$80 (si vous voulez savoir : positive : de #%00000000 à #%01111111 ; négative : de #%10000000 à #%11111111)
BVC : Branch if oVerflow Clear -> branche si overflow flag = OFF
BVS : Branch if oVerflow Set -> branche si overflow flag = ON, c'est-à-dire s'ils dépassent #$FF ou #$00.
Ces deux-là en revanche dépendent de l'overflow flag ; je l'expliquerai dans la partie ADC/SBC.
Avez-vous remarqué ? J'ai mis RTS...
RTS : ReTurn from Subroutine. Cet opcode doit terminer chaque bloc. Pourquoi ? parce que ça dit à la snes de retourner aux actions possibles de Mario, au jeu...
Voyez cela comme ça : .....(jeu).............(Mario touche/frappe un bloc)->[action du bloc........RTS].......(jeu).....
Quand vous tapez un bloc, le jeu est "jumped", littéralement "sauté", par l'opcode :
JSR : Jump to SubRoutine.
JSL : Jump to Subroutine Long. Cet opcode utilisé dans un bloc bascule vers une autre subroutine (comme par exemple la routine ou Mario devient petit ou gros) ; les opcodes situés à l'adresse indiquée sont effectuées (s'il n'y en pas, c'est le gros bug). Vous pouvez trouver des subroutines dans la ROM Map de smwcentral (conseil : cliquez sur type pour les trier, elles se trouveront à la fin). Si vous voulez savoir comment ça marche, JSL met la location de votre code dans le stack et bascule vers ladite subroutine. Le RTL : Return to Subroutine Long à la fin de la subroutine repioche la localisation de votre code dans le stack. C'est le même principe avec RTS : le saut vers votre code a mis la localition "vers le jeu" dans le stack. RTS à la fin utilise alors cette donnée pour y retourner ; donc si vous mettez qqch dans le stack lors de votre code et que vous ne le reprenez pas, RTS va vous envoyer n'importe où !
Attention : l'adresse à mettre lors de l'utilisation de JSL est la snes adresse.
Ex :
JSL $00F5B7 bascule vers la subroutine qui blesse Mario
RTS
Ce bloc est donc comme une "black piranhe plant" ^^
JSR doit être utilisé avec RTS, et JSL avec RTL. Simple ?
JMP : JuMP
JML : JuMp Long
Idem sauf qu'on peut basculer vers une étiquette.
Allez les derniers :
ADC : ADd with Carry. Ajoute la valeur directe d'après à l'A.
SBC : SuBstract with Carry. Soustrait la valeur d'après à l'A.
CLC : Clear Carry flag
SEC : SEt Carry flag
Ces quatre opcodes doivent être associés pour éviter de drôles de résultats. En effet, ADC et SBC dépendent du "carry flag" : si le carry flag est mis en quelle que sorte sur ON (1), ADC ajoutera votre valeur à l'A + 1 ; si le carry flag est mis sur OFF (0), SBC ôtera votre valeur à l'A et enlèvera aussi 1. Pour éviter toute interférence, on utilise CLC avec ADC et SEC avec SBC.
Note : si l'addition dépasse #$FF ou est en dessous de #$00, le compteur reprend à l'exact opposé. Ex : #$FF + #$01 = #$00, #$FF + #$02 = #$ 01, #$00 - #$02 = #$FE, ... Donc #$100 = #$00 et #$-01 = #$FF. Quand les compteurs reprennent à ces valeurs, l'overflow flag esy mis sur ON.
Note 2 : les seules opérations possibles sont les additions/soustraction avec les registers. Les multiplications et divisions sont infaisables (sauf les multiplications et divisions rapides avec ASL et LSR). Ce qui nous amène donc aux...
Les boucles
Vous voulez augmenter telle valeur de, disons trente, INC ne marchera pas (à moins de mettre 30 INC ^^), et malheureusement on ne veut pas utiliser ADC. Que faire ? et bien on fait des boucles ! un petit exemple :
LDA $0DBF Met le nb de pièces dans l'A...
boucle : ...étiquette...
INC ...augmente de 1 l'A...
CMP #$70 ...comparer cette valeur avec #$70...
BNE boucle ...si ces deux valeurs ne sont pas égales, retourner à "boucle"...
STA $0DBF ...sinon réinsérer les pièces dans le compteur...
RTS ...retour.
Si on fait un bloc avec ce code, on a un bloc qui augmente les pièces de Mario jusqu'à 70, d'où l'inutilité de ADC qui ne peut pas régler le compteur de pièces sur un tel nombre.
Maintenant les boucles de multiplication :
PHY Sauvegarde Y dans le stack...
LDY $0DBF ...y met le nb de pièces...
LDA #$00 ...met 0 dans l'A...
boucle :
CLC ...carry flag = OFF...
ADC #$03 ...A + 3...
DEY ...diminue de 1 Y...
CPY #$00 ...comparer Y avec 0...
BNE boucle ...si les deux valeurs ne sont pas égales, aller à boucle...
STA #$0DBF ...sinon remettre le nb de pièces ds le compteur...
PLY ...remettre la valeur de Y...
RTS ...retour.
Ce code multiplie le nb de pièces par 3. En réalité, il augmente l'A de 3 autant de fois qu'il y a de pièces (le nb de Y), ce qui revient au même, non ?
Voila pour la première partie. Si vous avez déjà compris cela, ça ira tout seul après ^^.
Bon, pour arriver à concocter quelques custom blocs, il faut d’abord se munir de blocktool, de translhexion, du bloc-notes, de spritetool, bien sur de Lunar Magic et d’une rom de Super Mario World et enfin du fichier d’aide qui se trouve ici. Note : ayez sous les yeux la partie RAM Map de smw central là.
Comme ce tuto risque d’être un peu long, je vais le décomposer en 3 parties : les bases de l’ASM, créer son bloc et enfin des exemples de custom blocs.
1. Les bases de l’ASM (ou étude théorique)
D’abord, ‘y faut dire 65C816 ASM pour la snes (tuh…). Pour ceux qui ne connaissent pas cela veut dire ASseMbly(eur). C’est le langage informatique qu’utilise la snes. En réalité il existe plusieurs versions d'assembleur, une pour chaque processeur. Ici je vais vous apprendre l'ASM 65C816 car c'est celui qui nous intéresse. Quand vous ouvrez la rom avec Translhexion, vous voyez la forme hexadécimal de l’ASM (pour ceux qui ne savent pas comment utiliser translhexion ni blocktool, reportez vous aux tutos de cette discussion ; je suppose que vous savez ce qu’est l’hexadécimal puisque vous savez utiliser Lunar Magic…). On appelle cet ensemble de chiffre le "machine code" in english ou en français le langage machine. C'est le langage compréhensible par la snes. Pour pouvoir traduire l'ASM en fichier executable ou ici en fichier.bin qui contiendra le langage machine, il faut un compilateur, un logiciel qui traduira le fichier source dans lequel se trouve le code source (l'ASM est utilisé dans le code source). On dit que l'assembleur est un langage compilé car il est traduit une fois pour toute. Let's go !
Bon. Commençons par le commencement.
L’ASM est fait d’opcodes et de valeurs. Les opcodes correspondent à des actions à effectuer. Les valeurs, bah… ce sont des valeurs. Les valeurs peuvent être directes : un nombre, là comme ça, au hasard, 007 ^^ ; ou indirectes (nous allons voir ça après). Sachez qu’en faisant de l’ASM, vous avez à disposition des registers(res) : l’accumulator(eur), les X et Y registers, le stack, le processor status ou flag register (celui-là est particulier). Le plus important est l’accumulator : il vous permet de stocker 1 valeur mais peut être utilisé indéfiniment. Ca fait beaucoup de notion, nan ? un petit exemple :
LDA #$07
STA $7E0DBF
‘paniquez pas j’vous explique. LDA et STA sont des opcodes. #$07 est une valeur directe, $0DBF indirecte. Traduction : LDA veut dire LoaD Accumulator (je noterai accumulator A par la suite) et STA STore A. Les valeurs directes en hexadécimal sont précédées de #$ et en binaire de #%, et les valeurs indirectes sont simplement précédées de $ puisqu’elle viennent de la RAM.
Ce qui veut dire que ce code met dans l’A la valeur en héxa 07 et restitue la valeur de l’A à l’adresse 7E0DBF. Si on regarde dans la RAM Map de smwcentral, il s’agit du compteur de pièces. Si vous faites un bloc avec ce code (on verra ça après), le bloc remet le compteur des pièces à 7. Note pour les valeurs indirectes, le 7E n’est pas obligatoire, ainsi que les 2 zéros s’il y en a après : $7E0019 devient $19. Attention : on ne supprime pas les deux derniers zéros. Ex : $7E1900 ne sera jamais $19.
Eh ben là vous avez déjà appris...le tiers de cette partie ! on continue avec d’autres opcodes :
STZ : STore Zero to memory. STZ $0DBF -> met 0 à l’adresse 0DBF (Mario a plus de pièces ^^)
INC : INCrement A or memory. INC $0DBF -> augmente de 1 la valeur indirecte de l’adresse 0DBF (Mario gagne une pièce)
DEC : DECrement A or memory. DEC $0DBF -> diminue de 1 la valeur indirecte de l’adresse 0DBF (Mario perd une pièce)
Ca continue... En réalité il me semble qu’il y en a 92. Vous pouvez avoir une vue d’ensemble en ouvrant le fichier d’aide dont j’ai parlé au début (en anglais)
ASL : Left Shift A or memory. Multiplie par 2 l’A. Ex : A=15, ASL, A=30
LSR : Right Shift A or memory. Divise par 2 l’A. Note : si le résultat est avec une virgule (en gros si ça tombe en xx,5), on garde la partie entière (on garde xx).
TAX : Transfer A to X. Transfert la valeur de l’A au register X (j’expliquerai après)
TAX, TAY, TXY, TYA,... même principe
XBA : eXchange B and A. Echange le "high byte" (dans un nombre avec xxyy, xx est le high byte) avec le "low byte"(yy). Ex : A=1523, XBA, A=2315
Note sur les bits : 1 byte = 8bits = xx, 2 bytes = 16bits = xxxx, 3 bytes = 24bits = xxxxxx, etc...
REP : Reset Processor Status et SEP: SEt Processor status flag. Quand vous avez une adresse en 2 byte, les manips ne marcheront pas. Vous devez donc utiliser :
REP #$20 -> A passe en 16 bits
REP #$30 -> A, X et Y passent en 16 bits
REP #$10 -> X et Y passent en 16bits
SEP #$20 -> A retourne en 8 bits
SEP #$30 -> A, X et Y retournent en 8bits
SEP #$10 -> X et Y retournent en 8 bits
J'explique ce qu'est le processor status ou flag register : c'est un ensemble de 8 bits (ex : 01000101) dont chaque bit se met sur 0 ou 1 en fonction des opérations effectuées. On a :
N V M X D I Z C
N : negative flag V : overflow flag
M : "m" (?) flag X : "x" (?) flag
D : decimal flag I : interrupt flag
Z : zero flag C : carry flag
Bon je vous explique quand même X et Y : ils fonctionnent à peu près pareil que l'A, mais ils ont d'autres propriétés, notamment une très importante : l'indexing (le fait de pouvoir stocker plusieurs valeur à la fois (je ne vous l'expliquerais pas car je ne le comprend pas, mais sachez qu'il est SUPER utile dans les custom sprites)). Préférez l'A ^^. Cela dit cela peut être utile si vous voulez ne pas utilisez l'A pour telle raison. Après il y a le stack : il vous permet de stocker une valeur un moment, puis de la remettre ensuite. C'est comme une pile de livres : vous mettez un livre sur cette pile, vous effectuez n'importe quoi, mais après vous reprenez le livre.
Opcodes liés au stack :
PHA : PusH A. Prend un "livre" ou valeur de l'accumulator et le met sur le stack.
PLA : PuLl A. Remet à sa place le "livre" ou valeur déposée précédemment.
Voici maintenant 2 opcodes tout à fait inutile xD :
NOP : NO oPération. Celui ci ne fera strictement rien
STP : StoP the Clock. Celui la "freeze" le jeu : la partie se bloque.
Et maintenant des très importants :
CMP : CoMPare A with memory et BEQ : Branch if EQual, BNE : Branch if Not Equal
Un petit exemple va vous permettre de comprendre :
LDA $0DBF met le nb de pièces de mario dans l'A...
CMP #$20 ...compare cette valeur avec le nombre 20 (en décimal, 32)...
BNE retour ...si ces 2 valeurs ne sont pas égales, aller à l'étiquette retour...
INC $0DBF ...sinon, augmenter les pièces de 1...
retour :
RTS ...retourner au jeu.
Donc si vous n'avez pas 32 pièces, vous n'aurez pas une pièce en plus.
CMP associé à BEQ, BNE,... sont utilisé pour les branchements.
Quand vous utilisez CMP, on soustrait la valeur de l'A à la valeur derrière l'opcode : si les deux valeurs sont identiques, le zero flag est mis sur ON. BEQ branche alors si le zero flag = ON. C'est pourquoi vous pouvez voir des BEQ ou BNE tout seul : ils utilisent le zero flag directement.
Il existe aussi :
BCS (BGE) : Branch if GrEater than -> si valeur > A, carry flag = ON donc BCS branche si carry flag = ON
BCC (BLT) : Branch if Less Than -> branche si carry flag = OFF
BPL : Branch if Plus -> branche si negative flag = OFF
BMI : Branch if Minus -> branche si negative flag = ON
Les deux dépendent du negative flag : une valeur directe est dite positive si <#$80, et négative si >=#$80 (si vous voulez savoir : positive : de #%00000000 à #%01111111 ; négative : de #%10000000 à #%11111111)
BVC : Branch if oVerflow Clear -> branche si overflow flag = OFF
BVS : Branch if oVerflow Set -> branche si overflow flag = ON, c'est-à-dire s'ils dépassent #$FF ou #$00.
Ces deux-là en revanche dépendent de l'overflow flag ; je l'expliquerai dans la partie ADC/SBC.
Avez-vous remarqué ? J'ai mis RTS...
RTS : ReTurn from Subroutine. Cet opcode doit terminer chaque bloc. Pourquoi ? parce que ça dit à la snes de retourner aux actions possibles de Mario, au jeu...
Voyez cela comme ça : .....(jeu).............(Mario touche/frappe un bloc)->[action du bloc........RTS].......(jeu).....
Quand vous tapez un bloc, le jeu est "jumped", littéralement "sauté", par l'opcode :
JSR : Jump to SubRoutine.
JSL : Jump to Subroutine Long. Cet opcode utilisé dans un bloc bascule vers une autre subroutine (comme par exemple la routine ou Mario devient petit ou gros) ; les opcodes situés à l'adresse indiquée sont effectuées (s'il n'y en pas, c'est le gros bug). Vous pouvez trouver des subroutines dans la ROM Map de smwcentral (conseil : cliquez sur type pour les trier, elles se trouveront à la fin). Si vous voulez savoir comment ça marche, JSL met la location de votre code dans le stack et bascule vers ladite subroutine. Le RTL : Return to Subroutine Long à la fin de la subroutine repioche la localisation de votre code dans le stack. C'est le même principe avec RTS : le saut vers votre code a mis la localition "vers le jeu" dans le stack. RTS à la fin utilise alors cette donnée pour y retourner ; donc si vous mettez qqch dans le stack lors de votre code et que vous ne le reprenez pas, RTS va vous envoyer n'importe où !
Attention : l'adresse à mettre lors de l'utilisation de JSL est la snes adresse.
Ex :
JSL $00F5B7 bascule vers la subroutine qui blesse Mario
RTS
Ce bloc est donc comme une "black piranhe plant" ^^
JSR doit être utilisé avec RTS, et JSL avec RTL. Simple ?
JMP : JuMP
JML : JuMp Long
Idem sauf qu'on peut basculer vers une étiquette.
Allez les derniers :
ADC : ADd with Carry. Ajoute la valeur directe d'après à l'A.
SBC : SuBstract with Carry. Soustrait la valeur d'après à l'A.
CLC : Clear Carry flag
SEC : SEt Carry flag
Ces quatre opcodes doivent être associés pour éviter de drôles de résultats. En effet, ADC et SBC dépendent du "carry flag" : si le carry flag est mis en quelle que sorte sur ON (1), ADC ajoutera votre valeur à l'A + 1 ; si le carry flag est mis sur OFF (0), SBC ôtera votre valeur à l'A et enlèvera aussi 1. Pour éviter toute interférence, on utilise CLC avec ADC et SEC avec SBC.
Note : si l'addition dépasse #$FF ou est en dessous de #$00, le compteur reprend à l'exact opposé. Ex : #$FF + #$01 = #$00, #$FF + #$02 = #$ 01, #$00 - #$02 = #$FE, ... Donc #$100 = #$00 et #$-01 = #$FF. Quand les compteurs reprennent à ces valeurs, l'overflow flag esy mis sur ON.
Note 2 : les seules opérations possibles sont les additions/soustraction avec les registers. Les multiplications et divisions sont infaisables (sauf les multiplications et divisions rapides avec ASL et LSR). Ce qui nous amène donc aux...
Les boucles
Vous voulez augmenter telle valeur de, disons trente, INC ne marchera pas (à moins de mettre 30 INC ^^), et malheureusement on ne veut pas utiliser ADC. Que faire ? et bien on fait des boucles ! un petit exemple :
LDA $0DBF Met le nb de pièces dans l'A...
boucle : ...étiquette...
INC ...augmente de 1 l'A...
CMP #$70 ...comparer cette valeur avec #$70...
BNE boucle ...si ces deux valeurs ne sont pas égales, retourner à "boucle"...
STA $0DBF ...sinon réinsérer les pièces dans le compteur...
RTS ...retour.
Si on fait un bloc avec ce code, on a un bloc qui augmente les pièces de Mario jusqu'à 70, d'où l'inutilité de ADC qui ne peut pas régler le compteur de pièces sur un tel nombre.
Maintenant les boucles de multiplication :
PHY Sauvegarde Y dans le stack...
LDY $0DBF ...y met le nb de pièces...
LDA #$00 ...met 0 dans l'A...
boucle :
CLC ...carry flag = OFF...
ADC #$03 ...A + 3...
DEY ...diminue de 1 Y...
CPY #$00 ...comparer Y avec 0...
BNE boucle ...si les deux valeurs ne sont pas égales, aller à boucle...
STA #$0DBF ...sinon remettre le nb de pièces ds le compteur...
PLY ...remettre la valeur de Y...
RTS ...retour.
Ce code multiplie le nb de pièces par 3. En réalité, il augmente l'A de 3 autant de fois qu'il y a de pièces (le nb de Y), ce qui revient au même, non ?
Voila pour la première partie. Si vous avez déjà compris cela, ça ira tout seul après ^^.
Commentaire