Postagens mais visitadas

Documentação ~ NEXTBasic Compiler


Compilador:
 -> Declaração de variáveis
 -> Atribuindo valores 
 -> Laços de Repetição
 -> Teste Condicional 
 -> Equal
 -> Sub Rotinas
 -> Funções
 -> Importando arquivos Externos
 -> Incluindo Assembly no Source Code
 -> Manipulando Bits e Posições de Memoria
 -> Interrupções, Exceptions e Trap Vectors
 

Biblioteca Genesis_std:

 -> genesis_header.asm
 -> Std_init
-> Vdp_set_config
 -> Joypad6B_read
 -> Wait_vblank
 -> Load_tiles_DMA
 -> Load_tiles_DMA_128ksafe
 -> Load_cram_DMA
 -> Load_cram_DMA_128ksafe
 -> Draw_tile
 -> Draw_tilemap
 -> Set_sprite_gfx
 -> Set_sprite_size
 -> Set_sprite_position
 -> Update_sprite_table
 -> Set_VerticalScroll_position
 -> Set_HorizontalScroll_position
 -> Hscroll_strip8
 -> Hscroll_line
 -> Update_Hscroll_table
 -> Enable_global_int
 -> Disable_global_int
 -> Enable_V_int
 -> Disable_V_int
 -> Enable_H_int
 -> Disable_H_int
 -> Enable_Ext_int
 -> Disable_Ext_int
 -> Enable_Display
 -> Disable_Display
 -> Set_Hint_counter
 -> Direct_color_DMA


 <- Declaração de Variáveis ->

  Uma Varivel pode ser declarada formalmente por meio do comando "Dim", seguindo a estrutura:

 Dim Nome_Da_Variável as "Tipo"

 Dim var1 as integer 'Por exemplo


 Sendo que o tipo pode ser:
    -> Byte - Unsigned de 8 Bits
    -> Word/Integer - Unsigned de 16 Bits
    -> Fixed - (variavel unsigned de 16 bits e ponto fixo que varia de 0.00 até 511.99)
    -> Long -  Unsigned 32 Bits
    -> String - 32 Bits (funciona como um ponteiro armazenando o endereço da cadeia de caracteres)

  Variáveis Signed são suportadas, bastando adicionar "signed" logo após o tipo da variável, exceto para os tipos string e fixed.

  Também é possível declarar Matrizes usando os símbolos "[" e "]" para armazenar os valores correspondentes ao tamanho da Matriz.

  Dim matriz[10] as word  ' Cria uma Matriz de 10 elementos (0-9 elementos) do tamanho Word

  Dim matriz[10,10] as word ' Cria uma Matriz  bidimensional de 100 elementos do tamanho Word 

  Também podemos criar Matrizes cujo os elementos podem ser de tipos diferentes e são indexados por um identificador, para isso usamos a estrutura "Enum" para nomear e definir a estrutura da matriz.

Enum matriz_indexada
  x as integer
  y as integer
  z as long
end Enum

  E na hora de fazer a declaração formal adicionamos a tag "new" antes de especificar o tipo da matriz, e o acesso a cada elemento é feito usando "." + Nome do identificador de cada elemento

  Dim nova_matriz as new Matriz_indexada

  nova_matriz.x = 50

  Essa estrutura também comporta vetores (matrizes unidimensionais) na sua declaração:

  Dim nova_matriz[100] as new Matriz_indexada
 
  No Menu Ferramentas-> Opções é possível Ligar/Desligar a declaração automática de variáveis.

  Com a opção de declaração automática de variáveis Ligada, caso o programa faça referencia a um identificador que não tenha sido previamente declarado, ele é declarado automaticamente como uma variável do tipo Word Unsigned.

  No menu de opções também é possível especificar o intervalo de endereços da memoria RAM onde será alocado o Heap, por padrão temos os endereços do Mega Drive &HFF0000 &HFFFFFF).

  O NextBasic suporta tanto variáveis Globais quanto Locais, sendo que toda variável declarada dentro de uma função/Sub Rotina será local a menos que usemos a Tag "Global" ao invéz de "Dim" em sua declaração, variáveis locais são alocadas no Stack e serão destruídas assim que função/subrotina se encerrar. Qualquer variável declarada fora de uma Função/Subrotina será visível em qualquer parte do código fonte a partir do ponto onde ela é declarada.
 
  Para fazer com que uma variável seja visível em qualquer parte do código fonte (inclusive em arquivos de código fonte externos) é necessário declara-la como global, substituindo a tag "dim" por "Global" na linha onde ocorre a declaração da variável.

  O registrador usado como Frame Pointer para alocar memoria no Stack é o Registrador A6.

  Além disso ainda é possível declarar variáveis e "linkar-las" a um endereço especifico da memoria, esse recurso foi incluído para permitir ao programador manipular registradores do sistema de maneira mais cômoda, isso pode ser feito usando a tag "Sysreg".

Sysreg VDP_CONTROL_REG as integer at &hC00004



  <- Atribuição de valores ->

  A atribuição de valores é feita especificando uma variável, um operador de atribuição e uma expressão matemática.

  Dentro das expressões matemáticas temos os seguintes operadores matemáticos/booleanos:

+    - Soma
-     - Subtração
*    - Multiplicação
/     - Divisão com maior precedência que a multiplicação (é realizada antes da multiplicação)
\     - Divisão com menor precedência que a multiplicação (é realizada depois da multiplicação)
~    - Not Bitwise
<<  - Deslocamento para a esquerda
>>  - Deslocamento para a direita
and - Operador bitwise and
or   - Operador bitwise or
xor - Operador bitwise xor (or exclusiva)
not - Operador 'Not' retorna um valor verdadeiro caso o valor testado seja falso
>    - Operador maior (retorna verdadeiro se o termo da esquerda for maior que o da direita)
<    - Operador menor (retorna verdadeiro se o termo da esquerda for menor que o da direita)
<>  - Operador diferente (retorna verdadeiro se o termo da esquerda for diferente ao da direita)
=    - Operador igual (retorna verdadeiro se o termo da esquerda for igual o da direita)
>=  - Operador maior/igual (retorna verdadeiro se o termo da esquerda for maior/igual o da direita)
<=  - Operador menor/igual (retorna verdadeiro se o termo da esquerda for menor/igual o da direita)

   E os operadores de atribuição podem ser:

  =  Igual, resolve a expressão matemática e atribui o valor a variável
+=  Mais Igual, resolve a expressão matemática e soma ao valor a variável
-=   Menos  Igual, resolve a expressão matemática e subtrai do valor a variável
*=  Mult. Igual, resolve a expressão matemática e multiplica pelo valor a variável
/=   Div Igual, resolve a expressão matemática e divide com o valor a variável
\=   Div Igual, resolve a expressão matemática e divide com o valor a variável
|=   OR igual, resolve a expressão matemática e realiza a operação Or com o valor a variável
&= AND igual, resolve a expressão matemática e realiza a operação and com o valor a variável 
^=  XOR Igual, resolve a expressão matemática e realiza a operação xor com o valor a variável
~= NOT Igual, resolve a expressão matemática, inverte o valor e salva na variável.
<<= LShift Igual, resolve a expressão matemática e desloca para a esquerda o valor da variável
>>= RShift Igual, resolve a expressão matemática e desloca para a direita o valor da variável

 Exemplo de uso:

 var <<= 2 + 3  Equivalente à     var = var << (2 + 3) 
 x &= 256         Equivalente à     x = x and 256  
 
  A precedência de operadores existe, sendo que as quatro operações matemática sempre tem a maior precedência em comparação com os outros operadores. Então na expressão

 x =  y AND x+2 

x será somado com 2 e então será realizada a operação AND com Y.  Já na expressão:

x = ~ x + y

x será somado com y e o resultado sera invertido.

A precedência exata dos operadores é dada pela ordem: OR, XOR, AND, NOT(), ~, 'operadores de comparação', operações de shift '<<' '>>', +, -, MOD, \, *, /.  
(sendo que os últimos operadores dessa lista tem a maior precedência, então são executados primeiro).

  O uso de parênteses para estabelecer a ordem de precedência dos cálculos é incentivado, já que isso não afeta o desempenho do código gerado (na verdade é até bom, pois evita a saturação dos registradores em calculos com multiplos operadores associativos). 

Quando uma operação de atribuição é iniciada o compilador avalia o data type da variável onde será salvo o resultado, e utilizara esse data type como referencia para resolver a expressão matemática.
  
  Logo, se misturarmos variáveis de data types diferentes na expressão o compilador fara a conversão entre eles automaticamente, então variáveis com um data wide menor serão estendidas (ao salvar uma variável byte de 8 bits em uma variável word de 16 bits por exemplo), e variáveis com um data wide maior terão sua parte excedente descartada, então ao salvar uma variável long de 32bits em uma variável word de 16 bits os 16 bits mais significativo da variável long serão descartados.  

  O mesmo se sucede para variáveis Fixed, ao tentar salvar uma variável fixed em uma variável word, por exemplo, o valor dela será convertido para inteiro automaticamente.
  O inverso também é valido, ao tentar salvar uma variável inteira em uma variável fixed a conversão é feita automaticamente.

 A conversão é feita sempre descartando o valor após o ponto no Momento em que a variavel é acessada
Ex.:
1
2
3
4
   dim _fixed_1 as fixed = 3.5
   dim _fixed_2 as fixed = 8.5
	   
   dim var_int as integer = _fixed_1 + _fixed_2   'Neste caso o valor salvo em var_int é 11, resultado de 8 + 3
   Para preservar o calculo usando o data type original das variáveis que compões a equação, neste caso, pode se usar a função _fixed(), assim o compilador vai calcular a equação usando a matemática de ponto fixo e converterá o Resultado retornado pela função para inteiro antes de salvar na variável de destino.
1
 dim var_int as integer = _fixed( _fixed_1 + _fixed_2 ) '  Neste caso o valor salvo em var_int é 12, resultado de 8.5 + 3.5
   
  A conversão automatica de valores Não é aplicada a operadores Bitwise (AND, OR, XOR, ~, <<, >>, etc.) apenas a operadores matematicos (+, -, *, /) e de comparação (= , > , <> , >=, etc.)
  

  <- Laços de repetição ->

 Existem três estruturas com laços de repetição, a estrutura FOR e a estrutura WHILE e a Estrutura DO.

  A Estrutura DO consiste num laço de repetição infinito.

Do

'[...] Laço de repetição infinito

Loop

  A unica maneira de sair desse laço de repetição é por meio do comando "Exit Do"


  A estrutura While( ) consiste num laço de repetição que se mantem enquanto a expressão matemática dentro dos parenteses for verdadeira (verdadeiro é sempre qualquer coisa diferente de zero).
  Ela pode ser utilizada para executar um bloco de código:

While()

'[...] Laço de repetição

wend
 
  Ou em sua maneira "in_line" que executa apenas um comando inserido na mesma linha logo apos a estrutura while():

 while( <expressão_matematica> ) <comando_a_executar>

  É possível sair do laço de repetição While a qualquer momento usando o comando "Exit While"

  O laço de repetição For atribui um valor a uma variável por meio de uma expressão matemática e especifica um valor de referencia, a cada execução do laço a variável sera incrementada e o laço se repetira enquanto o valor armazenado na variável for diferente do valor de referencia.

For i=0 to 30

'[...] O código sera executado enquanto i for diferente de 30
'[...] Ao termino da execução do laço a variável 'i' sera incrementada
Next i

  Também é possível usar uma expressão matemática para especificar que valor sera incrementado a variável de teste utilizando a tag 'step'.

For i=0 to -10 step -1

'[...]O valor -1 sera somado a variável 'i'
'[...] O laço se repetirá enquanto i for diferente de -10

Next i


  É possível sair do laço for a qualquer momento usando o comando "Exit For".


<- Teste Condicional ->

  O compilador Next Basic possui duas estruturas de teste condicional, a estrutura Select e a Estrutura If.

  A estrutura Select serve para comparar o resultado de uma expressão matemática com vários valores especificados numa lista:

 Select <expressao_avaliada>

 case <expressao 1>
       '[...]Código a Executar caso o resultado da expressão 1 seja igual o resultado da expressão avaliada
 case <expressao 2>
       '[...]Código a Executar caso o resultado da expressao 2 seja igual o resultado da expressao avaliada  
   ...
 case <expressao n>
      '[...]Código a Executar caso o resultado da expressão n seja igual o resultado da expressão avaliada 
case else
     '[...]Código a executar caso nenhum resultado das expr. anteriores seja igual ao da expr. avaliada

End Select


  A estrutura "Case Else" é opcional e caso um dos resultados seja verdadeiro a estrutura executa o bloco de código equivalente e depois se encerra sem testar os valores posteriores.  Exemplo:


select x
  
case 1
  '...
case 2
  '...
case 5
  '...
case else
  '...
end select
 
 Se o valor de X for igual a 2, o valor 5 não é nem sequer testado, se o valor de X não for igual a nenhum dos valores testados o bloco de código correspondente ao "case else" sera executado.


  A Estrutura IF resolve a expressão matemática e testa se o valor é verdadeiro (verdadeiro é igual a Diferente de Zero), se o valor for verdadeiro então o bloco de código é executado, se não, o programa segue em frente ( e executa o bloco de código depois do "Else" caso ele exista), é possivel usar essa estrutura de maneira "in_line" tambem.

' Usando a estrutura If

if x > y then
'[...]
end if

' Usando a estrutura If In_line
If x>y then <um comando apenas>

' Usando a estrutura If / Else

if x > y then
  '[...]
else
'  [...]
end if

' Usando a estrutura If / Else in_line
If x>y then  <um comando apenas> else <um comando apenas>


  Também é possível aninhar vários testes de condição de maneira que caso um teste seja falso, ele pule automaticamente para o próximo, assim que uma condição verdadeira for encontrada o bloco se encerra sem testar as demais, a estrutura "Else" é opcional em ambos os casos.

 if x = 1 then
     '[...]
elseif x= 2 then
     '[...]
elseif x= 3 then
     '[...]
elseif x= 4 then
     '[...]
else
     '[...]
end if


<- Equal ->

  O comando Equal serve como uma "Alias" permitindo especificar um identificador que sera substituído por uma string na hora da compilação, isso é útil para nomear valores constante. Exemplo

 Equal Gravidade "10"

 Direcao_y += Gravidade 

  Neste caso acima o compilador ira enxergar

 Direcao_y += 10



 <- Sub Rotinas -> 

 Sub Rotinas são trechos de código para o qual podemos pular arbitrariamente e retornar depois, geralmente são utilizadas para automatizar tarefas que precisam ser feitas varias vezes, de maneira que ao invés de ficar reescrevendo os procedimento é possível apenas "pular" para a subrotina quando necessário.

  Uma Subrotina pode receber parâmetros, porem não pode retornar nenhum valor.  Para definir uma subrotina no Next Basic usamos a seguinte sintaxe:

Sub Nome_subrotina( <parametros> )
'[...] Código a executar
end sub

  É possível sair de uma subrotina em duas ocasiões, usando o comando "Return" ou então quando ela chegar ao fim.
  Os parâmetros são passados via Stack, e são especificados da seguinte forma

Byval Nome_do_parametro as <tipo>

  Sendo que o tipo pode ser byte, Word/Integer, Long ou String.  Depois de especificados os parâmetros eles podem ser acessados como variáveis dentro da subrotina, caso sejam especificados mais de 1 parâmetro eles devem ser separados por virgula, Zero parâmetros também são permitidos.

 Também é possível passar parâmetros por referencia caso o valor esteja contido em uma variável (similar aos ponteiros da linguagem C), dessa maneira podemos acessar essa variável e ler ou alterar seu valor dentro da subrotina. Parâmetros passados por referencia também podem ser tratados como Matrizes Unidimensionais, o que permite passar matrizes como parâmetros também.

 Exemplo:  Uma subrotina de soma dois valores word (x1 e x2) e salva numa variável global "k"

sub somar_valores(byval X1 as integer, Byval x2 as integer)
k = X1 + X2
end sub

O mesmo exemplo, usando um parâmetro por referencia para salvar o resultado na variável Global K:
1
2
3
4
5
6
7
8
dim k as integer

'Ao termino da execução dessa subrotina a variavel K tera valor = 4
soma(2,2,k)

sub somar_valores(byval X1 as integer, byval X2 as integer, Byref _result_ as integer)
_result_ = X1 + X2
end sub


  Parâmetros por referencia também podem ser acessados como um vetor unidimensional, o que permite passar matrizes inteira como parâmetros para funções e subrotinas.


	 dim _m[10] as integer ' Declara uma Matriz -m com 10 elementos unsigned integer
	 
	 subrotina(_m) 'Apos a execução dessa linha o elemento 5 da matriz _m tera o valor 10 salvo
	 
	 sub subrotina(byref vec_ as integer) ' Recebe um valor inteiro por Referencia
	  vec_[5] = 10                        ' Salva o valor 10 no Quinto Elemento da matriz recebida por referencia
	 end sub			      ' Fim da subrotina


  Subrotinas são chamadas da mesma maneira que as funções, porem sempre fora de uma expressão matemática, já que elas não retornam nenhum valor. 

  <- Funções ->

  As funções são muito similares as subrotinas, porem com o diferencial de que elas podem retornar valor. Neste caso é necessário especificar no código que se trata de uma função e definir o tipo de valor que essa função retorna(que pode ser Byte, Word/Integer, Long, String ou Fixed).  Exemplo, uma função que soma dois valores word e retorna o resultado ficaria assim:

Function soma_valores(byval X1 as integer, Byval x2 as integer) as integer
return V1 + V2
End Function

  Como ocorre um retorno de valor geralmente as chamadas de função acorrem de dentro de uma expressão matemática. Exemplo:

  var1 = soma_valores(2,2)

  Neste caso o resultado armazenado na variável var1 sera de 4, já que a função soma_valores() retorna a soma dos parâmetros passados na chamada de função.


<- Importando Arquivos Externos ->

  O Next Basic é capaz de ler arquivos de código fonte externos, para arquivos de código fonte existem 3 formatos reconhecidos.

  Arquivos .asm -> Arquivos de código fonte em assembly, estes são lidos e adicionados ao ASM gerado pelo NextBasic antes da compilação... Eles são inseridos no ponto em que são "importados" no código em Basic.

 Arquivos .nbs (ou . vb)-> Arquivos NextBasic Source São arquivos que contem código fonte em Basic, estes arquivos são inseridos no código principal na posição em que são importados na hora da compilação.

  Arquivos .nbh -> Arquivos NextBasic Header, estes arquivos são adicionados sempre ao inicio do código principal (usem com cautela), estes arquivos são bons para armazenar definições usando o comando Equal e declaração formais de variáveis globais (sem atribuição de valor) que são visíveis em qualquer parte do código.

  Qualquer outro arquivo adicionado ao código será lido como um arquivo Raw binary.

  Para incluir um arquivo no source code usamos o comando "import", sendo que o NextBasic é capaz de enxergar arquivos na pasta do source code que estamos editando ou na pasta "system" dentro do diretório do compilador.

exemplo:

Import "\system\genesis_std.nbs" ' Importa a biblioteca Genesis Estandard na pasta system do compilador
Import "\sprites.bin"            ' Importa um arquivo binario chamado "Sprites" no diretorio do source code
  
  
  Para e importação de arquivos Raw Binary é possivel passar diretivas para o compilador com parâmetros que especificam como esse arquivo deve ser inserido no binario final resultante da compilação. Esses paremetros podem ser referentes ao alinhamento com a memoria, um endereço absoluto ou a posição do arquivo em relação ao source code.
  Para inserir os parametros basta colocar uma virgula após o endereço/nome do arquivo a ser importando e inserir os parametros desejados, o parametros podem ser:

  -o Para inserir o arquivo importado num endereço absoluto de memoria especificado em Hexadecimal
  -e Força o alinhamento do arquivo importado com um endereço par de memoria
  -u Força o alinhamento dos dados após o arquivo importado com um endereço par de memoria
  -f Envia o arquivo importado para o final do source file
  -a alinha o arquivo importado com um valor especificado pelo programador

  Caso os parâmetros sejam omitidos, o compilador ira importar o arquivo e alinha-lo com um endereço par da memoria automaticamente.

Exemplo de uso:

1
   	  import "\data.bin , -o 4FFFF , -u " ' Importa o arquivo data.bin no endereço absoluto $4ffff e força o alinhamento dos dados apos esse arquivo com um endereço par da memoria

  <- Incluindo Assembly no Source code ->

  Existem diversas maneira de se fazer isso, usar o comando import para incluir um arquivo de código fonte em assembly externo é uma delas (como especificado no tópico Anterior), também podemos usar a Tag "_asm" para incluir uma linha em assembly entre aspas, exemplo:

  _asm(" move.l D0,D1")

  Para incluir mais de uma linha devemos usar as Tag "_asm_block #__" para inicializar o bloco de código em assembly e as Tags " __# _asm_block_end" para finalizar o Bloco, tudo que estiver entre essas tag's sera copiado e adicionado direto ao assembly gerado pelo NextBasic.

  Alem disso ainda temos diversas outras rotinas para nos ajudar a integrar funções em Basic com funções em assembly, dentre elas os comando Push e Pop. sendo que o comando Push serva para gravar um valor ou expressão matemática em um Registrador do Sistema e o comando Pop serve para ler um valor de um registrador do sistema.  Em ambos os casos devemos especificar o tamanho dos dados que estamos lendo (byte, Word ou Long) e o registrador que estamos acessando deve ser referenciado entre " aspas ".

  Por exemplo, imagine que queremos integrar uma função em assembly no nosso código e essa função recebe como parâmetros um valor v1 no registrador D0 e v2 no registrador D1, o resultado dessa função é salvo no registrador D5.

  Para integrar essa função em assembly no nosso código em Basic bastaria utilizar os seguintes comandos:

Function funcao_asm(Byval v1 as word, Byval v2 as word) as word
     push( v1 as word, "D0") 'Salva o valor de V1 em D0
     push( v2 as word, "D1") 'Salva o valor de V2 em D1

_asm_block #__

;[...] Codigo em Assembly

__# _asm_block_end

return Pop( "D5" as Word) 'Retorna o resultado da função em ASM salvo em D5

 End Function


  Também existem outros fatores que podem facilitar a integração do Basic com o Assembly, por exemplo, um código em assembly pode enxergar todas as variáveis e labels do código em Basic, porem o contrario não é valido (o código em Basic não consegue enxergar variáveis e labels definidas no código em assembly).

  Para acessar variáveis do Basic, basta saber o nome da variável e se ela é Global ou Local.
  Se ela for global basta adicionar "_global_" ao nome da Variável, para variáveis locais adicionamos "_local_" porem, variáveis locais são acessadas sempre por meio do Frame Pointer (registrador A6), Exemplo:

  Para salvar uma variável Global 'x' no Registrador D0:

move.w _global_x,D0

  Para uma variável local 'y':

move.w _local_y(A6),D0

  Para pular para qualquer trecho do código em Basic, basta usar o nome da Label/Função/Subrotina (já que o NextBasic não altera nem adiciona nada aos nomes das labels).

 jmp Nome_Label

<- Manipulando Bits e Posições de Memoria ->

  O Next Basic conta com alguns recursos que permitem verificar/controlar o estado dos bits individualmente em uma variável.
  Podemos fazer isso por meio da função bit_test:

Bit_Test(variável , n_bit)

 Sendo que o primeiro parâmetro é uma expressão matemática, o segundo parâmetro pode ser um numero literal ou uma expressão matemática (inclusive pode ser outra variável servindo como índice), essa função retorna verdadeiro caso o Bit testado seja 1.

Para o caso especifico de variáveis podemos ler o valor de cada bit individualmente usando "." seguindo do endereço do bit que queremos ler.

variavel.5 -> Retorna verdadeiro caso o bit 5 seja 1.

  Para controlar o estado de um bit em uma variável podemos usar a função bit_set para forçar o estado 1 ou bit_clear para forçar um estado 0.  Essas duas funções seguem o mesmo molde dos parâmetros da função anterior.

Bit_Set(<variável> , <n_bit>)
Bit_Clear(<variável> , <n_bit>)

  Também podemos gravar ou ler posições de memoria usando as funções Peek e poke.

  A função peek lê um valor de memoria absoluta de um tamanho especificado no parâmetro <tipo> (que pode ser Byte, Word, Long).   Acessar valores Word e Long deve ser feitos com cautela, pois  esse tipo de valor só pode ser acedido a partir de posições PARES de memoria, se você tentar ler ou gravar um valor Word/Long em uma posição de memoria Impar, o MC68000 ira gerar uma expection de Address Error! Apenas lembrando que valores usados como endereços de memoria devem ser do tipo Long.
 
Peek (<Endereço> as <tipo>)

 A função Poke serve para gravar um determinado valor numa posição de memoria absoluta.

Poke (<dados> as <tipo> , <Endereço>)

  Os dados a serem gravados tem um tamanho especificado no parâmetro <tipo> (Byte, Word, ou Long), e serão gravados no Endereço (cujo tamanho é calculado como long), só lembrando que tentar gravar um valor Word ou Long em um endereço de memoria ímpar também gera uma exception de Address Error.  Por tanto certifique-se que o endereço de memoria seja SEMPRE par caso esteja tentando gravar mais de 1 byte por vez. 

  A função Addressof( ) retorna um valor long equivalente ao endereço de uma Label, Variável, Matriz, Subrotina, ou Função.  Muito útil para integrar rotinas em assembly, calcular offsets de memoria e atribuir valores a ponteiros.


<- Interrupções, Exceptions e Trap Vectors ->

  O NextBasic gera a Vector Table (caso essa opção esteja marcada no menu Ferramentas->Opções), O Stack Pointer é definido para o ultimo endereço Par da memoria RAM e o inicio do código é definido para depois da label "inicio_src:" Essa label foi inserida dentro do arquivo "genesis_header.asm", pois quando compilamos para o Mega Drive obrigatoriamente temos que colocar no inicio da ROM o Header contendo os dados da ROM, bem como a verificação de segurança de Trademark da Sega (tudo isso é feito automaticamente no arquivo genesis_header.asm).  Porem caso você não utilize o arquivo genesis_header.asm sera necessário inserir a label "inicio_src" manualmente para marcar o inicio do source code.

  Para definir um ponto para onde o programa deve saltar ao ocorrer uma exception, basta criar uma subrotina sem parâmetros, com o mesmo nome referenciado na vector table.
 Exemplo: para criar uma subrotina para onde o programa deve saltar sempre que ocorrer uma Intrrupção de nivel 7 (Interrupção Vertical no casso do Mega Drive) podemos usar esse código:

sub isr_07_vector()
'[...] 
end sub

 Caso os vetores não sejam especificados no código eles serão inseridos ao final do source code, sendo que as interrupções são seguidas da instrução RTE, já as demais exception's e Errors Trap convergem para um Loop infinito caso ocorram!

 Exception_list:

isr_buserror
isr_addreserror
isr_illegalinstruction
isr_divisionbyzero
isr_chk
isr_trapv
isr_privilegeviolation
isr_Errortrap
isr_01_vector
isr_02_vector
isr_03_vector
isr_04_vector
isr_05_vector
isr_06_vector
isr_07_vector
trap_00_vector
trap_01_vector
trap_02_vector
trap_03_vector
trap_04_vector
trap_05_vector
trap_06_vector
trap_07_vector
trap_08_vector
trap_09_vector
trap_10_vector
trap_11_vector
trap_12_vector
trap_13_vector
trap_14_vector
trap_15_vector



..:: - Biblioteca Genesis STD - ::..

Genesis_Header.asm

  Arquivo contendo o Header necessário para inicializar uma ROM de Mega Drive, esse header faz a verificação de Trade Mark da Sega e limpa completamente a RAM do MC68000 e do Z80. É importante que ele fique sempre no topo.

Std_Init()

     Subrotina que inicializa o VDP com as configurações padrão (Windows Plane Desativada, PlaneA e PlaneB com tamanho igual 64x32 tiles, interrupções Horizontais, Externas e Globais desligadas e interrupção Vertical Ligada), alem de inicializar os valores do buffer usado para construir os comandos do DMA e de zerar a sprite Table.

vdp_set_config( config_table )

  Subrotina que carrega os valores de configuração do VDP a partir de uma tabela, recebe como parâmetro um valor Long contendo o endereço da tabela. A tabela pode ser gerada pela ferramenta auxiliar VDP Config. Tool disponível no menu Ferramentas.

Joypad6B_read( Joystick )

  Lê o estado dos botões do Joystick especificado pelo valor passado parâmetro  (0 para joystick 1 e 1 para o joystick 2), e retorna um valor Word contendo o status de cada botão, sendo 1 para pressionado e 0 para não pressionado, nessa ordem:

Mode - X - Y - Z - Start - A - C - B - Right - Left - Down Up

  Um conjunto de Equal's foi inserido na biblioteca para facilitar a leitura dos botões, associando uma ID ao valor correspondente a cada Bit na variável que armazena o resultado da leitura do Joystick:

' Bits referentes a cada botão no Joystick
Equal btn_up "0"
Equal btn_down "1"
Equal btn_left "2"
Equal btn_right "3"
Equal btn_b "4"
Equal btn_c "5"
Equal btn_a "6"
Equal btn_start "7"
Equal btn_z "8"
Equal btn_y "9"
Equal btn_z "10"
Equal btn_mode "11"


 Dessa maneira podemos usar um comando de bit_test + uma estrutura IF para detectar quando um botão foi pressionado. Exemplo:

J = Joypad6B_read(0) ' Le o Joystick 1 e salva na variavel J

If bit_test(j, btn_Start) then
'[...] Executa o que esta dentro desse If caso o botão Start seja Pressionado
end if


 Wait_Vblank()

  Essa função prende o processador em um Loop até que o próximo intervalo de Vertical Blank se incie, isso é feitos monitorando o Bit 3 do Registrador Status do VDP. Essa função não recebe nenhum valor como parâmetro. Exemplo:

do
'[...] O que esta dentro desse laço sera executado uma vez a cada frame
wait_Vblank()
loop


Load_tiles_DMA(Data_Address, N_tile, Vram_Addr)

  Essa rotina copia um certo numero de tiles para a Vram usando DMA (Direct Memory Access), o primeiro parâmetro é um Long contendo o endereço a partir do qual vamos começar a copiar esses tiles, a segundo parametro é um Word contendo o numero de tiles que vamos copiar, e o ultimo parâmetro é um long contendo o endereço final na Vram / 32 ( ou seja o endereço em tiles)  isso por que cada tile consiste num conjunto de 32 bytes.
  Esse método é muito veloz para copiar grandes quantidades de memoria, principalmente durante os intervalos de V_blank.  Exemplo de uso:

load_tiles_DMA(addressof(fonte_0), 1, 1)

Fonte_0: 
    datalong &h11100111
    datalong &h11100111
    datalong &h11000011
    datalong &h00011000
    datalong &h00011000
    datalong &h11000011
    datalong &h11100111
    datalong &h11100111

 O programa acima copia 1 tile a partir da label "Fonte_0" e salva na posição 1 da memoria de video, a posição Zero é utilizada para armazenar o tile que o VDP usa para preencher os espaços "vazios" da tela, então é importante não alterar o conteúdo desse tile, sendo mais seguro realizar todas as copias para a Vram a partir da posição '1' .


Load_tiles_DMA_128kSafe(Data_Address, N_tile, Vram_Addr)

  Faz exatamente o mesmo que  subrotina anterior porem NÃO verifica se o endereço é seguro para a transferência, isso por que o VDP do Mega Drive enxerga a memoria em Chunks de 128Kb, se os dados carregados estiverem entre um chunk e outro parte dos dados sera corrompida no processo.
 A Subrotina anterior verifica se o endereço é seguro antes de fazer a copia e caso não seja ela automaticamente divide a transferência em duas operações, porem esse processo consome mais ciclos de processamento ja que é necessário fazer os cálculos de verificação do endereço.
  Caso você tenha certeza que o endereço é seguro e que ele não revaza de um chunk para o outro, você pode usar essa rotina (Load_tiles_DMA_128kSafe) que é ainda mais veloz!

Load_cram_DMA(Data_Address, N_cores, Palet_)

  Essa subrotina serve para carregar dados para a Cram (Color Ram, a memoria que armazena as paletas de cores do Mega Drive), O primeiro parâmetro é um Long contendo o Endereço dos dados que vamos copiar, o segundo parâmetro é um Word contendo a quantidade de cores que vamos copiar, e o ultimo parâmetro é um word contendo o numero da palheta a partir da qual vamos iniciar   a copia, esse numero pode ir de 0 a 3 (dado que o Mega Drive possui 4 palhetas de cores).  Exemplo:

  load_cram_dma(addressof(paleta_cores),16,0) ' Carrega 16 cores na paleta 0

paleta_cores:
    dataint &h0000,&h0EEE,&h0EEE,&h0EEE,&h0EEE,&h0EEE,&h0EEE,&h0EEE
    dataint &h0EEE,&h0EEE,&h0EEE,&h0EEE,&h0EEE,&h0EEE,&h0EEE,&h0EEE

Load_cram_DMA_128ksafe(Data_Address, N_cores, Palet_)

  Faz exatamente o que a subrotina anterior porem sem verificar se o endereço não ultrapassa os limites dos Chunks de 128Kb, é bem mais veloz que a subrotina anterior porem só deve ser usada caso se tenha certeza que o endereço é seguro (para mais esclarecimentos verifique o paragrafo da subrotina Load_tiles_DMA_128ksafe ).

 Draw_tile( Tile, pos_x, pos_y, Plane )

  Desenha um tile na Tela na posição e no plano indicado, essa função recebe como parâmetro um valor indicando que tile vamos desenhar na tela, a posição em X, a posição em Y e o plano (que pode ser Plane_A ou Plane_B), a coordenada é dada em tiles e todos os parâmetros são do tipo word/integer. 
  Por padrão o tile é desenhado usando a paleta Zero, porem é possível definir qualquer outra paleta realizando a operação "or" entre o valor que especifica o índice do tile usado e as tags palette_1, palette_2 ou palette_3  sendo também possível aplicar efeitos de Flip Horizontal e vertical, ou definir a prioridade do Tile usando a operação "or" com as tags V_Flip, H_Flip ou Priority_H

 Exemplo:

 draw_tile(1,0,0,Plane_A) ' Desenha o tile 1 na posição 0,0 (canto superior esquerdo) do plano_A


Draw_tilemap( tilemapSize_xsize_ypx , pyPlane , offset )

Desenha um tilemap na tela, recebe como paremtro um Long contendo o endereço do tilemap, um word contendo o tamanho do mapa em X, um word contendo o tamanho do mapa em Y, um word contendo a coordenada em X, um word contendo a coordenada em Y, um word contendo o Plano onde o mapa sera desenhado e um Word contendo o offset 

Set_sprite_gfx(sprite_number, Gfx, Palet_)

  Essa subrotina define os gráficos e a paleta de cores para um determinado sprite, ela recebe como parametro um Word contendo o indice correspondente ao Sprite que vamos alterar, o numero do tile na Vram que vamos atribuir a esse sprite e por ultimo um word contendo o numero da paleta que que vamos usar para colorir esse sprite, também é possível aplicar os efeitos de de Flip Horizontal e vertical, ou definir a prioridade do sprite, para isso basta realizar a operação "OR" entre o parâmetro Gfx e o identificador do efeito desejado (H_Flip, V_Flip, Priority_H).

set_sprite_gfx(0,1,0) ' Linka o Sprite Zero da tabela com o primeiro tile da Vram e com a paleta Zero


Set_sprite_size(sprite_number, Widht, Height)

  Essa subrotina define o tamanho de um sprite e recebe como parâmetros um word contendo o índice equivalente ao sprite que queremos modificar, um word contendo o tamanho que sera atribuído ao sprite na horizontal e um word que sera o tamanho atribuído ao sprite na vertical.
  Sendo que o Mega Drive consegue renderizar sprites de até 4x4 tiles e cada tamanho intermediário é representado por um numero de 0-3 sendo 0 equivalente a 1 Tile e 3 equivalente a 4 tiles. Exemplo:

set_sprite_size(0,0,0) ' Define o sprite Zero da Sprite Table como tendo 1x1 tile de tamanho  


Set_sprite_position(sprite_number, pos_x, pos_y)

  Essa subrotina define a posição de um sprite na tela, sendo que ela recebe como parâmetros um Word contendo o índice do sprite que vamos mover, um word contendo a posição em X para qual vamos mover-lo e por ultimo um word contendo a posição em Y, ambas em pixels!
  Só lembrando que os Sprites podem se mover numa área de 512 x 512 pixels e a área visível para o jogador começa na posição 128x128.

set_sprite_position(0,128,128) ' Move o Sprite Zero da Tabela para o canto superior esquerdo da tela 


Update_sprite_table( )
  
  Essa Subrotina, atualiza a Sprite Table, aplicando todas as modificações que foram feitas nos Sprites desde a ultimas vez em que ela foi chamada, ela se faz necessária por que essa biblioteca mantem um buffer na RAM e todas as modificações feitas nos sprites são feitas primeiramente nesse buffer, ao chamar a rotina de Update o buffer é transmitido para a Vram usando DMA.
  Essa subrotina não recebe nenhum valor como parâmetro. Exemplo:

 Do

'[...] Código que modifica a posição ou animação dos sprites

Update_sprite_table( ) ' Aplica as modificações
Wait_Vblank( ) ' Espera o inicio do Vblank 
Loop

Set_VerticalScroll_position(cam_V, Plane)

  Essa função serve para definir o valor do scroll Vertical, por padrão ele vem configurado para o modo "Whole Screen", essa função recebe como parâmetro um word contendo a posição para qual a Scroll Cam sera enviada e um Word contendo o numero do plano ao qual aplicaremos o scroll (sendo 0 para o Plane_A e 1 Para o Plane_B)

do

x+=1
Set_VerticalScroll_position(x,Scroll_A)  

Wait_Vblank() 
loop


Set_HorizontalScroll_position(cam_H, Plane)

  Essa função serve para definir o valor do scroll Horizontal, por padrão ele também vem configurado para o modo "Whole Screen" e essa função recebe como parâmetro um word contendo contendo a posição para qual a Scroll Cam Horizontal sera enviada e um Word contendo o numero do plano ao qual aplicaremos o scroll, sendo 0 para o Plano A e 1 Para o Plano B, foi adicionado um Equal na biblioteca para facilitar a utilização desses parâmetros, sendo possível usar os identificados Scroll_A e Scroll_B para seleciona o plano de Scrolling.

do

y+=1
Set_HorizontalScroll_position(y,Scroll_A)  

Wait_Vblank() 
loop



Hscroll_strip8( camera, strip, plane )

Esse subrotina deve ser usada para setar a posição da camera Horizontal para cada faixa de 8 Pixels individualmente (quando o modo de scroll Horizontal estiver configurado dessa maneira no VDP), ela recebe como parâmetro um Word contendo a posição que sera atribuída a câmera, um word contendo o numero da faixa (strip) e um word contendo o plano ao qual essa strip pertence. A mudanças só são aplicadas apos chamar a subrotina update_Hscroll_table()

Hscroll_line( cameralineplane )

Esse subrotina deve ser usada para setar a posição da camera Horizontal para cada linha individualmente (quando o modo de scroll Horizontal estiver configurado dessa maneira no VDP), ela recebe como parâmetro um Word contendo a posição que sera atribuída a câmera, um word contendo o numero da linha e um word contendo o plano ao qual essa linha pertence. A mudanças só são aplicadas apos chamar a subrotina update_Hscroll_table()


Update_Hscroll_table()

  Atualiza a Scroll Table na Vram via DMA.

Enable_global_int()

  Ativa interrupções Globais

Disable_global_int()

 Desativa as Interrupções Globais

Enable_V_int()

  Ativa Interrupções Verticais

Disable_V_int()

 Desativa Interrupções Verticais

Enable_H_int()

 Ativa Interrupções Horizontais

Disable_H_int()

 Desativa Interrupções Horizontais

Enable_Ext_int()

 Ativa Interrupções Externas.

Disable_Ext_int()

Desativa Interrupções Externas.

Enable_display()

Liga Display.

Disable_display()

Desliga o Display (acelera transferências por DMA fora do Vblank).

Set_Hint_counter( count )

Seta o valor de contagem de scanlines entre as interrupções horizontais (valor salvo no registrador Horizontal Interrupt Counter do VDP), recebe como parâmetro um word contendo o numero de scanlines entre uma interrupção e outra.

Direct_color_DMA( bmp , frames)

Exibe uma imagem de 192x224 na tela usando a tecnica de Direct Color DMA, recebe como parâmetro um long contendo o endereço da imagem e um word contendo o numero de frames em que a imagem sera exibida (sendo 60 frames = 1 segundo).

Nenhum comentário:

Postar um comentário