[Tutorial] Pawn Amx.
#1

Pawn Amx.
Por rjjj ou Ken




O que й o arquivo de extensгo .amx que й gerado pela compilaзгo do cуdigo escrito em Pawn ? Para que serve a diretiva #emit ? Qual a relaзгo entre o programa samp-server e o arquivo .amx ?



O processamento tem sido desde muito tempo a parte mais difнcil da programaзгo em Pawn. Por essa razгo, cуdigos como #emit se tornaram sнmbolos dos maiores mestres .



Particularmente penso que isso tenha acontecido devido ao pouco conhecimento sobre o funcionamento do computador que й adquirido com o estudo dos manuais da linguagem.





1 - Computador.




A idйia do computador surgiu com o cientista Blaise Pascal, o mesmo elaborador do Teorema de Pascal que й estudado em Hidrуstбtica (Fнsica) no Ensino Mйdio .



Graзas a uma variaзгo de tensгo elйtrica (uma espйcie de "ligaзгo" que existe entre dois pontos de um campo elйtrico, espaзo onde uma partнcula com carga, como o elйtron, exerce sua forзa elйtrica) que ocorre nos circuitos do aparelho, surgem dois estados que sгo a base de tudo que o computador faz: ligado (true) e desligado (false).



Com a manipulaзгo desses estados, o denominado processador (popularmente chamado de CPU, que significa Unidade Central de Processamento em inglкs) consegue efetuar cбlculos aritmйticos (adiзгo, subtraзгo, etc) e lуgicos (AND, OR, etc).



Compatнvel com o processador, hб a memуria principal (popularmente conhecida como memуria RAM), que й o lugar onde ficam os dados de todos os programas que estгo sendo executados. Esses dados sгo os bits, que podem ser 0 ou 1, justamente devido а CPU .



O processador й composto por registradores, elementos que trabalham diretamente com os dados que ficam na memуria.



Quando vocк cria uma variбvel em uma linguagem de programaзгo, ela, durante o processamento (ou execuзгo) do programa, irб para a memуria principal e lб serб controlada pelos registradores do processador, que farгo acontecer o que vocк quis fazer com a variбvel.





2 - Mбquina Abstrata.




Pawn й uma linguagem de programaзгo de extensгo, isto й, tem o objetivo de estender as funcionalidades de um programa.



Por isso que quando um cуdigo pode ser escrito, a equipe SA-MP raramente disponibiliza uma funзгo padrгo que faзa a mesma coisa que ele .



Alйm disso, Pawn, assim como Java, й uma linguagem que usa uma mбquina abstrata (ou mбquina virtual), um programa que executa o arquivo criado por ela. Pode haver diversos motivos para isso, como tecnologia multiplataforma e otimizaзгo.



Logo, o arquivo .amx (acrфnimo para Abstract Machine Executor, executor da mбquina abstrata, ou seja, aquilo que й responsбvel pelas operaзхes da mesma) nгo й lido diretamente pelo sistema operacional como um arquivo .exe, e sim pela mбquina abstrata que vem embutida no programa samp-server.



O cуdigo que й lido pela mбquina abstrata й o bytecode (ou P-code), nada mais nada menos que o conteъdo do arquivo .amx e prуprio cуdigo Pawn apуs a compilaзгo .



A mбquina abstrata tambйm tem seu prуprio espaзo virtual na memуria. Nesse local й que todos os dados programados em Pawn sгo trabalhados.



Isso explica a relaзгo entre o samp-server e os arquivos .amx.





3 - Memуria.




Para o processador do computador, nomes de dados e outras informaзхes similares sгo insignificantes, pois eles sгo organizados na memуria de acordo com um sistema de endereзos.



O endereзo de uma variбvel й um nъmero utilizado para localizб-la na memуria. Cada byte (conjunto de 8 bits) tem seu prуprio endereзo:








Sendo cada nъmero em vermelho o endereзo do dado ao seu lado, a figura acima expressa a distribuiзгo de endereзos na memуria.



Nota-se que os endereзos sгo atribuнdos de forma crescente e conforme o total de bytes ocupados da memуria do computador. Isso significa que antes do dado de endereзo 1200000 jб existem outros dados, que podem ser atй de outro programa.



Em programaзгo, o nome de uma variбvel que contйm o endereзo de outra variбvel й ponteiro. Hб tambйm o termo referкncia, que й o mesmo que endereзo.



Isso й a parte geral.



Como eu disse anteriormente, a mбquina abstrata ocupa uma porзгo da memуria. Todos os endereзos que podem ser manipulados em Pawn sгo atribuнdos levando em conta apenas essa parte da memуria, ou uma seзгo dela.



Em outras palavras, jamais esses endereзos terгo alguma relaзгo com os de outros programas, simplesmente pelo fato de a linguagem Pawn nгo ter sido desenvolvida para a produзгo de aplicaзхes como os arquivos de extensгo .exe.



A memуria da mбquina abstrata й dividida em dois blocos, o estбtico e o dinвmico .



O estбtico й constituнdo de seзхes chamadas prefix, data e code. Jб o dinвmico engloba as seзхes stack e a heap:








Na figura, as seзхes foram colocadas nesta ordem pelo fato de haver uma de ligaзгo entre todas elas. Por exemplo, se a data aumenta de tamanho, o endereзo do topo da heap muda.



O bloco estбtico da memуria tem esse nome porque nгo se pode modificar o seu tamanho durante a execuзгo do .amx.



Entгo quer dizer que o tamanho do bloco dinвmico pode ser modificado ?



De certa forma sim, mas ele tem um tamanho fixo tambйm .



Na verdade, o que ocorre й que o bloco dinвmico tem um tamanho fixo definido na compilaзгo, mas nele hб um espaзo livre e um espaзo ocupado por dados. Estes que vгo sendo alterados no processamento.



Isso porque o que fica no bloco estбtico й construнdo durante a compilaзгo, e o que fica no dinвmico durante a execuзгo.




* Prefix



O prefix й a seзгo da memуria da mбquina abstrata na qual estгo armazenadas informaзхes de criaзгo de variбveis locais nгo declaradas com static, tamanho do bloco estбtico, nomes de funзхes declaradas com public e native, entre outras.




* Data



Na data ficam as variбveis globais, as declaradas com static (mesmo se forem locais) e as strings que sгo explicitamente passadas a funзхes.




* Code



Na seзгo code sгo colocadas todas as definiзхes de funзхes.




* Stack



A stack ou pilha й a mais complexa de todas as seзхes. Ela й a regiгo da memуria na qual ficam as variбveis locais nгo declaradas com static, os argumentos de funзхes, entre outros.



Essa estrutura funciona conforme a lei de LIFO, sigla para "Last In, First Out", que em portuguкs pode ser interpretado como "Ъltimo a Entrar, Primeiro a Sair".



Isso quer dizer que no cуdigo abaixo, primeiro a variбvel x seria construнda, depois a y e depois a z, mas no final da execuзгo da funзгo, primeiro a variбvel z seria destruнda, depois a y e por fim a x:


pawn Код:
main()
{
    new x;
    new y;
    new z;
    print("1");
    print("2");
    print("3");
}

Interessante, nгo ?



Duas operaзхes caracterizam a stack: a push, que й a adiзгo de um dado ao topo da stack, e a pop, que й a remoзгo do dado no topo da stack.



A stack tambйm pode guardar informaзхes de outras funзхes no chamado frame da funзгo. Por exemplo:


pawn Код:
main()
{
    new a;
    new b;
    Chamar();
}



stock Chamar()
{
    new c;
}

No caso acima, a stack da mбquina abstrata assumiria este formato:








No frame da funзгo main ficariam as variбveis a e b, e no da funзгo Chamar ficaria a variбvel c.



Pouco antes da funзгo Chamar ser executada, um dado especial й colocado na stack: o endereзo de retorno.



Й por meio dele que й possнvel voltar justamente а funзгo main logo apуs a Chamar ser lida.



Quando chega o fim de uma funзгo, as variбveis que foram criadas nela sгo destruнdas. No caso do exemplo acima, isso na stack seria expresso pela remoзгo do frame da funзгo Chamar e consequentemente a elevaзгo do frame do main ao topo da stack.



Outro ponto importante й o adicionamento de argumentos de funзхes na stack. Isso й feito na ordem inversa, entгo, no cуdigo abaixo, primeiro o nъmero 10 seria colocado na stack, depois o 20 e por fim o 30:


pawn Код:
main()
{
    Numeros(30, 20, 10);
}



stock Numeros(a, b, c)
{
    return 1;
}

E o principal sobre a stack й isso. Mais detalhes serгo abordados na parte do tutorial sobre #emit.




* Heap



A heap em si й pouco utilizada no SA-MP Pawn. Dentre os dados que vгo para a heap, destacam-se os valores padrхes de argumentos de funзхes que sгo arrays ou passados por referкncia.



Exemplo:


pawn Код:
stock Valores(x, &y = 20)
{
    printf("Valor em x: %d", x);
    printf("Valor em y: %d", y);
    return 1;
}


stock Outros(a[] = "Exemplo")
{
    printf("String em a: %s", a);
    return 1;
}

Se as funзхes acima fossem chamadas, o nъmero 20 e a string "Exemplo" seriam enviados para a heap.




Agora, lembra daquela figura que mostrava como й a memуria da mбquina abstrata ? Nгo sei se percebeu, mas nгo havia um traзo separando a stack e a heap .



Isso foi intencional, pois elas, diferentemente das outras seзхes, compartilham o bloco em que estгo.



Se a stack fica muito grande ela acaba se colidindo com a heap (stack overflow), o que causa o run-time erro "stack/heap collision". O mesmo acontece se heap ficar muito grande (heap overflow).



Outro colapsos sгo o stack underflow e heap underflow, que sгo causados por uma remoзгo de dados maior que a adiзгo.





4 - Registradores.




Os registradores sгo unidades que compхem a CPU. Os dados sгo movidos da memуria para eles a fim de serem processados.



A quantidade desses elementos varia dependendo do processador, porйm o nъmero de registradores que atuam com a mбquina abstrata й fixo .



Sгo os registradores especiais da mбquina abstrata:




* PRI



Esse й o registrador especial principal. Ele, junto com o registrador ALT, processa todos os cуdigos Pawn.




* ALT



Й o registrador alternativo. Tem a mesma funзгo do principal.




* DAT



Registrador que trabalha com o endereзo para o inнcio da seзгo data, sendo ele referente a ela, nгo а mбquina abstrata.




* COD



Registrador que trabalha com o endereзo para o inнcio da seзгo code, sendo ele referente a ela, nгo а mбquina abstrata.




* CIP



Registrador que trabalha com o endereзo para o local de uma funзгo o qual estб sendo lido no momento. Esse endereзo й relativo а seзгo code, nгo а mбquina abstrata.




* STP



Registrador que trabalha com o endereзo para o inнcio da seзгo stack, sendo ele referente a ela, nгo а mбquina abstrata.




* STK



Registrador que trabalha com o endereзo do dado da stack que estб sendo processado no momento, sendo ele referente а seзгo stack, nгo а mбquina abstrata.




* FRM



Registrador que trabalha com o endereзo para o fim do frame de funзгo que estб abaixo do frame que estб no topo da stack no momento, sendo esse endereзo referente а seзгo stack, nгo а mбquina abstrata.




* HEA



Registrador que trabalha com o endereзo para o inнcio da seзгo heap, sendo ele referente a ela, nгo а mбquina abstrata.




Atravйs da diretiva #emit pode-se controlar esses registradores e consequentemente o processamento do arquivo .amx.





5 - Amx Assembly.




Entre todas as linguagens de programaзгo, Assembly й considerada a de mais baixo nнvel, ou seja, a mais prуxima da linguagem de mбquina. Caracteriza-se por permitir a programaзгo minuciosa das aзхes de registradores.



O Amx Assembly й uma sublinguagem da linguagem Pawn, e й constituнdo pelos diferentes cуdigos que podem ser formados pela diretiva de prй-processador #emit .



Cada passo do processamento, isto й, cada operaзгo que a mбquina abstrata efetua й denominada instruзгo.



Em #emit, uma instruзгo й representada por sнmbolos chamados mnemфnicos. Dezenas destes estгo disponнveis no SA-MP Pawn, por isso os mais ъteis serгo vistos nesta parte do tutorial.



O parвmetro de um mnemфnico pode fazer uma coisa totalmente diferente dependendo se й ou nгo й uma variбvel ou funзгo, entгo organizei as explicaзхes de cada tipo.




* LOAD.pri ou LOAD.alt



Esse mnemфnico move o valor de um dado para o registrador PRI ou ALT, dependendo de qual for especificado.




* Seguido de um nъmero



Considera-o como o endereзo da variбvel da seзгo stack/heap cujo valor serб passado ao registrador. Esse endereзo seria relativo а seзгo stack/heap:


pawn Код:
#include <a_samp>


main()
{
    new a = 105;
    new b = 2005;
    new x, y;
    #emit LOAD.pri 16556  //Move para o registrador PRI o valor da variбvel "a".
    #emit LOAD.alt 16552  //Move para o registrador ALT o valor da variбvel "b".
    #emit STOR.S.pri x   //Armazena na variбvel "x" o valor em PRI.
    #emit STOR.S.alt y   //Armazena na variбvel "y" o valor em ALT.
    printf("Valor da variбvel a: %d", x);  //Mostra o nъmero 105.
    printf("Valor da variбvel b: %d", y);  //Mostra o nъmero 2005.
}

Note que para pegar o endereзo da variбvel b diminuimos em 4 o endereзo da variбvel a, que foi colocada antes dela na stack.



O raciocнnio de variar em 4 o endereзo de uma cell para obter os endereзos das cells que estгo ao seu redor na memуria pode ser aplicado a qualquer tipo de endereзo.




* Seguido de um nome



Considera-o como o nome do dado da seзгo data/code (uma variбvel global, por exemplo) que terб seu valor passado ao registrador:


pawn Код:
#include <a_samp>


new a = 125;


main()
{
    new b;
    #emit LOAD.pri a  //Move para o registrador PRI o valor da variбvel "a".
    #emit STOR.S.pri b  //Armazena na variбvel "b" o valor em PRI.
    printf("Valor da variбvel a: %d", b);  //Mostra o nъmero 125.
}



* LOAD.S.pri ou LOAD.S.alt



Tem a mesma funзгo do LOAD.pri ou LOAD.alt, mas й um pouco diferente.




* Seguido de um nъmero



Considera-o como o endereзo da variбvel da seзгo stack/heap que terб seu valor passado ao registrador especificado. Mas esse endereзo й relativo а funзгo na qual o #emit й utilizado.



Por exemplo:


pawn Код:
#include <a_samp>


main()
{
    Funcao();
}


stock Funcao()
{
    new x = 62;
    new y = 20;
    new a, b;
    #emit LOAD.S.pri 0xFFFFFFFC  //Move o valor da variбvel "x" para o registrador PRI.
    #emit LOAD.S.alt 0xFFFFFFF8  //Move o valor da variбvel "y" para o registrador ALT.
    #emit STOR.S.pri a  //Armazena na variбvel "a" o valor em PRI.
    #emit STOR.S.alt b  //Armazena na variбvel "b" o valor em ALT.
    printf("Valor da variбvel x: %d", a);  //Mostra o nъmero 62.
    printf("Valor da variбvel y: %d", b);  //Mostra o nъmero 20.
    return 1;
}

E esses 0xFFFFFFFC e 0xFFFFFFF8 ?



Eles sгo os endereзos das variбveis x e y, respectivamente. Sгo os mesmos -4 e -8 em notaзгo hexadecimal, mas nгo os coloquei assim porque o #emit nгo reconhece o sinal negativo .




* Seguido de um nome



Avalia-o como o nome do dado da seзгo stack/heap (uma variбvel local nгo declarada com static, por exemplo) que terб seu valor passado ao registrador:


pawn Код:
#include <a_samp>


main()
{
    Funcao();
}


stock Funcao()
{
    new i = 75;
    new o;
    #emit LOAD.S.pri i  //Move o valor da variбvel "i" para o registrador PRI.
    #emit STOR.S.pri o  //Armazena o valor em PRI na variбvel "o".
    printf("Valor da variбvel i: %d", o);  //Mostra o nъmero 75.
    return 1;
}



* STOR.pri ou STOR.alt



Armazena em uma variбvel o valor que estб em um registrador.




* Seguido de um nъmero



Considera-o como o endereзo da cell da seзгo data/code na qual serб armazenado o valor do registrador indicado. Esse endereзo й relativo а seзгo data/code.


pawn Код:
#include <a_samp>


new x;
new y;
new z;


main()
{
    #emit CONST.pri 123  //Move o nъmero 123 para o registrador PRI.
    #emit CONST.alt 321  //Move o nъmero 321 para o registrador ALT.
    #emit STOR.pri 4  //Armazena na variбvel "y" o valor em PRI.
    #emit STOR.alt 8  //Armazena na variбvel "z" o valor em ALT.
    printf("Valor da variбvel x: %d", x);  //Mostra o nъmero 0.
    printf("Valor da variбvel y: %d", y);  //Mostra o nъmero 123.
    printf("Valor da variбvel z: %d", z);  //Mostra o nъmero 321.
}



* Seguido de um nome



Avalia-o como o nome da cell da seзгo data/code na qual serб armazenado o valor de um registrador.



Por exemplo:


pawn Код:
#include <a_samp>


new x;
new y;
new z;


main()
{
    #emit CONST.pri 14  //Move o nъmero 14 para o registrador PRI.
    #emit CONST.alt 19  //Move o nъmero 19 para o registrador ALT.
    #emit STOR.pri y  //Armazena na variбvel "y" o valor em PRI.
    #emit STOR.alt z  //Armazena na variбvel "z" o valor em ALT.
    printf("Valor da variбvel x: %d", x);  //Mostra o nъmero 0.
    printf("Valor da variбvel y: %d", y);  //Mostra o nъmero 14.
    printf("Valor da variбvel z: %d", z);  //Mostra o nъmero 19.
}



* STOR.S.pri ou STOR.S.alt



Atua de forma similar ao STOR.pri ou STOR.alt.




* Seguido de um nъmero



Considera o nъmero da mesma forma que o LOAD.S.pri ou LOAD.S.alt, mas passa o valor do registrador para a variбvel em vez de da variбvel para o registrador:


pawn Код:
#include <a_samp>


main()
{
    Funcao();
}


stock Funcao()
{
    new i;
    new o;
    #emit CONST.pri 30  //Move o nъmero 30 para o registrador PRI.
    #emit CONST.alt 40  //Move o nъmero 40 para o registrador ALT.
    #emit STOR.S.pri 0xFFFFFFFC  //Armazena na variбvel "i" o valor em PRI.
    #emit STOR.S.alt 0xFFFFFFF8  //Armazena na variбvel "o" o valor em ALT.
    printf("Valor da variбvel i: %d", i);  //Mostra o nъmero 30.
    printf("Valor da variбvel o: %d", o);  //Mostra o nъmero 40.
    return 1;
}


* Seguido de um nome



Avalia o nome de maneira idкntica ao LOAD.S.pri ou LOAD.S.alt, mas passa o valor do registrador para a variбvel em vez de da variбvel para o registrador:


pawn Код:
#include <a_samp>


main()
{
    Funcao();
}


stock Funcao()
{
    new a;
    new b;
    #emit CONST.pri 60  //Move o nъmero 60 para o registrador PRI.
    #emit CONST.alt 70  //Move o nъmero 70 para o registrador ALT.
    #emit STOR.S.pri a  //Armazena na variбvel "a" o valor em PRI.
    #emit STOR.S.alt b  //Armazena na variбvel "b" o valor em ALT.
    printf("Valor da variбvel a: %d", a);  //Mostra o nъmero 60.
    printf("Valor da variбvel b: %d", b);  //Mostra o nъmero 70.
    return 1;
}



* CONST.pri ou CONST.alt



Pode igualar o valor em um registrador a um nъmero ou pode pegar o endereзo de um dado.




* Seguido de um nъmero



Iguala o registrador ao nъmero especificado:


pawn Код:
#include <a_samp>


main()
{
    new u;
    #emit CONST.pri 9200  //Move o nъmero 9200 ao registrador PRI.
    #emit STOR.S.pri u  //Armazena o valor em PRI na variбvel "u".
    printf("Valor da variбvel u: %d", u);  //Mostra o nъmero 9200.
}



* Seguido de um nome



Trata-o como o nome de um dado cujo endereзo serб passado ao registrador. Se esse dado for da seзгo stack/heap, o endereзo й relativo а funзгo na qual #emit for utilizado e se for da seзгo data/code, o endereзo й relativo а essa seзгo.



Exemplo, se da seзгo stack/heap:


pawn Код:
#include <a_samp>


main()
{
    Funcao();
}


stock Funcao()
{
    new x;
    new y;
    new a;
    new b;
    #emit CONST.pri x  //Move o endereзo da variбvel "x" para o registrador PRI.
    #emit CONST.alt y  //Move o endereзo da variбvel "y" para o registrador ALT.
    #emit STOR.S.pri a  //Armazena na variбvel "a" o endereзo em PRI.
    #emit STOR.S.alt b  //Armazena na variбvel "b" o endereзo em ALT.
    printf("Endereзo da variбvel x: %d", a);  //Mostra o nъmero -4.
    printf("Endereзo da variбvel y: %d", b);  //Mostra o nъmero -8.
    return 1;
}

Exemplo, se da seзгo data/code:


pawn Код:
#include <a_samp>


new a;
new b;


main()
{
    new c, d;
    #emit CONST.pri a  //Move o endereзo da variбvel "a" para o registrador PRI.
    #emit CONST.alt b  //Move o endereзo da variбvel "b" para o registrador ALT.
    #emit STOR.S.pri c  //Armazena na variбvel "c" o endereзo em PRI.
    #emit STOR.S.alt d  //Armazena na variбvel "d" o endereзo em ALT.
    printf("Endereзo da variбvel a: %d", c);  //Mostra o nъmero 0.
    printf("Endereзo da variбvel b: %d", d);  //Mostra o nъmero 4.
}



* ADDR.pri ou ADDR.alt



Move o endereзo de uma variбvel ao registrador indicado.




* Seguido de um nъmero



Ainda nгo descoberto.




* Seguido de um nome



Envia ao registrador o endereзo de um dado da seзгo stack/heap ou da seзгo data/code. Esse endereзo й relativo а seзгo stack/heap:


pawn Код:
#include <a_samp>


main()
{
    new x = 184;
    new y;
    #emit ADDR.pri x  //Move o endereзo da variбvel "x" para o registrador PRI.
    #emit STOR.S.pri y  //Armazena o endereзo em PRI na variбvel "y".
    printf("Endereзo da variбvel x: %d", y);  //Mostra o nъmero 16472.
}



* LCTRL



Esse й sem dъvidas й um dos mais importantes mnemфnicos de todos .




* Seguido de um nъmero



Ele envia ao registrador PRI (nгo й possнvel especificar o ALT) o valor que estб em um dos outros registradores especiais da mбquina abstrata.



Isso dependerб do nъmero que й parвmetro:



Код:
0  -  Registrador COD.
1  -  Registrador DAT.
2  -  Registrador HEA.
3  -  Registrador STP.
4  -  Registrador STK.
5  -  Registrador FRM.
6  -  Registrador CIP.

Exemplo:


pawn Код:
#include <a_samp>


main()
{
    new x;
    #emit LCTRL 3  //Move ao registrador PRI o valor no registrador STP.
    #emit STOR.S.pri x  //Armazena o valor em PRI na variбvel "x".
    printf("Endereзo do inнcio da stack: %d", x);  //Mostra o nъmero 16508.
}


* Seguido de um nome



Ainda nгo descoberto.





* SCTRL



Mnemфnico capaz de modificar o valor de certos registradores especiais de acordo com o que estб no registrador PRI.




* Seguido de um nъmero



O nъmero parвmetro determina o registrador a ser alterado:


Код:
2  -  Registrador HEA.
4  -  Registrador STK.
5  -  Registrador FRM.
6  -  Registrador CIP.

Exemplo:


pawn Код:
#include <a_samp>


main()
{
    #emit LCTRL 6  //Move ao registrador PRI o valor no registrador CIP.
    #emit ADD.C 52  //Aumenta em 52 o valor de PRI.
    #emit SCTRL 6  //Iguala o registrador CIP ao registrador PRI.
    print("1");
    print("2");
}

Graзas б mudanзa no registrador CIP, a primeira mensagem seria "pulada", fazendo com que apenas a segunda fosse mostrada.




* Seguido de um nome



Ainda nгo descoberto.




* XCHG



Troca o valor do registrador PRI pelo do registrador ALT e vice-versa :


pawn Код:
#include <a_samp>


main()
{
    #emit CONST.pri 50  //Move o nъmero 50 para o registrador PRI.
    #emit CONST.alt 100  //Move o nъmero 100 para o registrador PRI.
    #emit XCHG  //Troca os valores dos registradores. PRI depois disso contйm 100 e ALT contйm 50.
}



* PUSH.C



Cria uma cell e a coloca na stack da mбquina abstrata.




* Seguido de um nъmero



Avalia-o como o valor que serб armazenado pela cell criada.



Exemplo:


pawn Код:
#include <a_samp>


main()
{
    #emit PUSH.C 5  //Cria uma cell e armazena na mesma o nъmero 5.
    #emit STACK 4  //Destrуi a cell criada anteriormente.
}



* Seguido de um nome



Avalia-o como o nome da variбvel cujo endereзo relativo а funзгo na qual #emit for usado serб armazenado pela cell criada:


pawn Код:
#include <a_samp>


main()
{
    new x = 5;
    new y = 7;
    #emit PUSH.C x  //Cria uma variбvel armazenando nela o valor -4 e a coloca na stack.
    #emit PUSH.C y  //Cria uma variбvel armazenando nela valor -8 e a coloca na stack.
    #emit STACK 8  //Remove as cells criadas com #emit.
}



* PUSH.S



Tambйm cria uma cell e a coloca na stack da mбquina abstrata.




* Seguido de um nъmero




Ainda nгo descoberto.




* Seguido de um nome



Considera-o como o nome da variбvel da qual o valor serб armazenado na cell criada.



Exemplo:


pawn Код:
#include <a_samp>


main()
{
    new x = 5;
    new y = 7;
    #emit PUSH.S x  //Cria uma cell armazenando nela o nъmero 5 e a coloca na stack.
    #emit PUSH.S y  //Cria uma variбvel armazenando nela o nъmero 7 e a coloca na stack.
    #emit STACK 8  //Remove as cells criadas com #emit.
}



* PUSH.ADR



Outro mnemфnico para a criaзгo de cells na stack.




* Seguido de um nъmero



Ainda nгo descoberto.




* Seguido de um nome



Cria uma cell ponteiro armazenando o endereзo da variбvel que tem o nome parвmetro:


pawn Код:
#include <a_samp>


main()
{
    new a[] = "String";
    #emit PUSH.ADR a  //Cria uma variбvel armazenando o endereзo da variбvel "a" e a coloca na stack.
    #emit STACK 4  //Remove a variбvel criada anteriormente com #emit.
}



* STACK



Remove bytes da stack da mбquina abstrata. Deve-se usб-lo em alguns casos para retirar dados criados por #emit que acabam nгo sendo destruнdos automaticamente.




* Seguido de um nъmero



Trata-o como o total de bytes a serem removidos atravйs da operaзгo pop da stack:


pawn Код:
#include <a_samp>


main()
{
    #emit PUSH.C 1  //Cria uma variбvel na stack contendo o nъmero 1.
    #emit PUSH.C 2  //Cria uma variбvel na stack contendo o nъmero 2.
    #emit PUSH.C 3  //Cria uma variбvel na stack contendo o nъmero 3.
    #emit STACK 12  //Remove 12 bytes da stack, ou seja, destrуi as ъltimas 3 cells que entraram nela.
}



* Seguido de um nome



Ainda nгo descoberto.




Fora esses hб mnemфnicos especializados em cбlculos matemбticos, formaзгo de estruturas condicionais, entre outros.



A grande maioria desses, entretanto, pode ser substituнda por outros cуdigos escritos em Pawn .



Uma lista completa deles pode ser encontrada no manual Pawn Implementer's Guide.





6 - Funзхes e o Amx.




Para chamar uma funзгo nativa (obtida com native) por meio do #emit primeiramente deve-se ter certeza de que ela apareceu alguma vez antes no GM ou FS, caso contrбrio ela nгo serб registrada na tabela de funзхes nativas do arquivo .amx e consequentemente nгo poderб ser utilizada.



Se a funзгo nativa nгo tem parвmetros, basta colocar seu nome ao lado do mnemфnico SYSREQ.C no #emit:


pawn Код:
public OnGameModeInit()
{
    #emit SYSREQ.C DisableInteriorEnterExits  //Chama a funзгo nativa "DisableInteriorEnterExits".
    return 1;
}

Se tiver parвmetros, cria-se os argumentos da funзгo a ser chamada e os coloca na stack na ordem inversa, ou seja, primeiro o ъltimo e por fim o primeiro.



Em seguida adiciona-se uma cell armazenando o nъmero de argumentos colocados multiplicado por 4, que й o tamanho de uma cell em bytes, e por fim limpa-se a stack:


pawn Код:
public OnPlayerConnect(playerid)
{
    SendClientMessage(playerid, 0x33CCFFAA, "Texto 1");
    new ID = playerid;
    new Cor = 0x33FF00FF;
    new Mensagem[] = "Texto 2";
    #emit PUSH.ADR Mensagem  //Cria uma cell com o endereзo da variбvel array "Mensagem" e a coloca na stack. A razгo disso й o fato de variбveis arrays sempre serem passadas por referкncia em funзхes.
    #emit PUSH.S Cor  //Cria uma cell com o mesmo valor da variбvel Cor e a coloca na stack.
    #emit PUSH.S ID  //Cria uma cell com o mesmo valor da variбvel ID e a coloca na stack.
    #emit PUSH.C 12  //Tendo passado todos os argumentos na ordem inversa, cria-se mais uma cell contendo o total deles multiplicado por 4.
    #emit SYSREQ.C SendClientMessage  //Chama a funзгo nativa "SendClientMessage".
    #emit STACK 16  //Nesse caso temos que remover manualmente os dados colocados por #emit. Como foram criadas 4 cells com essa diretiva, ou seja, 16 bytes, removemos 16 bytes da stack.
    SendClientMessage(playerid, 0xFF0000FF, "Texto 3");
    return 1;
}

Quanto аs funзхes nгo-nativas, й um pouco mais complicado executб-las devido а necessidade de manipulaзгo do registrador CIP. A definiзгo das mesmas deve ficar acima dos locais nos quais elas sгo chamadas.



A tйcnica consiste em desviar a orientaзгo do processamento para dentro da definiзгo da funзгo e depois para o local de onde esta foi chamada. Para isso, deve-se considerar mnemфnicos e seus parвmetros tendo 4 bytes cada .



Na ausкncia de parвmetros na funзгo, faz-se o seguinte:


pawn Код:
#include <a_samp>


stock Mensagem()
{
    printf("Mensagem");
    return 1;
}


main()
{
    print("Inнcio");
    #emit LCTRL 6  //Envia o valor do registrador CIP para o registrador PRI.
    #emit ADD.C 36  //Aumenta em 36 o valor em PRI.     ADD.C = 4 bytes.   36 = 4 bytes.
    #emit PUSH.C 0  //Cria uma cell com o nъmero de argumentos da funзгo multiplicado por 4.     PUSH.C = 4 bytes.   0 = 4 bytes.
    #emit PUSH.pri  //Cria uma cell com o valor em PRI.    PUSH.pri = 4 bytes.
    #emit CONST.pri Mensagem  //Pega o endereзo da funзгo "Mensagem".    CONST.pri = 4 bytes.  Mensagem = 4 bytes.
    #emit SCTRL 6  //Iguala o registrador CIP ao registrador PRI. Isso desvia a orientaзгo do processamento para o interior da funзгo "Mensagem" atravйs de seu endereзo.    SCTRL = 4 bytes.  6 = 4 bytes.
    print("Fim");
}

Aquela segunda cell que й criada no meio do cуdigo acima contйm o endereзo de retorno (lembra dele ?) da funзгo chamada, que por sua vez й igual a: (valor que estava em CIP + nъmero de bytes de cуdigo da linha do ADD.C atй a linha do SCTRL).



O fato de ser atй a parte do SCTRL pode ser explicado por ele ser o responsбvel pelo desvio para a funзгo.



Graзas ao endereзo de retorno, a execuзгo de cуdigos, apуs a leitura da funзгo, continua a partir do local em que esta foi chamada.



Funзхes nгo-nativas com parвmetros sгo uma mistura das nгo-nativas sem parвmetros e das nativas com parвmetros:


pawn Код:
#include <a_samp>


stock Numeros(x, y)
{
    printf("Nъmeros %d e %d", x, y);
    return 1;
}


main()
{
    print("Inнcio");
    #emit LCTRL 6  //Envia o valor do registrador CIP para o registrador PRI.
    #emit ADD.C 52  //Aumenta em 52 o valor em PRI.     ADD.C = 4 bytes.   52 = 4 bytes.
    #emit PUSH.C 90  //Cria uma cell para o segundo argumento da funзгo "Numeros".   PUSH.C = 4 bytes.  90 = 4 bytes.
    #emit PUSH.C 45  //Cria uma cell para o primeiro argumento da funзгo "Numeros".   PUSH.C = 4 bytes.  45 = 4 bytes.
    #emit PUSH.C 8  //Cria uma cell para o nъmero de argumentos multiplicado por 4.  PUSH.C = 4 bytes.  8 = 4 bytes.
    #emit PUSH.pri  //Cria uma cell com o valor em PRI.    PUSH.pri = 4 bytes.
    #emit CONST.pri Numeros  //Pega o endereзo da funзгo "Numeros".    CONST.pri = 4 bytes.  Numeros = 4 bytes.
    #emit SCTRL 6  //Iguala o registrador CIP ao registrador PRI. Isso desvia a orientaзгo do processamento para o interior da funзгo "Numeros" atravйs de seu endereзo.    SCTRL = 4 bytes.  6 = 4 bytes.
    print("Fim");
}


E este й o fim do tutorial.




Espero ter ajudado .
Reply
#2

LOOL.
Tem paciкncia em..


Muito bom. Parabйns. .



First-q
Reply
#3

Vei. Na Boa.
Reply
#4

Ken,Conheзo vocк desdo inicio do Brasil samp forever,veenho comparando vc nakela epoca e vejo que vc evoluiu muito cara,Uma dica para vocк Tente deixar seus tutoriais menores,mesmo que ja esteja resumido sugiro deixar um pouco menor,porque muita gente ao ver um tutorial desse tamanho nao chega a ler nem а 20% dele,Entao cara,bom tutorial ! Parabйns de verdade
'
'
'
'
'
'


Qual й a finalidade desse tutorial?
Reply
#5

Nem li o tutorial todo, mas todos tutoriais que vocк faz sгo уtimos, e pelo que vi esse й mais um tutorial muuuito bem feito!

Parabйns!
Reply
#6

Realmente, уtimo tutorial, e como ele disse, tem paciencia pra pegar todas essas informaзхes
Reply
#7

Уtimo tutorial, muito interessante mesmo rjj!

atй tirei algumas dъvidas, parabйns!

+rep!
Reply
#8

Magnific

+rep
Reply
#9

Reply
#10

Muito bom, agora temos a tal explicaзгo sobre emit
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)