mirror of
https://github.com/gbdev/awesome-gbdev.git
synced 2025-08-12 19:13:59 +02:00
Move salvaged articles to dedicated gb-archive repo due to licensing issues described in #67
This commit is contained in:
@@ -1,142 +0,0 @@
|
||||
Fast Two Player Code using RGBASM
|
||||
(or Fast OOP using RGBASM)
|
||||
Written by GeeBee
|
||||
With an addition by Otaku
|
||||
|
||||
As an example, let's say you have an assembly language two
|
||||
player game that you are working on where the majority of
|
||||
the code for player one might be identical for player two.
|
||||
There are basically two ways of handling this:
|
||||
|
||||
1) Pass a pointer to these routines that points to the
|
||||
"object" structure that you wish to process.
|
||||
|
||||
2) Repeat all of these code routines using slightly
|
||||
different function/variable names.
|
||||
|
||||
The advantage to using approach number 1 is that you can
|
||||
have two or more objects that share the same code routines.
|
||||
One disadvantage is the code will be slower than approach
|
||||
2 because you have to access the object structure through
|
||||
a pointer. Also this pointer can tie up valuable GB registers
|
||||
which can cause longer/slower code due to fewer available
|
||||
registers.
|
||||
|
||||
The advantage to using approach number 2 is that since your
|
||||
object data doesn't have to be accessed through a pointer the
|
||||
code can be much quicker which sometimes is very important
|
||||
on the limited GB/GBC platform. The disadvantages are that
|
||||
code must be repeated for each object (which can rapidly eat
|
||||
up ROM space for a large number of objects) and having several
|
||||
identical routines (with different function & variable names)
|
||||
is a big maintenance problem when the same changes need to be
|
||||
applied to each object.
|
||||
|
||||
|
||||
If you can get around the maintenance problems of approach
|
||||
number 2, this can be ideal in some cases where you need
|
||||
fast execution speed OR in cases where you have just finished
|
||||
coding a 1 player game and as an after-thought you decide to
|
||||
add 2 player support but you don't want to rewrite your code
|
||||
to use object pointers.
|
||||
|
||||
RGBASM is powerful enough that you can eliminate the majority
|
||||
of the maintenance problems in this case. Here's example code:
|
||||
(The macro operator '@' is incremented with each macro included
|
||||
in your source code so this code needs to be compiled stand-alone
|
||||
and then linked with other code or you must guarantee that no
|
||||
macros are included before this code. The macro FOO below is
|
||||
only used to increment '@' from 0 to 1 so that the first object
|
||||
ends with _1 instead of _0.)
|
||||
|
||||
|
||||
ObjectX_1: DB
|
||||
ObjectY_1: DB
|
||||
ObjectX_2: DB
|
||||
ObjectY_2: DB
|
||||
|
||||
FOO: MACRO
|
||||
db 0
|
||||
ENDM
|
||||
|
||||
; Set Object X,Y coordinates to H,L
|
||||
|
||||
OBJCODE: MACRO
|
||||
|
||||
SetObjectPos\@:
|
||||
ld a,h
|
||||
ld [ObjectX\@],a
|
||||
ld a,l
|
||||
ld [ObjectY\@],a
|
||||
ret
|
||||
|
||||
GetObjectPos\@:
|
||||
ld a,[ObjectX\@]
|
||||
ld h,a
|
||||
ld a,[ObjectY\@]
|
||||
ld l,a
|
||||
ret
|
||||
|
||||
ENDM
|
||||
|
||||
; Include code for object #1
|
||||
OBJCODE
|
||||
; Include code for object #2
|
||||
OBJCODE
|
||||
|
||||
; Set Object 1 coordinates to 0,0
|
||||
ld hl,0
|
||||
call SetObjectPos_1
|
||||
|
||||
; Set Object 2 coordinates to 0,0
|
||||
ld hl,0
|
||||
call SetObjectPos_2
|
||||
|
||||
; Return Object 1 coordinates in H,L
|
||||
call GetObjectPos_1
|
||||
|
||||
; Return Object 2 coordinates in H,L
|
||||
call GetObjectPos_2
|
||||
|
||||
--
|
||||
added by Otaku
|
||||
|
||||
Using SET to point to the start of your object
|
||||
structure, and then calculating offsets into the
|
||||
object structure is a neater way of doing this,
|
||||
without having to worry about an idle use of MACRO
|
||||
incrementing the @ counter.
|
||||
|
||||
So:
|
||||
Object1: DS 16
|
||||
Object2: DS 16
|
||||
|
||||
RSRESET
|
||||
X_POS RB 2
|
||||
Y_POS RB 2
|
||||
HEALTH RB 1
|
||||
|
||||
|
||||
; assume that x & y are passed in BC & DE
|
||||
; assume for this example that GB has the ability
|
||||
; to move registers directly to memory without using
|
||||
; the A register
|
||||
SetObjectPos: MACRO
|
||||
ObjectBase SET \1
|
||||
LD [ObjectBase+X_POS],C
|
||||
LD [ObjectBase+X_POS+1],B
|
||||
LD [ObjectBase+Y_POS],E
|
||||
LD [ObjectBase+Y_POS+1],D
|
||||
PURGE ObjectBase
|
||||
ENDM
|
||||
|
||||
SetObject1Pos:
|
||||
SetObjectPos Object1
|
||||
RET
|
||||
SetObject2Pos:
|
||||
SetObjectPos Object2
|
||||
RET
|
||||
|
||||
|
||||
|
||||
|
@@ -1,28 +0,0 @@
|
||||
Some ideas for why sprites would disappear in 2x Speed Mode
|
||||
Written by Jan-Lieuwe Koopmans
|
||||
moa@gelrevision.nl
|
||||
|
||||
|
||||
- You move your sprites using an ISR (interrupt service routine) and that
|
||||
particular interrupt is disabled by the cpu-speed switch routine (IE_REG
|
||||
being altered);
|
||||
|
||||
|
||||
- Bit 1 of the LCDC register is somehow set to zero;
|
||||
|
||||
|
||||
- The DMA copy of OAM workram to real OAM ram is not done anymore (that
|
||||
routine is in HiRam and is being called by the default VBL ISR).
|
||||
|
||||
|
||||
- Your sprite-tiles are not being copied to VRAM in a proper way.
|
||||
|
||||
|
||||
|
||||
You can check all these easily using No$Gmb (press F10 in debug mode to see
|
||||
register contents, check HiRam (starting at $FF80) using the debugger, check
|
||||
Video RAM using F5 in debug mode).
|
||||
|
||||
|
||||
|
||||
|
@@ -1,70 +0,0 @@
|
||||
by The ZenPsycho
|
||||
|
||||
;*****************************
|
||||
;* Jump table code *
|
||||
;*****************************
|
||||
;
|
||||
;******************************************************************
|
||||
;
|
||||
; on entry:
|
||||
; A = Address offset
|
||||
;
|
||||
; Registers used:
|
||||
; A, HL, DE
|
||||
;
|
||||
; on exit:
|
||||
; A destroyed
|
||||
; hl contains address from jump table
|
||||
;
|
||||
; place this routine at reset $08
|
||||
;
|
||||
; [example code]
|
||||
; (If A=0 then jp this)
|
||||
; (if A=1 then jp that)
|
||||
; (if A=2 then jp theother)
|
||||
;
|
||||
; RST $08 ;call function to jump to address from pointer table
|
||||
; DB this ;pointer table
|
||||
; DB that
|
||||
; DB theother
|
||||
;
|
||||
;
|
||||
;******************************************************************
|
||||
|
||||
|
||||
pop hl ;get address of first pointer from stack
|
||||
push de ;preserve DE
|
||||
add a,a ;multiply A by two
|
||||
ld e,a ;store A in E
|
||||
ld d,00 ;so that we can
|
||||
add hl,de ;add the offset to hl
|
||||
ldi a,(hl) ;and now
|
||||
ld h,(hl) ;get an address from the pointer table
|
||||
ld l,a ;
|
||||
pop de ;restore DE
|
||||
jp hl ;and jump to address from table
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;*****************************************************************
|
||||
|
||||
pop hl ;get address
|
||||
push bc ;preserve BC
|
||||
ld c,a ;get offset from A into BC
|
||||
ld b,00 ;
|
||||
ldi a,(hl) ;load from address into A and increment address
|
||||
push hl ;store address+1
|
||||
add hl,bc ;add offset to address+1
|
||||
ld c,a ;get data from address+1 into BC (B still=0)
|
||||
ld a,(hl) ;get data from address+1+offset
|
||||
pop hl ;restore address+1
|
||||
add hl,bc ;
|
||||
pop bc ;
|
||||
jp hl ;
|
||||
|
||||
;*****************************************************************
|
@@ -1,46 +0,0 @@
|
||||
;***************************************
|
||||
;* Lookup table function *
|
||||
;***************************************
|
||||
;
|
||||
; On entry:
|
||||
; A contains table index
|
||||
;
|
||||
; registers used:
|
||||
; A, HL, BC
|
||||
;
|
||||
; On exit:
|
||||
; A contains table entry
|
||||
; HL contains address of the end of the table
|
||||
; BC is preserved
|
||||
;
|
||||
; this function is placed at $0018
|
||||
;
|
||||
; [Example Code]
|
||||
; LD A, $04
|
||||
; CALL TABLE
|
||||
;
|
||||
; TABLE:
|
||||
; RST $18 ;call table look up function , push PC onto stack
|
||||
; DB $06 ;number of bytes in table
|
||||
; DB $06, $05, $04, $03, $02, $01 ;table data
|
||||
; ret
|
||||
;
|
||||
; after running this code the accumulator (A) contains $02
|
||||
;
|
||||
;************************************************************************
|
||||
|
||||
|
||||
POP HL ;Load address from stack
|
||||
PUSH BC ;preserve BC
|
||||
LD C,A ;Load index
|
||||
LD B,0 ;into BC
|
||||
LD A,[HLI] ;Load size of table into A
|
||||
PUSH HL ;preserve the address of the start of the table
|
||||
ADD HL,BC ;HL now points to table index
|
||||
LD C,A ;load size of table into BC
|
||||
LD A,[HL] ;A now contains Table entry at the specified index
|
||||
POP HL ;to the start address of the table,
|
||||
ADD HL,BC ;add the size of the table
|
||||
POP BC ;restore BC
|
||||
JP HL ;and jump to the instruction AFTER the table
|
||||
|
@@ -1,78 +0,0 @@
|
||||
Ok so you want sprites in your gameboy game/demo. You've got a DMA routine, you know how it works. What you don't know is what is the quickest, best way to copy it into HRAM at $FF80. That's where document comes in. I'm going to teach you how to use a routine that will allow you to easily copy data to ram. and this isn't only good for copying your DMA routine, you can use it to copy tile data and other bits of assorted code into ram, easily. the first thing you do is you put the following routine at $0028, like this:
|
||||
|
||||
|
||||
SECTION "COPY DATA",home[$0028]
|
||||
|
||||
POP HL
|
||||
PUSH BC
|
||||
LD A,[HLI]
|
||||
LD B,A
|
||||
LD A,[HLI]
|
||||
LD C,A
|
||||
copydataloop:
|
||||
LD A,[HLI]
|
||||
LD [DE],A
|
||||
INC DE
|
||||
DEC BC
|
||||
LD A,B
|
||||
OR C
|
||||
JR NZ, copydataloop
|
||||
POP BC
|
||||
JP [HL]
|
||||
|
||||
|
||||
The reason we put it at $0028 is because it's a reset code. which means you only need one byte to call the code that's there, as opposed to the 3 bytes you use in a normal call.
|
||||
I'll explain this code a little later, but for now I will show you how to use it. We'll create a new file called "DMA.INC". in this file you put this:
|
||||
|
||||
|
||||
DMACOPY:
|
||||
LD DE, $FF80 ;$FF80 is where we want to copy this data
|
||||
RST $28 ;Call the copy routine
|
||||
DB $00, $0D ;the amount of bytes we want to copy,
|
||||
;represented in a 16 bit unsigned integer.
|
||||
|
||||
|
||||
;And now the data. This is a DMA routine.
|
||||
;Hand assembled. It copies data from $C100
|
||||
|
||||
DB $F5, $3E, $C1, $EA, $46, $FF, $3E, $28, $3D, $20, $FD, $F1, $D9
|
||||
|
||||
RET
|
||||
|
||||
|
||||
now save this file, and include it in your main file. then when you want to copy the DMA routine to $FF80,
|
||||
|
||||
CALL DMACOPY
|
||||
|
||||
simple huh?
|
||||
ok now let's explore how the routine itself works.
|
||||
|
||||
|
||||
|
||||
POP HL ;when you call RST $28, it pushes the address of the
|
||||
;byte immediately following it, onto the stack.
|
||||
;this instruction stores that address into HL
|
||||
PUSH BC ;(preserve BC)
|
||||
LD A,[HLI] ;This copies the byte at that address into B
|
||||
LD B,A ;which is the MSB of the amount of bytes we want to
|
||||
LD A,[HLI] ;copy. Then this copies the LSB into C.
|
||||
LD C,A ;Now HL should point at the first byte of our data
|
||||
copydataloop:
|
||||
LD A,[HLI] ;Copy that byte into a
|
||||
LD [DE],A ;then copy a to the address contained in DE ($FF80)
|
||||
INC DE ;Increment the address in DE.
|
||||
DEC BC ;One byte down, BC to go
|
||||
LD A,B ;then OR B and C together
|
||||
OR C ;to find out if BC = 0
|
||||
JR NZ, copydataloop ;if it isn't, copy another byte
|
||||
POP BC ;(restore BC)
|
||||
JP [HL] ;Jump to the byte after the data, which is a RET
|
||||
|
||||
|
||||
|
||||
Pretty clever eh?
|
||||
|
||||
If you have any questions or comments, E-mail me at ZenPsycho@yahoo.com
|
||||
|
||||
|
||||
|
@@ -1,81 +0,0 @@
|
||||
Common Gameboy Development Acronyms
|
||||
Written by GeeBee and Jason
|
||||
|
||||
*plug* http://www.devrs.com/gb/
|
||||
*plug* http://www.tripmode.com/gbdev/
|
||||
|
||||
Read up, because GeeBee likes to give pop quizes. =)
|
||||
|
||||
AdvGBIDE - GB IDE assembler by MegaMan_X. Abandoned(?).
|
||||
AI - Artificial Intelligence
|
||||
APA - All points accessable. Used in pixel-based graphics.
|
||||
API - Application Program Interface
|
||||
ASxxxx - Assembler used in GBDK.
|
||||
BG - Background
|
||||
C3 - Carbon Copy Card
|
||||
CGB - Reference to the Gameboy Color
|
||||
CPU - Central Processing Unit
|
||||
DA - Digital Audio
|
||||
DX - Term adapted indicating Gameboy Color enhanced.
|
||||
GB - Game Boy
|
||||
GBC - Gameboy Color.
|
||||
GBCAM - Gameboy Camera.
|
||||
GBDEV - Gameboy Development (well duh.) =)
|
||||
GBDK - C compiler for GB by Pascal Felber now maintained by
|
||||
Michael Hope. http://gbdk.sourceforge.net
|
||||
GBMB - Map builder for GB by Harry Mulder.
|
||||
GBP - Game Boy Pocket
|
||||
GBTD - Tile editor for GB by Harry Mulder.
|
||||
GBX - Gameboy XChanger. Flash cart programmer manufactured by Bung.
|
||||
Product is not currently being manufactured.
|
||||
GG - Game Genie
|
||||
DMA - Direct Memory Access
|
||||
EPROM - A chip that works like a ROM but is reprogrammable
|
||||
but requires an ultaviolet light source to do so.
|
||||
Flash - A chip that works like a ROM but that can be
|
||||
reprogrammed many times easily.
|
||||
Flash EPROM - Same as Flash.
|
||||
Flash Cart - Any cart that has had the original ROM removed
|
||||
and a Flash installed in its place as shown
|
||||
in the ReadPlus cart reader/writer docs.
|
||||
FMV - Full Motion Video
|
||||
GUI - Graphical User Interface
|
||||
HBL - Horizontal Blank
|
||||
HBlank - Horizontal Blank
|
||||
HC - High color. Involves palette updates during Horizontal Blank.
|
||||
IDE - Integrated Development Environment
|
||||
HuC - Hudson Controller (selects memory banks + other features)
|
||||
LCC - GBDK's original compiler.
|
||||
LCD - Liquid Crystal Display
|
||||
LSB - Least Significant Bit (or Byte)
|
||||
MBC - Memory Bank Controller
|
||||
MSB - Most Significant Bit (or Byte)
|
||||
NDA - Non-Disclosure Agreement
|
||||
OAM - Object (Sprite) Attribute Memory
|
||||
PAL - Palette
|
||||
PV - Pocketvoice. Flash cart with microphone and speaker built in.
|
||||
RAM - Random Access Memory (read / write memory)
|
||||
RGBDS - Powerful GB assembler by Carsten Sorensen now maintained by
|
||||
Otaku. http://www.otakunozoku.com
|
||||
RGBASM - Powerful GB assembler by Carsten Sorensen now maintained by
|
||||
Otaku. http://www.otakunozoku.com
|
||||
ROM - Read Only Memory (new carts contain this)
|
||||
SDCC - New compiler used in GBDK.
|
||||
SGB - Super Game Boy
|
||||
SPR - Sprite
|
||||
SRAM - Save RAM. Battery backed up random access memory.
|
||||
SRC - Source Code
|
||||
SSC - Super Smart Card (programmable cart, no longer made)
|
||||
TASM - Table assembler for GB (not related to Turbo Assembler)
|
||||
TIM - Timer
|
||||
TIMINT - Timer Interrupt
|
||||
VBK - Video RAM Bank
|
||||
VBL - Vertical Blank
|
||||
VBLINT - Vertical Blank Interrupt
|
||||
VRAM - Video RAM
|
||||
WIN - Window
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,50 +0,0 @@
|
||||
How to animate the water as seen in Zelda DX
|
||||
By Marc Gale
|
||||
marc@sbirmc.ac.uk
|
||||
|
||||
The way I've done it uses the following:
|
||||
|
||||
|
||||
1. Four seperate animation tiles which will cycle around happily. In
|
||||
other words, displaying tile 1, 2, 3, 4, 1 will look good.
|
||||
2. A byte in RAM somewhere for the frame counter
|
||||
3. Use a specific single tile number for all water
|
||||
4. A byte in RAM somewhere for the water animation delay
|
||||
5. A byte in RAM somewhere for the water animation count
|
||||
|
||||
|
||||
What I did was to ensure that the specified tile number was used
|
||||
for all water on the map. Then, in the V-Blank interrupt, copy the
|
||||
appropriate frames' tile from ROM to the tile data area, and then
|
||||
update the frame counter. Of course, checking to see if we're
|
||||
ready for the animation first. A pseudo code for the interrupt
|
||||
follows:
|
||||
|
||||
|
||||
VBlank_Int:
|
||||
grab the animation counter
|
||||
increase it by one
|
||||
put it back
|
||||
compare the counter to the animation delay value
|
||||
if we haven't reached the delay value, return
|
||||
|
||||
|
||||
reset the animation counter
|
||||
|
||||
|
||||
grab the frame number
|
||||
increase the frame number, resetting it if neccessary
|
||||
|
||||
|
||||
copy the appropriate tile data from rom into the specified
|
||||
tile to be used for water
|
||||
fin:
|
||||
|
||||
|
||||
Or something like that. When I have some replacement graphics
|
||||
for my project, I'll release the full source code (any volunteers?) as
|
||||
I can't release the project whilst it still uses Zelda's graphics.
|
||||
|
||||
|
||||
|
||||
|
@@ -1,202 +0,0 @@
|
||||
GeeBee's GB Assembly Code Tips v1.0
|
||||
-----------------------------------
|
||||
|
||||
Note: The following references to 'cycles' refers
|
||||
to machine cycles. To convert to clock cycles
|
||||
multiply by 4. (i.e. 1 machine cycle = 4 clock cycles)
|
||||
|
||||
|
||||
; **** Load A with $00 ****
|
||||
Method 1:
|
||||
ld a,0 ; 2 bytes, 2 cycles, Doesn't affect flags
|
||||
|
||||
Method 2:
|
||||
xor a ; 1 byte, 1 cycle, Flag results: C=0, Z=1
|
||||
|
||||
|
||||
; **** Compare A to $00 ****
|
||||
Method 1:
|
||||
cp 0 ; 2 bytes, 2 cycles
|
||||
|
||||
Method 2:
|
||||
or a ; 1 byte, 1 cycle
|
||||
|
||||
Method 3:
|
||||
and a ; 1 byte, 1 cycle
|
||||
|
||||
|
||||
; **** Call/Return ****
|
||||
Method 1: ; 4 bytes, 10 cycles
|
||||
...
|
||||
call sub
|
||||
ret
|
||||
|
||||
Method 2: ; 3 bytes, 4 cycles
|
||||
...
|
||||
jp sub
|
||||
|
||||
|
||||
; **** Exchange DE & HL ****
|
||||
Method 1: ; 6 bytes, 6 cycles
|
||||
ld a,d
|
||||
ld d,h
|
||||
ld h,a
|
||||
ld a,e
|
||||
ld e,l
|
||||
ld l,a
|
||||
|
||||
Method 2: ; 4 bytes, 9 cycles
|
||||
push de
|
||||
ld d,h
|
||||
ld e,l
|
||||
pop hl
|
||||
|
||||
|
||||
; **** Load hl,[Address] ****
|
||||
Method 1:
|
||||
ld a,[Address] ; 8 bytes, 10 cycles
|
||||
ld l,a
|
||||
ld a,[Address+1]
|
||||
ld h,a
|
||||
|
||||
Method 2:
|
||||
ld hl,Address ; 6 bytes, 8 cycles
|
||||
ld a,[hl+]
|
||||
ld h,[hl]
|
||||
ld l,a
|
||||
|
||||
|
||||
; **** Call [HL] ****
|
||||
Method 1: ; 5 bytes, 8 cycles
|
||||
ld de,.retadr
|
||||
push de
|
||||
jp [hl]
|
||||
.retadr:
|
||||
|
||||
Method 2: ; 4 bytes, 7 cycles
|
||||
call DoJump
|
||||
...
|
||||
...
|
||||
|
||||
DoJump:
|
||||
jp [hl]
|
||||
|
||||
|
||||
; **** HL = -HL ****
|
||||
Method 1: ; 7 bytes, 8 cycles
|
||||
ld a,l
|
||||
cpl
|
||||
ld l,a
|
||||
ld a,h
|
||||
cpl
|
||||
ld h,a
|
||||
inc hl
|
||||
|
||||
Method 2: ; 7 bytes, 7 cycles
|
||||
xor a
|
||||
sub l
|
||||
ld l,a
|
||||
ld a,0
|
||||
sbc h
|
||||
ld h,a
|
||||
|
||||
|
||||
; **** A = CONST - A ****
|
||||
Method 1: ; 4 bytes, 4 cycles
|
||||
ld b,a
|
||||
ld a,CONST
|
||||
sub b
|
||||
|
||||
Method 2: ; 3 bytes, 3 cycles
|
||||
cpl
|
||||
add CONST+1
|
||||
|
||||
|
||||
; **** HL = HL + A ****
|
||||
Method 1: ; 6 bytes, 6 cycles
|
||||
add l
|
||||
ld l,a
|
||||
ld a,0
|
||||
adc h
|
||||
ld h,a
|
||||
|
||||
Method 2: ; 5 bytes, 5 cycles
|
||||
add l
|
||||
ld l,a
|
||||
jr nc,.notcarry
|
||||
inc h
|
||||
.notcarry:
|
||||
|
||||
|
||||
; **** Parameter Setup ****
|
||||
Method 1: ; 10 bytes
|
||||
|
||||
Entry1:
|
||||
ld a,1
|
||||
jr Sub
|
||||
Entry2:
|
||||
ld a,2
|
||||
jr Sub
|
||||
Entry3:
|
||||
ld a,3
|
||||
Sub:
|
||||
...
|
||||
...
|
||||
|
||||
Method 2: ; 8 bytes
|
||||
|
||||
Entry1:
|
||||
ld a,1
|
||||
DB 1 ; Opcode for LD BC,xxxx
|
||||
Entry2:
|
||||
ld a,2
|
||||
DB 1 ; Opcode for LD BC,xxxx
|
||||
Entry3:
|
||||
ld a,3
|
||||
Sub:
|
||||
...
|
||||
...
|
||||
|
||||
|
||||
; **** Fast subroutine execution ***
|
||||
|
||||
Method 1:
|
||||
ld hl,param1
|
||||
call sub1
|
||||
ld hl,param2
|
||||
call sub2
|
||||
ld hl,param3
|
||||
call sub1
|
||||
...
|
||||
...
|
||||
|
||||
.sub1:
|
||||
...
|
||||
ret
|
||||
.sub2:
|
||||
...
|
||||
ret
|
||||
|
||||
Method 2:
|
||||
ld sp,calltable
|
||||
ret ; jump to sub1
|
||||
|
||||
.sub1:
|
||||
pop hl
|
||||
...
|
||||
ret
|
||||
.sub2:
|
||||
pop hl
|
||||
...
|
||||
ret
|
||||
|
||||
calltable:
|
||||
dw sub1,param1
|
||||
dw sub2,param2
|
||||
dw sub1,param3
|
||||
|
||||
** End of File ***
|
||||
|
||||
|
||||
|
||||
|
@@ -1,105 +0,0 @@
|
||||
Notes for getting assembly language programs
|
||||
that run on VGB to run on a real GB
|
||||
by kOOPa, 2-Jan-98:
|
||||
--------------------------------------------
|
||||
|
||||
1. Emulators tend to set all of RAM to $00 on power up.
|
||||
Real GBs do NOT initialize RAM on power up. RAM is filled
|
||||
with random values on power up. You must clear it yourself
|
||||
if you wish it to be set to some known value.
|
||||
|
||||
2. The real hardware could care less about the ROM checksum
|
||||
($14e,$14f) but the complement check ($14d) MUST be correct
|
||||
or programs will "lock up" after scrolling the Nintendo logo.
|
||||
|
||||
Use RGBFIX -V in the RGBDS development system to set
|
||||
the checksum and the complement byte after each source
|
||||
code compile. It doesn't matter whether you program in
|
||||
C or assembly, this program will fix it.
|
||||
|
||||
3. The Nintendo scrolling graphic from $104 - $133 must be
|
||||
accurate. If one byte of it is changed then your programs
|
||||
will "lock up" after scrolling this graphic logo.
|
||||
|
||||
4. When the LCD display is off (bit 7 of $ff40 set to 0) you
|
||||
can write to video memory at any time with out restrictions.
|
||||
While it is on you can only write to video memory during
|
||||
H-Blank and V-Blank. Code similar to this will work:
|
||||
|
||||
; Write B to Video RAM location HL
|
||||
WriteVRAM:
|
||||
di ;turn off interrupts
|
||||
Write1:
|
||||
ldh a,[$41] ;read $ff41
|
||||
and 2
|
||||
jr nz,Write1
|
||||
ld [hl],b
|
||||
ei ;turn on interrupts
|
||||
ret
|
||||
|
||||
There should not be many instructions between the "jr nz" and
|
||||
write to memory "ld [hl],b". A worst case of 64 CPU clock cycles
|
||||
are available to access main video memory (not OAM!) following
|
||||
the "jr nz" command.
|
||||
|
||||
The "di" and "ei" commands above are only required if you
|
||||
are using Serial, Timer, or Hi-2-Lo interrupts.
|
||||
|
||||
5. The LCD display is on at reset (bit 7 of $ff40 set to 1).
|
||||
Before the LCD display can be turned off you must wait for
|
||||
V-Blank. One popular way of doing this is the following:
|
||||
|
||||
; Turn off LCD display
|
||||
LCDOff:
|
||||
ldh a,[$44h] ; $ff44=LCDC Y-Pos
|
||||
cp $90 ; $90 and bigger = in VBL
|
||||
jr nz,LCDOff ; Loop until = $90
|
||||
xor a
|
||||
ldh [$41],a ; Turn off LCD display
|
||||
ret
|
||||
|
||||
Note you should disable interrupts, if they are enabled,
|
||||
before executing the above code or else the test of $ff44
|
||||
could prove invalid.
|
||||
|
||||
Turning the LCD display on can be done at any time.
|
||||
|
||||
6. If you are using sprites then you should not use the
|
||||
following commands when their register contents are in
|
||||
the range $fe00-$feff.
|
||||
|
||||
inc bc
|
||||
inc de
|
||||
inc hl
|
||||
|
||||
If you don't follow this rule, sprite trash in the form
|
||||
of sprite "blink" will randomly affect your sprites.
|
||||
|
||||
7. Normally you should only make changes to Sprite RAM
|
||||
during V-Blank unless you are an expert and know exactly
|
||||
what you are doing. The common way to do this is to use
|
||||
the GB DMA register ($ff46) to do a fast copy from your
|
||||
sprite table in RAM to $fE00-$fe9f.
|
||||
|
||||
A. You need a sprite table in RAM with a starting address
|
||||
of $XX00 and with a length of 160 ($a0). Many often
|
||||
use $c000-$c09f for this purpose but anywhere in RAM
|
||||
starting with $XX00 is fine.
|
||||
|
||||
B. You need to create a VBlank interrupt routine that
|
||||
contains the DMA command, followed by a short delay
|
||||
to allow the DMA to complete, and copy this routine
|
||||
to high RAM ($ff00-$fffe). The DMA command WILL NOT
|
||||
WORK in ROM or low RAM because these are disabled
|
||||
during DMA.
|
||||
|
||||
C. After copying this routine to high RAM you then
|
||||
need to enable the VBLANK interrupt and then enable
|
||||
interrupts.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,540 +0,0 @@
|
||||
Making your first Gameboy Game
|
||||
by Mike Mika, Genetic Fantasia
|
||||
mmika@geneticfantasia.com
|
||||
|
||||
You will find many incredible tools out there to make gameboy games, but to make
|
||||
life easy for you at this point, find these programs:
|
||||
|
||||
TASM with Gameboy Table by Jeff Frohwein
|
||||
GFTILE by Genetic Fantasia (Only because its easy output, I recommend TILE256)
|
||||
VGB-DOS by Marcel De Kogel (Port of Marat Fayzullin's emulator)
|
||||
PAN97DOC the Gameboy Hacker Bible by Pan of Anthrox and Jeff Frohwein
|
||||
Z80 command list
|
||||
GAMEBOY memory map
|
||||
GBTOOL
|
||||
|
||||
|
||||
Terminology:
|
||||
|
||||
A CHARACTER is an 8x8 graphic tile. The gameboy screen is comprised of 32x32
|
||||
characters, with only 20x18 visible.
|
||||
|
||||
A MAP is the coordinate information of all the characters to be displayed on the
|
||||
screen. A 20x18 map would cover the visible screen.
|
||||
|
||||
A SPRITE is a form of tile that is not a part of the background, but
|
||||
may move freely over or under the background character map. It is also not
|
||||
restricted to 8x8 coordinates, but moves pixel by pixel.
|
||||
|
||||
A PALETTE is the color set used by the gameboy. The gameboy has four empty indexes
|
||||
where you can place the order of the four grey scale.
|
||||
|
||||
These are essential tools to begin with. It also helps to know a little bit about
|
||||
assembly. Even if you don't, you can figure it out pretty easy as the Z80 processor
|
||||
is very basic.
|
||||
|
||||
First Step
|
||||
|
||||
You should find yourself a good text editor. If not, just use the DOS text editor
|
||||
to write this program, or even use notepad. A gameboy program needs a header and
|
||||
checksum to run correctly on a gameboy. Any source you find will have this header.
|
||||
Here is an example (Used from Hero Zero's Text Demo):
|
||||
|
||||
.org $0000 ; Start of the binary
|
||||
|
||||
.org $0040 ; VBlank IRQ
|
||||
reti ; Do nothing
|
||||
.org $0048 ; LCDC Status IRQ
|
||||
reti ; Do nothing
|
||||
.org $0050 ; Timer Owerflow
|
||||
reti ; Do nothing
|
||||
.org $0058 ; Serial Transfear Completion
|
||||
reti ; Do nothing
|
||||
.org $0060 ; Hmm, this is a wierd on
|
||||
; Im not sure how this one works
|
||||
; Transition from high to low
|
||||
; of pin p10-p13
|
||||
; I think Its a hardware thing
|
||||
reti ; Do nothing :)
|
||||
; Irqs done..
|
||||
|
||||
.org $0100
|
||||
|
||||
; GameBoy Header with correct checksum
|
||||
.db $00,$C3,$50,$01,$CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83
|
||||
.db $00,$0C,$00,$0D,$00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6
|
||||
.db $DD,$DD,$D9,$99,$BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F
|
||||
.db $BB,$B9,$33,$3E ; Standard Nintendo DO NOT CHANGE...
|
||||
|
||||
.db "MY FIRST GAME " ; Cart name 16bytes
|
||||
.db $00,$00,$00 ; Not used
|
||||
.db $00 ; Cart type ROM Only
|
||||
.db $00 ; ROM Size 32k
|
||||
.db $00 ; RAM Size 0k
|
||||
.db $fe,$ba ; Maker ID $bafe=Genetic Fantasia
|
||||
.db $01 ; Version =1
|
||||
.db $DA ; Complement check (Important)
|
||||
.db $ff,$ff ; Cheksum, fix this if you are going to
|
||||
; run this in VGB, or simpy use the -NOCRC
|
||||
; switch in the VGB-DOS command line.
|
||||
|
||||
start ; This is addr $0150
|
||||
ld sp,$fff4 ; Put the stack where the GB wants it
|
||||
ld a,%00000000 ; No IRQs at all
|
||||
ldh ($ff),a
|
||||
sub a ; Misc standard init things..
|
||||
ldh ($41),a ; LCDC Status
|
||||
ldh ($42),a ; Screen scroll Y=0
|
||||
ldh ($43),a ; Screen scroll X=0
|
||||
|
||||
(Start program here)
|
||||
|
||||
Here we are! Our first delve into a program. Now let's make a map
|
||||
and a character set. Use GFTILE to draw a character set and save it as a binary,
|
||||
and also save it as a TASM file called "chr.tsm". Draw a map and save it as binary
|
||||
and also as a tasm file "map.tsm". Make sure the map dimensions are 32x32 before
|
||||
you save it! The gameboy screen routine we will be writing will be 32 characters
|
||||
wide by 32 characters tall.
|
||||
|
||||
|
||||
After that, we will have to set up the screen. By setting all the bits to on,
|
||||
the remarks on the right become effective. If we turn on the screen, we will set
|
||||
bit 1 to 1, (I.E. a,%10000000)
|
||||
|
||||
ld a,%00000000 ; LCD Controller = Off (No picture on screen)
|
||||
; WindowBank = $9800 (Not used)
|
||||
; Window = OFF
|
||||
; BG Chr = $8000
|
||||
; BG Bank= $9800
|
||||
; Sprites= 8x8 (Size Assembly, 1=8x16)
|
||||
; Sprites= Off (Sprites on or off)
|
||||
; BG = On
|
||||
ldh ($40),a
|
||||
|
||||
The gameboy has only four colors to deal with, making life both easy and difficult
|
||||
for you. Let's set up the palette for the screen and the future implementation of
|
||||
sprites to the same palette configuration we used in GFTILE, the index is in bit
|
||||
order (I.E. Color index0=11, index1=01, etc.). The reason I set up the sprite
|
||||
palettes now is to prevent confusion later. You have two sprite palettes that each
|
||||
sprite can choose from. For now, we will make them both the same as the background
|
||||
palette:
|
||||
|
||||
nor_col ; Sets the colors to normal palette
|
||||
ld a,%11011000 ; grey 3=11 (Black)
|
||||
; grey 2=10 (Dark grey)
|
||||
; grey 1=01 (Ligth grey)
|
||||
; grey 0=00 (Transparent)
|
||||
ldh ($47),a
|
||||
ldh ($48),a ; 48,49 are sprite palettes
|
||||
; set same as background
|
||||
ldh ($49),a
|
||||
ret
|
||||
|
||||
|
||||
Now we need to write the routines that will load the character set that you drew
|
||||
into the character memory ($8000), and the map onto the screen ($9800).
|
||||
|
||||
mchar
|
||||
ld hl,$8000
|
||||
ld d,$00 ; move 256 bytes
|
||||
ld e,$12 ; x2=512. Increase this value
|
||||
; to copy more of the character set.
|
||||
mchar_loop
|
||||
ldh a,($41) ; There are only certain times you
|
||||
and 2 ; can write out scrn/tile. This
|
||||
jr nz,mchar_loop ; loop waits for those times.
|
||||
ld a,(bc)
|
||||
ld (hli),a
|
||||
inc bc
|
||||
dec d
|
||||
jp nz,mchar_loop
|
||||
dec e
|
||||
jp nz,mchar_loop
|
||||
ret
|
||||
|
||||
map
|
||||
ld hl,$9800
|
||||
ld d,$00
|
||||
ld e,$04 ; 256*4=1024=32x32=One whole GB Screen
|
||||
map_loop
|
||||
ldh a,($41)
|
||||
and 2
|
||||
jr nz,map_loop
|
||||
ld a,(bc)
|
||||
ld (hli),a
|
||||
inc bc
|
||||
dec d
|
||||
jp nz,map_loop
|
||||
dec e
|
||||
jp nz,map_loop
|
||||
ret
|
||||
|
||||
Also, we'll add a routine to clear any sprites that may be randomly lingering around
|
||||
the screen.
|
||||
|
||||
spr_cls ; Clear Sprites
|
||||
ld l,0
|
||||
ld h,$ce
|
||||
spclear
|
||||
ld a,$ff
|
||||
ld (hl),a
|
||||
inc l
|
||||
ld a,l
|
||||
cp $ff
|
||||
jp nz,spclear
|
||||
ret
|
||||
|
||||
Now, let's include our Tasm files we made with GFTILE:
|
||||
|
||||
chrset:
|
||||
#include "chr.tsm"
|
||||
|
||||
chrmap:
|
||||
#include "map.tsm"
|
||||
|
||||
If you do not have a map/tile editor, then create these files:
|
||||
|
||||
"chr.tsm"
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$82,$82,$C6,$C6
|
||||
.DB $EE,$EE,$BA,$BA,$92,$92,$82,$82,$0,$0,$0,$0
|
||||
.DB $0,$0,$C0,$C0,$23,$23,$1E,$1E,$C,$C,$78,$78
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$8,$8
|
||||
.DB $F,$F,$8,$8,$8,$8,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$1C,$1C,$E0,$E0,$0,$0,$0,$0,$F,$F
|
||||
.DB $8,$8,$8,$8,$8,$8,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$80,$80,$10,$10,$0,$0,$10,$10,$10,$10
|
||||
.DB $18,$18,$8,$8,$0,$0,$8,$8,$78,$78,$C8,$C8
|
||||
.DB $CC,$CC,$7E,$7E,$33,$33,$61,$61,$40,$40,$0,$0
|
||||
.DB $3C,$3C,$70,$70,$C,$C,$4,$4,$EC,$EC,$78,$78
|
||||
.DB $0,$0,$0,$0,$20,$20,$20,$20,$7C,$7C,$20,$20
|
||||
.DB $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$30,$30
|
||||
.DB $18,$18,$C,$C,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$3E,$3E,$7E,$7E,$60,$60,$60,$60,$6E,$6E
|
||||
.DB $6E,$6E,$0,$0,$0,$0,$38,$38,$7C,$7C,$C6,$C6
|
||||
.DB $C6,$C6,$FE,$FE,$FE,$FE,$0,$0,$0,$0,$C3,$C3
|
||||
.DB $E7,$E7,$FF,$FF,$DB,$DB,$C3,$C3,$C3,$C3,$0,$0
|
||||
.DB $0,$0,$7F,$7F,$7F,$7F,$60,$60,$60,$60,$7C,$7C
|
||||
.DB $60,$60,$0,$0,$0,$0,$7E,$7E,$7F,$7F,$63,$63
|
||||
.DB $63,$63,$7E,$7E,$63,$63,$0,$0,$0,$0,$3E,$3E
|
||||
.DB $7F,$7F,$63,$63,$63,$63,$63,$63,$63,$63,$0,$0
|
||||
.DB $0,$0,$63,$63,$63,$63,$63,$63,$77,$77,$3E,$3E
|
||||
.DB $1C,$1C,$66,$66,$7E,$7E,$3E,$3E,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$C6,$C6,$C6,$C6,$C6,$C6
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$C3,$C3
|
||||
.DB $C3,$C3,$C3,$C3,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$60,$60,$7F,$7F,$7F,$7F
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$63,$63
|
||||
.DB $7F,$7F,$7E,$7E,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$63,$63,$7F,$7F,$3E,$3E,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$1C,$1C,$1C,$1C,$1C,$1C
|
||||
|
||||
"map.tsm"
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$1,$2,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$3,$4,$0
|
||||
.DB $0,$9,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$5,$6,$7,$8,$A,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$B,$C,$D,$E
|
||||
.DB $F,$10,$11,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$12,$13,$14,$16,$17,$18,$19,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
.DB $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
|
||||
|
||||
|
||||
Now that we've type this all in, go back to the position just before the palette
|
||||
routine (nor_pal). Enter this code:
|
||||
|
||||
setup
|
||||
ld bc,chrset
|
||||
call mchar
|
||||
ld bc,chrmap
|
||||
call map
|
||||
call nor_col
|
||||
call spr_cls
|
||||
ld a,%10010101 ; LCD Controller = Off (No picture on screen)
|
||||
; WindowBank = $9800 (Not used)
|
||||
; Window = OFF
|
||||
; BG Chr = $8000
|
||||
; BG Bank= $9800
|
||||
; Sprites= 8x8 (Size Assembly, 1=8x16)
|
||||
; Sprites= Off (Sprites on or off)
|
||||
; BG = On
|
||||
ldh ($40),a
|
||||
loop
|
||||
jr loop
|
||||
|
||||
Now, finally, at the end of our program, type:
|
||||
|
||||
.block $008000-$
|
||||
.end
|
||||
|
||||
This tells Tasm we are done and to make the binary 32k. Now save your file as
|
||||
prog1.asm.
|
||||
|
||||
|
||||
TIME TO COMPILE!
|
||||
|
||||
Here is the big moment. Using TASM, type:
|
||||
|
||||
TASM -69 -b prog1.asm prog1.gb
|
||||
|
||||
This will now compile your program. "-69" tells tasm to use the gameboy opcode
|
||||
table, and "-b" tells TASM to create a binary image. It will do all this in file
|
||||
PROG1.GB
|
||||
|
||||
To run this on a real gameboy or vgb, you now need to use GBTOOL and type these two
|
||||
command lines in the following order (IMPORTANT!):
|
||||
|
||||
GBTOOL C PROG1.GB
|
||||
|
||||
(Answer yes to inquiry)
|
||||
|
||||
GBTOOL F PROG1.GB
|
||||
|
||||
(Answer yes to inquiry)
|
||||
|
||||
You may also use the crcfix programs available on the net, feel free to experiment
|
||||
as this can be tedious everytime you try your program. If you want to bypass this
|
||||
step with VGB-DOS, you can. Without doing the above, type:
|
||||
|
||||
VGB-DOS PROG1.GB -NOCRC
|
||||
|
||||
This tells VGB-DOS to not worry about the CRC check or Complement check (Nintendo's
|
||||
cartridge protection).
|
||||
|
||||
|
||||
NOW! If you are using VGB-DOS, type:
|
||||
|
||||
VGB-DOS PROG1.GB
|
||||
|
||||
Voila! The program runs!
|
||||
|
||||
If you have uploaded this binary to the Smart Cart, make sure you rename the file
|
||||
to GBPROG1.GB (Just in case) and try it out. Bingo!
|
||||
|
||||
This is your first lesson.
|
||||
|
||||
The final file should look like:
|
||||
|
||||
|
||||
|
||||
.org $0000 ; Start of the binary
|
||||
|
||||
.org $0040 ; VBlank IRQ
|
||||
reti ; Do nothing
|
||||
.org $0048 ; LCDC Status IRQ
|
||||
reti ; Do nothing
|
||||
.org $0050 ; Timer Owerflow
|
||||
reti ; Do nothing
|
||||
.org $0058 ; Serial Transfear Completion
|
||||
reti ; Do nothing
|
||||
.org $0060 ; Hmm, this is a wierd on
|
||||
; Im not sure how this one works
|
||||
; Transition from high to low
|
||||
; of pin p10-p13
|
||||
; I think Its a hardware thing
|
||||
reti ; Do nothing :)
|
||||
; Irqs done..
|
||||
|
||||
.org $0100
|
||||
|
||||
; GameBoy Header with correct checksum
|
||||
.db $00,$C3,$50,$01,$CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83
|
||||
.db $00,$0C,$00,$0D,$00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6
|
||||
.db $DD,$DD,$D9,$99,$BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F
|
||||
.db $BB,$B9,$33,$3E ; Standard Nintendo DO NOT CHANGE...
|
||||
|
||||
.db "MY FIRST GAME " ; Cart name 16bytes
|
||||
.db $00,$00,$00 ; Not used
|
||||
.db $00 ; Cart type ROM Only
|
||||
.db $00 ; ROM Size 32k
|
||||
.db $00 ; RAM Size 0k
|
||||
.db $fe,$ba ; Maker ID $bafe=Genetic Fantasia
|
||||
.db $01 ; Version =1
|
||||
.db $DA ; Complement check (Important)
|
||||
.db $ff,$ff ; Cheksum, fix this if you are going to
|
||||
; run this in VGB, or simpy use the -NOCRC
|
||||
; switch in the VGB-DOS command line.
|
||||
|
||||
start ; This is addr $0150
|
||||
ld sp,$fff4 ; Put the stack where the GB wants it
|
||||
ld a,%00000000 ; No IRQs at all
|
||||
ldh ($ff),a
|
||||
sub a ; Misc standard init things..
|
||||
ldh ($41),a ; LCDC Status
|
||||
ldh ($42),a ; Screen scroll Y=0
|
||||
ldh ($43),a ; Screen scroll X=0
|
||||
|
||||
ld a,%00000000 ; LCD Controller = Off (No picture on screen)
|
||||
; WindowBank = $9800 (Not used)
|
||||
; Window = OFF
|
||||
; BG Chr = $8000
|
||||
; BG Bank= $9800
|
||||
; Sprites= 8x8 (Size Assembly, 1=8x16)
|
||||
; Sprites= Off (Sprites on or off)
|
||||
; BG = On
|
||||
ldh ($40),a
|
||||
|
||||
setup
|
||||
ld bc,chrset
|
||||
call mchar
|
||||
ld bc,chrmap
|
||||
call map
|
||||
call nor_col
|
||||
call spr_cls
|
||||
ld a,%10010101 ; LCD Controller = Off (No picture on screen)
|
||||
; WindowBank = $9800 (Not used)
|
||||
; Window = OFF
|
||||
; BG Chr = $8000
|
||||
; BG Bank= $9800
|
||||
; Sprites= 8x8 (Size Assembly, 1=8x16)
|
||||
; Sprites= Off (Sprites on or off)
|
||||
; BG = On
|
||||
ldh ($40),a
|
||||
loop
|
||||
jr loop
|
||||
|
||||
|
||||
nor_col ; Sets the colors to normal palette
|
||||
ld a,%11011000 ; grey 3=11 (Black)
|
||||
; grey 2=10 (Dark grey)
|
||||
; grey 1=01 (Ligth grey)
|
||||
; grey 0=00 (Transparent)
|
||||
ldh ($47),a
|
||||
ldh ($48),a ; 48,49 are sprite palettes
|
||||
; set same as background
|
||||
ldh ($49),a
|
||||
ret
|
||||
|
||||
mchar
|
||||
ld hl,$8000
|
||||
ld d,$00 ; move 256 bytes
|
||||
ld e,$12 ; x2=512. Increase this value
|
||||
; to copy more of the character set.
|
||||
mchar_loop
|
||||
ldh a,($41) ; There are only certain times you
|
||||
and 2 ; can write out scrn/tile. This
|
||||
jr nz,mchar_loop ; loop waits for those times.
|
||||
ld a,(bc)
|
||||
ld (hli),a
|
||||
inc bc
|
||||
dec d
|
||||
jp nz,mchar_loop
|
||||
dec e
|
||||
jp nz,mchar_loop
|
||||
ret
|
||||
|
||||
map
|
||||
ld hl,$9800
|
||||
ld d,$00
|
||||
ld e,$04 ; 256*4=1024=32x32=One whole GB Screen
|
||||
map_loop
|
||||
ldh a,($41)
|
||||
and 2
|
||||
jr nz,map_loop
|
||||
ld a,(bc)
|
||||
ld (hli),a
|
||||
inc bc
|
||||
dec d
|
||||
jp nz,map_loop
|
||||
dec e
|
||||
jp nz,map_loop
|
||||
ret
|
||||
|
||||
spr_cls ; Clear Sprites
|
||||
ld l,0
|
||||
ld h,$ce
|
||||
spclear
|
||||
ld a,$ff
|
||||
ld (hl),a
|
||||
inc l
|
||||
ld a,l
|
||||
cp $ff
|
||||
jp nz,spclear
|
||||
ret
|
||||
|
||||
chrset:
|
||||
#include "chr.tsm"
|
||||
|
||||
chrmap:
|
||||
#include "map.tsm"
|
||||
|
||||
.block $008000-$
|
||||
.end
|
@@ -1,63 +0,0 @@
|
||||
Brief introduction to Gameboy Color art
|
||||
Written by Jason
|
||||
|
||||
The Gameboy Color doesn't use APA (all points accessable) graphics such
|
||||
as your computer uses. Instead it uses tiles to draw everything. While
|
||||
this has certain advantages, it also makes simple things such as
|
||||
drawing a line really difficult.
|
||||
|
||||
The Gameboy Color uses 8x8 pixel tiles to create the background. The
|
||||
Gameboy Color's screen has a resolution of 20x18 tiles or 160x144 pixels.
|
||||
Inside its ram it has the ability to store 32x32 tiles or 256x256 pixels.
|
||||
This is useful for games that scroll the background. The Gameboy Color
|
||||
can display 256 unique tiles on the screen at one time. It has an additional
|
||||
RAM bank to display an additional 256 tiles, although this kills any chance
|
||||
of backwards compatability with the original Gameboy.
|
||||
|
||||
Sprites are tiles independant of the background. These are used for things
|
||||
such as the main character and badguys. The Gameboy Color can display either
|
||||
8x8 or 8x16 pixels per tile. It can display 40 sprites simultaniously. One
|
||||
of its main drawbacks is that it can only display 10 sprites per horizontal
|
||||
line. The Gameboy Color features a few hardware sprite manipulations. While
|
||||
it does not feature resizing the sprites or more useful things, you can
|
||||
flip the horizontal and vertical axis on each sprite. This is useful
|
||||
to make a character walk in 2 directions without needing to load a new
|
||||
tile into the sprite. The Gameboy Color can hold 256 8x8 pixel tiles
|
||||
or 128 8x156 pixel tiles into sprite ram. This is useful since it takes
|
||||
alot less computing power to change which ram location to display the tile
|
||||
than to load a unique tile into its place. You can layer sprites on top
|
||||
of each other to add an additional 3 colors to that tile. Keep in
|
||||
consideration the 10 sprites per horizontal line when you do this, though.
|
||||
You can fake create more sprites by changing their location during horizontal
|
||||
blank, however this isn't really useful since horizontal blank is rather
|
||||
quick and it is rarely needed to have the exact same sprite in multiple
|
||||
locations.
|
||||
|
||||
The Gameboy Color can display 56 simultanious colors. The background consists
|
||||
of 8 palettes. Each palette contains 4 colors to fill each tile. This creates
|
||||
the first 32 colors. The other 24 are created by the sprites. The sprites
|
||||
have 8 palettes of 3 colors. The loss of the fourth color is caused by
|
||||
sprites needing one color (default white with GBTD) to be transparent.
|
||||
Without this, every sprite would have to be square shaped. There is a trick
|
||||
to create more colors by changing palettes during horizontal blank, however
|
||||
this doesn't prove to be very useful in gameplay as the Gameboy Color has
|
||||
to stop the gameplay to update the palettes, and if one palette isn't updated
|
||||
in time, the picture will become miscolored. A faster CPU would eliminate
|
||||
this problem, but with the Gameboy Advance being released soon, Gameboy
|
||||
Color artists just have to deal with it for the time being.
|
||||
|
||||
The window is a nontransparent layer that can be applied on top of the
|
||||
background. It does not use its own palettes, but rather shares its
|
||||
palettes with the background's palettes. The window is useful for things
|
||||
such as pop-up maps, status bars that don't scroll with the background,
|
||||
and many other uses.
|
||||
|
||||
In order to prevent graphical glitches from being displayed, sprite and
|
||||
background data should only be updated when the screen is not rendering.
|
||||
Good times to do this are during vertical blank and horizontal blank.
|
||||
|
||||
Unfortunately that is about it for the Gameboy Color's display.
|
||||
|
||||
|
||||
|
||||
|
@@ -1,26 +0,0 @@
|
||||
GameBoy CPU Timing v0.01 13.04.2000/GPZ-HIT
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
standard gameboy:
|
||||
|
||||
Clock Speed: 4.194304 MHz - 0,2384 <20>s per cycle
|
||||
|
||||
cycles=microseconds/0,2384
|
||||
microseconds=cycles*0,2384
|
||||
|
||||
Horiz Sync: 9198 KHz - 456 Cycles per line (109<30>s)
|
||||
Vert Sync: 59.73 Hz - 70224 Cycles per frame (16.6ms)
|
||||
|
||||
color gameboy at 2x speed:
|
||||
|
||||
Clock Speed: 8.388688 MHz - 0,1192 <20>s per cycle
|
||||
Horiz Sync: 9198 KHz - 912 Cycles per line (109<30>s)
|
||||
Vert Sync: 59.73 Hz - 140448 Cycles per frame (16.6ms)
|
||||
|
||||
super gameboy:
|
||||
|
||||
Clock Speed: 4.295454 MHz
|
||||
Horiz Sync: 9420 KHz
|
||||
Vert Sync: 61.17 Hz
|
||||
|
||||
|
@@ -1,89 +0,0 @@
|
||||
GameBoy Color DMA-Transfers v0.0.1 13.04.2000/GPZ-HIT
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
"old" DMA Transfer (aka 'OAM-DMA')
|
||||
----------------------------------
|
||||
|
||||
source: - anywhere from $0000 to $dfff
|
||||
- must be page-aligned
|
||||
destination: - always OAM-Ram at $fe00 - $fe8f
|
||||
length: - always 4*40 (=160 / $a0) bytes
|
||||
|
||||
- when cpu speed is 2x, transfer speed is also 2x
|
||||
- transfer takes 671 cycles (160 microseconds)
|
||||
|
||||
rDMA $FF46 - source LSB
|
||||
|
||||
CAUTION:
|
||||
|
||||
- dma should be executed during v-blank period
|
||||
- no memory except for HRAM is available for the cpu while DMA is in progress,
|
||||
the common method is to place this routine in HRAM:
|
||||
|
||||
dma:
|
||||
ld a,$c0 ; oam-buffer is at $c000
|
||||
ld [rDMA],a
|
||||
|
||||
ld a,$28 ; wait until DMA completes (160 microseconds)
|
||||
.lp
|
||||
dec a
|
||||
jr nz,.lp
|
||||
ret
|
||||
|
||||
|
||||
"new" DMA Transfer
|
||||
------------------
|
||||
|
||||
source: - from $0000 to $7fff and $a000-$dfff
|
||||
- must be page-aligned
|
||||
destination: - always in VRAM at $8000-$9fff
|
||||
- must be page-aligned
|
||||
length: - can be set in 16-bytes increments (ie, its ALWAYS n*16 long),
|
||||
(1*16)=$0010 to (128*16)=$0800 bytes
|
||||
|
||||
- DMA speed is constant regardless of CPU-Speed
|
||||
- cpu is halted during dma transfer
|
||||
- transfer takes 220+n*7,68 microseconds (1x speed)
|
||||
110+n*7,68 microseconds (2x speed)
|
||||
|
||||
- has 2 different modes:
|
||||
1) Horizontal blanking DMA (aka 'HDMA')
|
||||
- transfers 16 bytes at each horizontal blank
|
||||
- can be stopped/restarted at each h-blank period
|
||||
2) General Purpose DMA (aka 'GDMA')
|
||||
- can not be stopped or suspended once started
|
||||
|
||||
rHDMA1 $ff51 bit 7-0 bit 7-0 of Source MSB
|
||||
rHDMA2 $ff52 bit 7-4 bit 7-4 of Source LSB
|
||||
bit 3-0 unused (set to 0)
|
||||
rHDMA3 $ff53 bit 7-5 unused (set to 0)
|
||||
bit 4-0 bit 4-0 of Destination MSB
|
||||
rHDMA4 $ff54 bit 7-4 bit 7-4 of Destination LSB
|
||||
bit 3-0 unused (set to 0)
|
||||
rHDMA5 $ff55 bit 7 DMA Mode / Control
|
||||
|
||||
read:
|
||||
0 : DMA in progress
|
||||
1 : DMA completed
|
||||
|
||||
write:
|
||||
- after set to 0, GDMA starts
|
||||
- after set to 1, HDMA starts at next h-blank
|
||||
- once started, HDMA can be stopped at next
|
||||
h-blank by setting this bit to 0
|
||||
|
||||
bit 6-0 length (number of 16-byte blocks - 1)
|
||||
- amount of bytes transferred is (n+1)*$10
|
||||
|
||||
CAUTION:
|
||||
|
||||
- don't switch ROM banks if DMA source is in the range of $4000-$7fff
|
||||
- don't switch ROM banks if DMA source is in the range of $d000-$dfff
|
||||
- don't switch VRAM banks until DMA has completed
|
||||
- halt can not be used while a DMA is in progress
|
||||
- dma must have completed before another dma is started or registers are
|
||||
altered
|
||||
- display must be enabled for a HDMA transfer taking place
|
||||
- GDMA is only reliable during v-blank when display is enabled
|
||||
|
||||
|
@@ -1,75 +0,0 @@
|
||||
GameBoy Video-Interupts v0.01 201299/GPZ-HIT
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
Type priority jumps to
|
||||
|
||||
V-Blank highest $0040
|
||||
LCDC . $0048
|
||||
Timer . $0050
|
||||
serial . $0058
|
||||
pad lowest $0060
|
||||
|
||||
|
||||
general IRQ registers:
|
||||
----------------------
|
||||
|
||||
$ffff (rIE) IRQ Enable (read/write)
|
||||
|
||||
bit:
|
||||
|
||||
7-5 ?
|
||||
4 Transition High=>Low Pin P10-P13 0:disabled 1:enabled
|
||||
3 serial i/o transfer complete 0:disabled 1:enabled
|
||||
2 Timer overflow 0:disabled 1:enabled
|
||||
1 LCDC 0:disabled 1:enabled
|
||||
0 V-Blank 0:disabled 1:enabled
|
||||
|
||||
$ff0f (rIF) IRQ Flag (read)
|
||||
|
||||
- if an interupt has occured, this register tells what kind
|
||||
of interupt it was.
|
||||
|
||||
bit:
|
||||
|
||||
7-5 ?
|
||||
4 Transition High=>Low Pin P10-P13
|
||||
3 serial i/o transfer complete
|
||||
2 Timer overflow
|
||||
1 LCDC
|
||||
0 V-Blank
|
||||
|
||||
LCDC IRQ-Registers:
|
||||
-------------------
|
||||
|
||||
$ff44 (rLY) LCDC Y-Coordinate
|
||||
|
||||
read: current Rasterline
|
||||
write: reset lcd-y counter
|
||||
|
||||
$ff45 (rLYC) LY-Compare (read/write)
|
||||
|
||||
- if rLYC = rLY then bit 2 of rSTAT is set
|
||||
|
||||
$ff41 (rSTAT) LCDC Status
|
||||
|
||||
bit:
|
||||
|
||||
7 ?
|
||||
|
||||
IRQ source selection:
|
||||
|
||||
6 rLYC=rLY <
|
||||
5 mode %10 (searching OAM) | write
|
||||
4 mode %01 (V-Blank) |
|
||||
3 mode %00 (H-Blank) <
|
||||
|
||||
2 rLYC=rLY Flag <
|
||||
|
|
||||
1-0 current mode: |
|
||||
|
|
||||
%00 H-Blank | read
|
||||
%01 V-Blank |
|
||||
%10 searching OAM |
|
||||
%11 transfering data to LCD <
|
||||
|
@@ -1,42 +0,0 @@
|
||||
GameBoy Video-timing v0.01 201299/GPZ-HIT
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
- all values referring to CPU at 'single' speed (CPU-clock 4.194304 MHz,
|
||||
0,2384 <20>s per cycle)
|
||||
|
||||
|
||||
Pixel
|
||||
0 159 ?
|
||||
Raster 0 -|-----------------------------|--------------- < <
|
||||
Line | | | |
|
||||
| OAM-Search Data Transfer | H-Blank | |
|
||||
| ----------> --------------> | -------------> | |
|
||||
| (mode %10) (mode %11) | (mode %00) | 65664 | 70224
|
||||
| | | Cycles | Cycles
|
||||
| | | (15.5ms) | (16.6ms)
|
||||
| | | |
|
||||
| | | | total
|
||||
| | | | per
|
||||
| | | | frame
|
||||
$90 143 -|-----------------------------|- V-Blank < 4560 |
|
||||
| vertical offscreen area | (mode %01) | Cycles |
|
||||
$9a 153 ----------------------------------------------- < (1.08ms) <
|
||||
|
||||
^------------^----------------^----------------^
|
||||
77-83 Cycles 169-297 Cycles 78-207 Cycles (*1)
|
||||
|
||||
^----------------------------------------------^
|
||||
456 Cycles (109<30>s) total per line
|
||||
|
||||
|
||||
(*1) timing in a line depends on the amount of sprites to be displayed:
|
||||
|
||||
sprites mode %10 mode %11 mode %00
|
||||
in line
|
||||
|
||||
0 ? (41.37<EFBFBD>s) 204 Cycles (48.6<EFBFBD>s)
|
||||
|
||||
10 ? 297 Cycles (70.69<EFBFBD>s) 78 Cycles (18.7<EFBFBD>s)
|
||||
|
||||
|
||||
|
@@ -1,95 +0,0 @@
|
||||
GameBoy Color Memormap v0.01 200300/GPZ-HIT
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
General Memory Map Hardware Write Registers
|
||||
------------------ ------------------------
|
||||
|
||||
0000 ---------------------------
|
||||
16kB ROM bank #0 0000-1FFF rRAMG MBC5+Ram RAM-Bank Enable ($00 == disabnled/$0a == enabled)
|
||||
- 0000-00ff header 2000-2FFF rROMB0 ROM-Bank LSB
|
||||
- hardware vectors 3000-3FFF rROMB1 ROM-Bank MSB (only bit 0 used)
|
||||
- 0100 start of user code,
|
||||
usally:
|
||||
NOP
|
||||
jp START
|
||||
- nintendo logo
|
||||
- cartridge hardware
|
||||
info
|
||||
--------------------------- 014F
|
||||
0150 ---------------------------
|
||||
~16kb ROM 'Home' Bank
|
||||
START: should be located
|
||||
here somewhere
|
||||
--------------------------- 3FFF
|
||||
4000 ---------------------------
|
||||
16kB switchable ROM bank 4000 Rumble Motor on Rumble-Cards (obvious =P)
|
||||
4000-5FFF rRAMB MBC5+Ram RAM-Bank (bit 3-0 used)
|
||||
6000-7FFF MBC1 ROM/RAM Select
|
||||
--------------------------- 7FFF
|
||||
8000 ---------------------------
|
||||
8kB Video RAM
|
||||
|
||||
- switchable config, refer to reg. $ff40 (rLCDC)
|
||||
|
||||
STD-Config 1|Bank 0 |Bank 1
|
||||
------------+--------------+---------------
|
||||
8000-8FFF |Charset-Tiles |Charset-Tiles
|
||||
|$00-$FF/b0 |$00-$FF/b1
|
||||
9000-97FF |Sprite-Tiles |Sprite-Tiles
|
||||
|$00-$7F/b0 |$00-$7F/b1
|
||||
9800-9BFF |Window-Map |Window-Attribs
|
||||
9C00-9FFF |Screen-Map |Screen-Attribs
|
||||
|
||||
(*) 9800-9FFF can be used for Sprite-Tiles
|
||||
$80-$FF if not used otherwhise
|
||||
|
||||
STD-Config 2|Bank 0 |Bank 1
|
||||
------------+--------------+---------------
|
||||
8000-87FF |Sprite-Tiles |Sprite-Tiles
|
||||
|$00-$7F/b0 |$00-$7F/b1
|
||||
8800-8FFF |shared Sprite+|shared Sprite+
|
||||
|Charset-Tiles0|Charset-Tiles
|
||||
|$80-$FF/b0 |$80-$FF/b1
|
||||
9000-97FF |Charset-Tiles |Charset-Tiles
|
||||
|$00-$7F/b0 |$00-$7F/b1
|
||||
9800-9BFF |Screen-Map |Screen-Attribs
|
||||
9C00-9FFF |Window-Map |Window-Attribs
|
||||
|
||||
--------------------------- 9FFF
|
||||
A000 ---------------------------
|
||||
8kB switchable RAM bank
|
||||
--------------------------- BFFF
|
||||
C000 ---------------------------
|
||||
4kB Internal RAM
|
||||
--------------------------- CFFF
|
||||
D000 ---------------------------
|
||||
4kB Internal RAM Banks 0-7
|
||||
--------------------------- DFFF
|
||||
E000 ---------------------------
|
||||
Echo of 8kB Internal RAM
|
||||
--------------------------- FDFF
|
||||
FE00 ---------------------------
|
||||
Sprite Attrib Memory (OAM)
|
||||
--------------------------- FE9F
|
||||
FEA0 ---------------------------
|
||||
Empty but unusable for I/O
|
||||
--------------------------- FEFF
|
||||
FF00 ---------------------------
|
||||
I/O ports / Hardware regs.
|
||||
--------------------------- FF4B
|
||||
FF4C --------------------------- FF40 rSVBK GBC internal RAM Bank Select
|
||||
GBC I/O - Hardware area
|
||||
--------------------------- FF7F
|
||||
FF80 ---------------------------
|
||||
Internal RAM - "Zero-Page"
|
||||
- stack is located here by
|
||||
default (after reset)
|
||||
--------------------------- FFFE
|
||||
FFFF ---------------------------
|
||||
Interrupt Enable Register
|
||||
--------------------------- FFFF
|
||||
|
||||
|
||||
|
||||
|
@@ -1,58 +0,0 @@
|
||||
ISTAT98.TXT 1998 BY MARTIN KORTH
|
||||
--------------------------------
|
||||
|
||||
|
||||
Interrupt INT 48h - LCD STAT
|
||||
----------------------------
|
||||
|
||||
|
||||
The STAT register (FF41) selects the conditions that will generate this
|
||||
interrupt (expecting that interrupts are enabled via EI or RETI and that
|
||||
IE.1 (FFFF.1) is set).
|
||||
STAT.3 HBLANK (start of mode 0)
|
||||
STAT.4 VBLANK (start of mode 1) (additional to INT 40)
|
||||
STAT.5 OAM (start of mode 2 and mode 1)
|
||||
STAT.6 LY=LYC (see info about LY=00)
|
||||
|
||||
|
||||
If two STAT-condiditions come true at the same time only one INT 48 is
|
||||
generated. This happens in combinations
|
||||
LYC=01..90 and OAM at the same time (see info about LY=00)
|
||||
LYC=90 and VBLANK at the same time
|
||||
OAM and VBLANK at the same time
|
||||
HBLANK and LYC=00 and LYC=91..99 are off-grid and cannot hit others.
|
||||
|
||||
|
||||
Some STAT-conditions cause the following STAT-condition to be ignored:
|
||||
Past VBLANK following LYC=91..99,00 is ignored
|
||||
Past VBLANK following OAM (at 00) is ignored
|
||||
Past LYC=00 at 99.2 following OAMs (at 00 and 01) are ignored
|
||||
Past LYC=01..8F following OAM (at 02..90) is ignored
|
||||
Past LYC=00..8F following HBLANK (at 00..8F) is ignored
|
||||
Past LYC=8F following VBLANK is ignored
|
||||
Past HBLANK following OAM is ignored
|
||||
Past HBLANK at 8F following VBLANK is ignored
|
||||
|
||||
|
||||
If the OAM condition occurs, everything following -is- recognized.
|
||||
An ignored VBLANK condition means that INT 48h does not produce a V-Blank
|
||||
interrupt, INT 40h is not affected and still produces V-Blank interrupts.
|
||||
|
||||
|
||||
The last LY period (LY=99) is a shorter than normal LY periodes. It is followed
|
||||
by the first LY period (LY=00) this period is longer than normal periodes.
|
||||
LY value clks description
|
||||
-------------------------------
|
||||
LY=01..8F 456 at the same moment than OAM
|
||||
LY=90 456 at the same moment than pseudo-OAM and VBLANK
|
||||
LY=91..98 456 during vblank (similiar to LY=01..8F)
|
||||
LY=99 ca.56 similiar to LY=91..98, but shorter
|
||||
LY=00 ca.856 starts during vblank, then present until second OAM
|
||||
Because of the pre-started long LY=00 period, LYC=00 occurs within vblank
|
||||
period and before first OAM (where we would have expected it)
|
||||
|
||||
|
||||
*EOF*
|
||||
|
||||
|
||||
|
@@ -1,36 +0,0 @@
|
||||
I have been recently coding some serial transfer routines and have found a
|
||||
few interesting things that I thought may be useful to anyone also
|
||||
contemplating doing link up.
|
||||
|
||||
Keeping the two gameboys in sync is the most difficuly things to do as the
|
||||
vblanks are almost always different.
|
||||
|
||||
A few things to bear in mind:
|
||||
|
||||
* Always delay the data by one frame. This gives an entire frame for
|
||||
transfer to occur which is good if one machine is behind.
|
||||
* Strangely, on performing a serial transfer (SC register set to $81) the
|
||||
serial interrupt occurs on the master first and then the slave.
|
||||
* On peforming a transfer and no machine is linked then SB will be $FF
|
||||
* On peforming a transfer and a machine is linked but swiched OFF then SB
|
||||
will be $0 (must be due to ground pin)
|
||||
* On peforming a transfer and a machine is linked but slave is in an
|
||||
interrupt then SB will be $FF
|
||||
* Keep all interrupt code as short as possible.
|
||||
* Be careful when using AC adaptors on gameboy Pockets as this sometimes
|
||||
causes interference on the serial port.
|
||||
|
||||
|
||||
If anyone is interested I may make my serial link code for GBDK(but using
|
||||
asm for interrupts) public.
|
||||
|
||||
|
||||
Hopefully this info helps someone at there in Game Boy land.......
|
||||
|
||||
Cheers
|
||||
|
||||
Ian.
|
||||
|
||||
|
||||
|
||||
|
@@ -1,65 +0,0 @@
|
||||
Memory Bank Controller (MBC) 3 for GameBoy Information
|
||||
|
||||
by bRILLO hEAD, 1-Jan-98
|
||||
|
||||
THe MBC3 is a memory controller for GameBoy that supports
|
||||
ROMs up to 16megabits (2megabytes) and RAM up to 256kbits
|
||||
(32kbytes). It has a built-in clock counter that requires
|
||||
an external 32.768KHz crystal for operation.
|
||||
|
||||
Reg 0 - $0000-$1fff - RAM/clock write protect
|
||||
|
||||
$0A - Enable
|
||||
$00 - Disable
|
||||
|
||||
Reg 1 - $2000-$3fff - ROM Bank Select
|
||||
|
||||
$00-$7F - Rom bank #
|
||||
|
||||
Reg 2 - $4000-$5fff - RAM Bank/Clock Reg Select
|
||||
|
||||
$00-$03 - RAM bank #
|
||||
$08-$0c - Clock register #
|
||||
|
||||
Reg 3 - $6000-$7fff - Latch clock counter data
|
||||
|
||||
Writing $00 and then $01 latches clock data.
|
||||
Another write $00 and write $01 is required
|
||||
to latch data again.
|
||||
|
||||
Reg SEC - $08 - Seconds counter
|
||||
|
||||
Reg MIN - $09 - Minutes counter
|
||||
|
||||
Reg HRS - $0A - Hours counter
|
||||
|
||||
Reg DAYL- $0B - Lower 8 bits of day counter
|
||||
|
||||
Reg DAYH- $0C - bit 0 = Upper 1 bit of day counter
|
||||
bit 6 = start(0)/stop(1) clock counter
|
||||
bit 7 = Day counter carry bit
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
Bit 7 of Reg DAYH once set stays set until a 0 is written.
|
||||
|
||||
To access the clock counter the ram bank must first be turned
|
||||
on by writing $0A to Reg 0.
|
||||
|
||||
To read the clock counter value, set Reg 3 to $01. This latches
|
||||
the values of all registers so they won't change on you while your
|
||||
trying to read them. However, this does not prevent the internal
|
||||
counters from keeping correct time. If reg 3 is already set to
|
||||
$01 then write $00 and then $01.
|
||||
|
||||
For example, to read Reg SEC write $08 to Reg 2. The value of
|
||||
Reg SEC can then be read from any address in $A000-$BFFF range.
|
||||
Use a similar process for writing to a register.
|
||||
|
||||
Due to slow MBC3/clock interfacing, 4 machine cycles (16 clock cycles)
|
||||
are required between register accesses.
|
||||
|
||||
|
||||
|
||||
|
@@ -1,294 +0,0 @@
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Multiple CPU Assembly Mnemonic Table v0.1 210299/GPZ-HIT
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Registers:
|
||||
----------
|
||||
|
||||
A : akku
|
||||
F : flag-register
|
||||
|
||||
B,C,D,E,H,L : 8080/z80 registers
|
||||
X,Y : 6502/6510 index registers
|
||||
|
||||
SP : 8080/z80 Stack Pointer
|
||||
AF,BC,DE,HL : 8080/z80 register pairs
|
||||
|
||||
PSW : alias for the AF register pair on 8080
|
||||
M : alias for the HL register pair on 8080
|
||||
|
||||
Flags:
|
||||
------
|
||||
|
||||
Z : zero flag
|
||||
C : carry flag
|
||||
H : 8080/z80 half-carry flag (carry from least significant nibble)
|
||||
N : 6502 negative (sign) flag
|
||||
8080/z80 substract flag (?!)
|
||||
|
||||
Arguments:
|
||||
----------
|
||||
|
||||
x8 : 8 bit constant that is implied with the opcode
|
||||
|
||||
p8 : 8 bit Port-Address
|
||||
|
||||
r8,r8' : one of the 8 bit registers
|
||||
r16,r16' : one of the register-pairs/16 bit registers
|
||||
|
||||
m8,m8' : 8 bit memory adress or offset (eg zeropage adressing modes)
|
||||
i8,i8' : 8 bit immediate value
|
||||
|
||||
m16,m16' : 16 bit memory adress
|
||||
i16,i16' : 16 bit immediate value
|
||||
|
||||
markers:
|
||||
--------
|
||||
|
||||
~ : opcode doesnt exactly match the comparison
|
||||
* : opcode is undocumented ('illegal')
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Gameboy note:
|
||||
-------------
|
||||
|
||||
zero-page location: $ff00-$ffff (only $ff80-$ffff useable)
|
||||
stack-location: set by user, SP after reset: $ffff
|
||||
opcode-syntax referring to: Rednex Gameboy Development System (asMotor of RGBDS)
|
||||
|
||||
6502 note:
|
||||
----------
|
||||
|
||||
since there is no HL register for pointers, absolute memory adresses are used
|
||||
instead. Also 6502 cpu's dont have the (limited) support for 16-bit stuff like
|
||||
some other 8 bit cpu's (aka register-pairs) so respective opcodes work in 8 bit
|
||||
only.
|
||||
|
||||
zero-page location: $0000-$00ff (only $0002-$00ff useable)
|
||||
stack-location: $0100-$01ff (fixed), SP after reset: $01ff
|
||||
opcode-syntax referring to: Omikron Turbo Assembler (c64)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
8080 Z80 Gameboy(~z80) 6502/6510
|
||||
------ ------ --------------- -----------
|
||||
|
||||
loading/storing data:
|
||||
---------------------
|
||||
MVI r8 [i8] LD r8, i8 LD r8, i8 LDA #i8 / LDX #i8 / LDY #i8
|
||||
MOV r8, r8' LD r8, r8' LD r8, r8' TAX / TXA / TAY / TYA / TXS / TSX
|
||||
|
||||
LDA [m8] [m8'] LD A, (m16) LD A, [m16] LDA m16
|
||||
LDAX B LD A, (BC) LD A, [BC] -
|
||||
LDAX D LD A, (DE) LD A, [DE] -
|
||||
LHLD [m8] [m8'] LD HL, (m16) LD HL, [m16] -
|
||||
LXI B [i8] [i8'] LD BC, i16 LD BC, i16 -
|
||||
LDID [i8] [i8'] LD DE, i16 LD DE, i16 -
|
||||
LXI H [i8] [i8'] LD HL, i16 LD HL, i16 -
|
||||
|
||||
STA [m8] [m8'] LD (m16), A LD [m16], A STA m16
|
||||
SHLD [m8] [m8'] LD (m16), HL LD [m16], HL -
|
||||
STAX B LD (BC), A LD [BC], A -
|
||||
STAX D LD (DE), A LD [DE], A -
|
||||
|
||||
MOV M, r8 LD (HL), r8 LD [HL], r8 -
|
||||
MOV r8, M LD r8, (HL) LD r8, [HL] -
|
||||
|
||||
MVI M [i8] LD (HL), i8 LD [HL], i8 -
|
||||
|
||||
store via HL and inc. HL - - LDI [HL],A -
|
||||
load via HL and inc. HL - - LDI A,[HL] -
|
||||
|
||||
store via HL and dec. HL - - LDD [HL],A -
|
||||
load via HL and dec. HL - - LDD A,[HL] -
|
||||
|
||||
load from zeropage - - LDH A,[$ff00+m8] LDA $00+m8
|
||||
store to zeropage - - LDH [$ff00+m8],A STA $00+m8
|
||||
|
||||
load indexed from zeropage - - LD A,[$ff00+C] LDA $00,Y
|
||||
store indexed to zeropage - - LD [$ff00+C],A STA $00,Y
|
||||
|
||||
|
||||
logical instructions:
|
||||
---------------------
|
||||
logical AND ANA M AND (HL) AND [HL] ~AND m16
|
||||
" ANA r8 AND r8 AND r8 -
|
||||
" ANI [i8] AND i8 AND i8 AND #i8
|
||||
logical OR ORA M OR (HL) OR [HL] ~OR m16
|
||||
" ORA r8 OR r8 OR r8 -
|
||||
" ORI [i8] OR i8 OR i8 ORA #i8
|
||||
logical EXCLUSIVE OR XRA M XOR (HL) XOR [HL] ~EOR m16
|
||||
" XRA r8 XOR r8 XOR r8 -
|
||||
" XRI [i8] XOR i8 XOR i8 EOR #i8
|
||||
|
||||
arithmetic instructions:
|
||||
------------------------
|
||||
dec. byte in memory - - DEC [HL] ~DEC m16
|
||||
dec. register DCR r8 DEC r8 DEC r8 DEX / DEY
|
||||
dec. register-pair DCX B DEC BC DEC BC -
|
||||
" DCX D DEC DE DEC DE -
|
||||
" DCX H DEC HL DEC HL -
|
||||
|
||||
inc. byte in memory INR M INC (HL) INC [HL] ~INC m16
|
||||
inc. register INR r8 INC r8 INC r8 INX / INY
|
||||
inc. register-pair INX B INC BC INC BC -
|
||||
" INX D INC DE INC DE -
|
||||
" INX H INC HL INC HL -
|
||||
|
||||
add with carry ACI [i8] ADC A, i8 ADC A, i8 ADC #i8
|
||||
" ADC M ADC A, (HL) ADC A, [HL] ~ADC m16
|
||||
" ADC r8 ADC A, r8 ADC A, r8 -
|
||||
|
||||
add ADD M ADD A, (HL) ADD A, [HL] ~CLC ; ADC m16
|
||||
" ADD r8 ADD A, r8 ADD A, r8 -
|
||||
" ADI [i8] ADD A, i8 ADD A, i8 CLC ; ADC #i8
|
||||
" DAD B ADD HL, BC ADD HL, BC -
|
||||
" DAD D ADD HL, DE ADD HL, DE -
|
||||
" DAD H ADD HL, HL ADD HL, HL -
|
||||
|
||||
substract with carry SBB M SBC A, (HL) SBC A, [HL] SBC m16
|
||||
" SBB r8 SBC A, r8 SBC A, r8 -
|
||||
" SBI [i8] SBC A, i8 SBC A, i8 SBC #i8
|
||||
|
||||
substract SUB M SUB (HL) SUB [HL] ~SEC ; SBC m16
|
||||
" SUB r8 SUB r8 SUB r8 -
|
||||
" SUI [i8] SUB i8 SUB i8 SEC ; SBC #i8
|
||||
|
||||
|
||||
compares:
|
||||
---------
|
||||
comp. with memory CMP M CP (HL) CP [HL] ~CMP m16
|
||||
comp. with register CMP r8 CP r8 CP r8 -
|
||||
comp. with immediate byte CPI [i8] CP i8 CP i8 CMP #i8
|
||||
|
||||
bit-shifts:
|
||||
-----------
|
||||
shift left with Carry RAL RLA RLA ROL A
|
||||
" - - RL r8 -
|
||||
shift right with Carry RAR RRA RRA ROR A
|
||||
" - - RR r8 -
|
||||
shift left - - SLA ASL A
|
||||
shift right - - SRL LSR A
|
||||
RLC RLCA RLCA -
|
||||
- - RLC r8 -
|
||||
RRC RRCA RRCA -
|
||||
- - RRC r8 -
|
||||
- - SRA -
|
||||
|
||||
misc operations on registers:
|
||||
-----------------------------
|
||||
Complement A CMA CPL CPL EOR #$ff
|
||||
Decimal Adjust A DAA DAA DAA -
|
||||
Swap low and high nibble - - SWAP r8 -
|
||||
exchange DE and HL XCHG EX DE, HL - -
|
||||
|
||||
setting/resetting flags:
|
||||
------------------------
|
||||
Set Carry Flag STC SCF SCF SEC
|
||||
Clear Carry Flag STC ; CMC SCF ; CCF SCF ; CCF CLC
|
||||
Complement Carry Flag CMC CCF CCF -
|
||||
|
||||
far branch:
|
||||
-----------
|
||||
jump to address JMP [m8] [m8'] JP m16 JP m16 JMP m16
|
||||
jump indirekt via pointer PCHL JP (HL) JP [HL] ~JMP (m16)
|
||||
|
||||
far conditional branch:
|
||||
-----------------------
|
||||
branch if Z==1 JZ [m8] [m8'] JP Z, m16 JP Z, m16 BNE *+3 ; JMP m16
|
||||
Z==0 JNZ [m8] [m8'] JP NZ, m16 JP NZ, m16 BEQ *+3 ; JMP m16
|
||||
C==1 JC [m8] [m8'] JP C, m16 JP C, m16 BCC *+3 ; JMP m16
|
||||
C==0 JNC [m8] [m8'] JP NC, m16 JP NC, m16 BCS *+3 ; JMP m16
|
||||
JM [m8] [m8'] JP M, m16 - -
|
||||
JP [m8] [m8'] JP P, m16 - -
|
||||
JPE [m8] [m8'] JP PE, m16 - -
|
||||
JPO [m8] [m8'] JP PO, m16 - -
|
||||
|
||||
near conditional branch:
|
||||
------------------------
|
||||
branch if Z==1 - - JR Z, m16 BEQ m16
|
||||
Z==0 - - JR NZ, m16 BNE m16
|
||||
C==1 - - JR C, m16 BCS m16
|
||||
C==0 - - JR NC, m16 BCC m16
|
||||
- - - -
|
||||
- - - -
|
||||
- - - -
|
||||
- - - -
|
||||
|
||||
subroutine call:
|
||||
----------------
|
||||
CALL [m8] [m8'] CALL m16 CALL m16 JSR m16
|
||||
|
||||
conditional subroutine call:
|
||||
----------------------------
|
||||
call if Z==1 CZ [m8] [m8'] CALL Z, m16 CALL Z, m16 BNE *+3 ; JSR m16
|
||||
Z==0 CNZ [m8] [m8'] CALL NZ, m16 CALL NZ, m16 BEQ *+3 ; JSR m16
|
||||
C==1 CC [m8] [m8'] CALL C, m16 CALL C, m16 BCC *+3 ; JSR m16
|
||||
C==0 CNC [m8] [m8'] CALL NC, m16 CALL NC, m16 BCS *+3 ; JSR m16
|
||||
CM [m8] [m8'] CALL M, m16 - -
|
||||
CP [m8] [m8'] CALL P, m16 - -
|
||||
CPE [m8] [m8'] CALL PE, m16 - -
|
||||
CPO [m8] [m8'] CALL PO ,m16 - -
|
||||
|
||||
subroutine return:
|
||||
------------------
|
||||
RET RET RET RTS
|
||||
|
||||
conditional subroutine return:
|
||||
------------------------------
|
||||
return if Z==1 RZ RET Z RET Z BNE *+1 ; RTS
|
||||
Z==0 RNZ RET NZ RET NZ BEQ *+1 ; RTS
|
||||
C==1 RC RET C RET C BCC *+1 ; RTS
|
||||
C==0 RNC RET NC RET NC BCS *+1 ; RTS
|
||||
RM RET M - -
|
||||
RP RET P - -
|
||||
RPE RET PE - -
|
||||
RPO RET PO - -
|
||||
|
||||
interupt related instructions:
|
||||
------------------------------
|
||||
set irq flag (irq's off) DI DI DI SEI
|
||||
clear irq flag (irq's on) EI EI EI CLI
|
||||
issue a hard irq RST RST x8 RST x8 BRK
|
||||
|
||||
i/o specific:
|
||||
-------------
|
||||
Get Byte from In-Port IN [p8] IN A, (p8) - -
|
||||
Send Byte to Out-Port OUT [p8] OUT (p8), A - -
|
||||
|
||||
stack related:
|
||||
--------------
|
||||
put reg. on Stack PUSH B PUSH BC PUSH BC -
|
||||
PUSH D PUSH DE PUSH DE -
|
||||
PUSH H PUSH HL PUSH HL -
|
||||
PUSH PSW PUSH AF PUSH AF PHA ; PHP
|
||||
|
||||
get reg. from Stack POP B POP BC POP BC -
|
||||
POP D POP DE POP DE -
|
||||
POP H POP HL POP HL -
|
||||
POP PSW POP AF POP AF PLP ; PLA
|
||||
|
||||
load Stack Pointer LXI SP [i8] [i8'] LD SP, i16 LD SP, i16 LDX #i8 ; TXS
|
||||
SPHL LD SP, HL LD SP, HL -
|
||||
- - LD [m16], SP -
|
||||
- - LD HL,SP+n -
|
||||
|
||||
dec. Stack-Pointer DCX SP DEC SP DEC SP TSX ; DEX ; TXS
|
||||
inc. Stack-Pointer INX SP INC SP INC SP TSX ; INX ; TXS
|
||||
|
||||
DAD SP ADD HL, SP ADD HL, SP -
|
||||
add to Stack-Pointer - - ADD SP, i8 TSX ; TXA ; CLC ; ADC #i8 ; TAX ; TXS
|
||||
|
||||
XTHL EX (SP), HL - -
|
||||
|
||||
misc:
|
||||
-----
|
||||
No Operation (Delay) NOP NOP NOP NOP
|
||||
halt operation (until irq) HALT HLT HLT *JAM
|
||||
stop cpu (until exception) - - STOP *JAM
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
@@ -1,462 +0,0 @@
|
||||
Super GameBoy Commands, Extracted by kOOPa, 15-Feb-98
|
||||
-----------------------------------------------------
|
||||
Last updated by: Bowser, 13-June-98
|
||||
|
||||
Updates:
|
||||
Block Area mode ($04) control codes updated
|
||||
Line mode ($05) written
|
||||
Divide mode ($06) written
|
||||
1CHR mode ($07) written
|
||||
|
||||
A SGB command transfer is 128 bits + a zero bit. The first five
|
||||
bits of the first byte is the command byte. The last 3 bits of
|
||||
the first byte represent the number of 128 bit packages to be
|
||||
sent. Unused bits in a SGB command transfer should be set to 0.
|
||||
Most of the commands listed below only transfer one package.
|
||||
The command bytes below are preceded by the # character to remind
|
||||
you that they have to be shifted left three times before used.
|
||||
|
||||
|
||||
Palettes
|
||||
--------
|
||||
|
||||
There are several different types of palettes in the SGB.
|
||||
One type is the System color palette. It is a virtual palette
|
||||
rather than a hardware palette. The hardware color palette
|
||||
is shown at the bottom of this document and contains what
|
||||
are called the SGB color palettes and also holds the SGB Border
|
||||
palette.
|
||||
|
||||
As far as SGB onscreen colors are concerned there are
|
||||
only really two palette types: SGB color palettes and
|
||||
the SGB border palette.
|
||||
|
||||
The SGB border palette is setup using command $14.
|
||||
There are 64 colors in this palette.
|
||||
|
||||
The SGB color palettes may be set directly using
|
||||
commands $00-$03. There are a total of four of
|
||||
these palettes and they determine which colors are
|
||||
used in the main game action window. The color for
|
||||
bit 00 will be the same for all SGB color palettes.
|
||||
The color most recently stored in bit 00 will be used.
|
||||
|
||||
The SGB color palettes may be set indirectly using
|
||||
the System color palettes using commands $0a-$0b.
|
||||
There are a total of 512 of these palettes.
|
||||
|
||||
|
||||
SGB Border
|
||||
----------
|
||||
|
||||
The SGB border is often shown as colorful graphics that
|
||||
surround the main game action window. The truth is that the
|
||||
SGB border actually covers the whole viewing screen and
|
||||
has the highest viewing priority. The reason it appears be
|
||||
just a "border" around most games is due to the fact that
|
||||
usually a 160x144 pixel wide box is drawn in the center of
|
||||
the SGB "border" using color 0. Since this color is
|
||||
transparent, the main game action window under it is visible.
|
||||
|
||||
Creating a program to convert a 16-color GIF to a SGB
|
||||
border is relatively easy and has been done for DOS.
|
||||
(i.e.gif2sopt.exe) What is not so easy is converting a
|
||||
64-color GIF to a SGB border because each tile only has
|
||||
access to 16-colors. The 16-color palette that the tile
|
||||
has access to is determined by the tile attribute byte.
|
||||
The tile attribute byte is described in the 'Picture
|
||||
Transfer' command ($14) below.
|
||||
|
||||
Main Action Window
|
||||
------------------
|
||||
|
||||
The SGB cartridge that plugs into the SNES contains
|
||||
a GB CPU. The SNES is able to video capture the video
|
||||
output of this GB CPU and display it on the screen as
|
||||
the main game action window. Since the SNES is only
|
||||
doing a raw video capture it only knows about 4 levels
|
||||
of grey coming from the GB CPU. In order to add more
|
||||
than 4 colors to the main game action window, the SGB
|
||||
software allows you to assign 1 of the 4 SGB color
|
||||
palettes for each 8x8 tile position in this window.
|
||||
"Block"($4), "Line"($5), "Divide"($6), "1Chr"($7),
|
||||
and "Set Attr from ATF"($15) all are different means
|
||||
for setting the palettes for each 8x8 tile location.
|
||||
On reset, each 8x8 tile position defaults to SGB
|
||||
color palette 0.
|
||||
|
||||
Commands $4-$7 are various methods for block-setting
|
||||
the 8x8 tile color palettes. The "Set Attr from ATF"
|
||||
($15) allows you to select 1 of 45 "ATtribute Files".
|
||||
Each "ATtribute File" contains 90 bytes (20x18x2 bits).
|
||||
By selecting an "ATtribute File", you can exactly
|
||||
select 1 of 4 SGB color palettes for each 8x8 tile
|
||||
location due to the fact that these files contain
|
||||
2 bits of information for each 8x8 tile location.
|
||||
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
Set SGB color Palettes
|
||||
0 & 1 ($00,data) - Download color palettes 0 & 1
|
||||
2 & 3 ($01,data) - Download color palettes 2 & 3
|
||||
0 & 3 ($02,data) - Download color palettes 0 & 3
|
||||
1 & 2 ($03,data) - Download color palettes 1 & 2
|
||||
--------------------------------------------------
|
||||
|
||||
Here is example data for setting SGB color palettes 0 & 1:
|
||||
|
||||
DW $7fff ;white ;bit 00 color
|
||||
|
||||
;Pallete 0
|
||||
DW $7c00 ;blue ;bit 01 color
|
||||
DW $03e0 ;green ;bit 10 color
|
||||
DW $0000 ;black ;bit 11 color
|
||||
|
||||
;Palette 1
|
||||
DW $03ff ;yellow ;bit 01 color
|
||||
DW $001f ;red ;bit 10 color
|
||||
DW $0000 ;black ;bit 11 color
|
||||
|
||||
|
||||
Please note that all four SGB color palettes share the
|
||||
same bit 00 color. The color most recently stored in
|
||||
bit 00 will be used.
|
||||
|
||||
Information for calculating the DW color value is given later
|
||||
in this text.
|
||||
|
||||
When using the following four Palette Direct Set commands,
|
||||
the GameBoy image will be altered (colors changed) even if
|
||||
you used the GameBoy Window Mask command to freeze the screen.
|
||||
Therefore use arguments Black or White with the Window Mask
|
||||
command before using the following four commands. If you want
|
||||
to freeze the screen, send palette data with the Attribute
|
||||
File ATF0-ATF44. In the event you are changing the screen by
|
||||
sending attribute data and color data at the same time, use
|
||||
Set SGB Palette Indirect command.
|
||||
|
||||
|
||||
"Block" Area Designation Mode ($04) (other data shown below)
|
||||
-----------------------------------------------------------
|
||||
|
||||
$00 - %00100xxx xxx = # of packets
|
||||
$01 - %///xxxxx xxxxx = # of data sets - One data set is control
|
||||
code, color palette designation, & the coords.
|
||||
|
||||
$02 - %/////xxx xxx = Control code
|
||||
000 = don't care
|
||||
001 = set pal (inside block + surrounding block line) to 'zz' of $03
|
||||
010 = set pal of surrounding block line to 'yy' of $03
|
||||
011 = set pal inside block to 'yy' & surrounding block line to 'yy'
|
||||
100 = set pal outside the block to 'xx'
|
||||
101 = set pal inside to 'zz' & outside the block to 'xx'
|
||||
110 = set pal outside the block to 'xx'
|
||||
111 = set pal inside block tp 'zz' + surrounding line to 'yy' + outside block to 'xx'
|
||||
$03 - %//xxyyzz Color Palette Designation
|
||||
xx = color palette outside surrounded area
|
||||
yy = color palette on surrounding block line
|
||||
zz = color palette inside surrounded area
|
||||
$04 - %///xxxxx xxxxx = start point H
|
||||
$05 - %///xxxxx xxxxx = start point V
|
||||
$06 - %///xxxxx xxxxx = end point H
|
||||
$07 - %///xxxxx xxxxx = end point V
|
||||
|
||||
$08-$0d Repeat of $02-$07 data if # of data sets > 1.
|
||||
|
||||
If number of packets is 1, set $0e & $0f to $00.
|
||||
|
||||
|
||||
|
||||
"Line" Area Designation Mode ($05)
|
||||
----------------------------------
|
||||
$00 - %00101xxx xxx = # packets
|
||||
$01 - %xxxxxxxx number of data sets ($1 - $6E), one data set controls:
|
||||
code, colours palette designation, and coords.
|
||||
|
||||
$02 - %xyyzzzzz control code 1'st data set
|
||||
x = Mode (0 = Horizontal line, 1 = Vertical line)
|
||||
yy = Palette number
|
||||
zzzzz = Line number
|
||||
|
||||
One dataset is 1 byte just as $02.
|
||||
If # data sets = 1, fill with 0's up to $0F
|
||||
|
||||
|
||||
"Divide" Area Designation Mode ($06)
|
||||
-----------------------------------
|
||||
$00 - %00101001 (number of packets must be 1)
|
||||
|
||||
$01 - %/vxxyyzz control code
|
||||
v = Mode (0 = divide horizontally, 1 = Divide vertical)
|
||||
xx = Colour palette ON division line
|
||||
yy = Colour palette ABOVE & LEFT of division
|
||||
zz = colour palette BELOW & RIGHT of division
|
||||
|
||||
$02 - %///xxxxx xxxxx = Character line number to divide at
|
||||
|
||||
fill with 0's to $0F
|
||||
|
||||
|
||||
"1CHR" Area Designation Mode ($07)
|
||||
----------------------------------
|
||||
$00 - %00111xxx xxx = number of packets ($01 - $06)
|
||||
$01 - %///xxxxx Beginning X coordinate
|
||||
$02 - %///yyyyy Beginning Y coordinate
|
||||
|
||||
$03 - %xxxxxxxx Number of datasets, 2 bits = 1 dataset
|
||||
$04 - %///////x MSB of data in $03. Max number = 360.
|
||||
$05 - %///////x Writing style ( 0 = Left -> right, 1 = up -> down)
|
||||
|
||||
$06 - %vvxxyyzz data
|
||||
vv = pal for dataset 1
|
||||
xx = pal for dataset 2
|
||||
yy = pal for dataset 3
|
||||
zz = pal for dataset 4
|
||||
|
||||
$07 - %vvxxyyzz data
|
||||
etc...
|
||||
|
||||
|
||||
Sound On/Off ($08)
|
||||
------------------
|
||||
|
||||
|
||||
Transfer Sound PRG/DATA ($09)
|
||||
-----------------------------
|
||||
|
||||
This transfers your score (in .GAK format) data.
|
||||
|
||||
|
||||
Set SGB Palette Indirect ($0a)
|
||||
------------------------------
|
||||
|
||||
|
||||
Set System Color Palette Data ($0b)
|
||||
-----------------------------------
|
||||
|
||||
|
||||
Enable/Disable Attraction Mode ($0c)
|
||||
------------------------------------
|
||||
|
||||
|
||||
Speed Function ($0d)
|
||||
--------------------
|
||||
|
||||
|
||||
SGB Function ($0e)
|
||||
------------------
|
||||
|
||||
|
||||
Super NES WRAM Transfer 1 ($0f)
|
||||
-------------------------------
|
||||
|
||||
|
||||
Super NES WRAM Transfer 2 ($10)
|
||||
-------------------------------
|
||||
|
||||
|
||||
Controller 2 Request (#11) ($00) - Request 1 play
|
||||
-------------------------------------------------
|
||||
|
||||
|
||||
Controller 2 Request (#11) ($01) - Request 2 play
|
||||
-------------------------------------------------
|
||||
|
||||
Is used to determine if system is SGB or regular GB.
|
||||
|
||||
|
||||
Set Program Counter ($12)
|
||||
-------------------------
|
||||
|
||||
|
||||
CharSet Transfer ($13) (%00000xyy)
|
||||
(x=Char Type:0=BG,1=OBJ y=CharSet Range:0=00-7f,1=80-ff)
|
||||
-----------------------------------------------------------
|
||||
|
||||
The tiles that are downloaded to the SNES for the border
|
||||
are regular GB tiles that have been modified for extra colors.
|
||||
Every tile consists of 32 bytes. The format is:
|
||||
|
||||
16 bytes - Standard 4 color GB character set
|
||||
16 bytes - Extended color information
|
||||
|
||||
The same way GB uses two bytes to convey 8 pixels
|
||||
and the 4 colors for each pixel, the extended color
|
||||
info uses the same technique for determining extended
|
||||
colors. This tells the SNES which color palette to
|
||||
use to display each pixel. This allows a total of 16
|
||||
colors per tile. Since SGB borders support up to 64 colors,
|
||||
access to the other colors are achieved by changing the
|
||||
Major palette number in the picture transfer tile map.
|
||||
|
||||
|
||||
Picture Transfer ($14) - Download border to SNES.
|
||||
------------------------------------------------
|
||||
|
||||
The border (or tile map) that is downloaded is 32x28 tiles
|
||||
or 256x224 pixels. The regular GB screen fits right in the
|
||||
middle like this:
|
||||
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXX....................XXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
The tile map consists of a tile number byte & a tile attribute
|
||||
byte at each position on the map. A total of 32 lines are
|
||||
downloaded even though the last 4 lines are not visible. This
|
||||
would equal 64 bytes per line and a total of 2048 bytes per map.
|
||||
Next, a 64 x 2 byte color palette for the map is downloaded.
|
||||
The first palette entry color is transparent. Use this color
|
||||
to display regular GB screen underneath.
|
||||
|
||||
The tile number comes before the tile attribute of each position.
|
||||
There can be up to 1024 tiles from which to select. The SGB only
|
||||
supports 256 so bits 0 & 1 of the tile attribute must be set to 0.
|
||||
Here are the tile attributes I understand so far:
|
||||
|
||||
Bit 7 - Vertical flip tile
|
||||
Bit 6 - Horizontal flip tile
|
||||
Bit 5 - Does nothing (Set to 0.)
|
||||
Bit 4 - Select Major Palette MSB (Usually set to 1.)
|
||||
Bit 3 - Select Major Palette -
|
||||
Bit 2 - Select Major Palette LSB
|
||||
Bit 1 - Tile # MSB (Most significant bit) (Set to 0.)
|
||||
Bit 0 - Tile # NSB (Next most significant bit) (Set to 0.)
|
||||
|
||||
This is often called the SGB border but in fact it covers
|
||||
the whole SNES screen. The Major Palette select has 8
|
||||
different settings. Only the last 4 - 7 are normally used
|
||||
thought to access all 64 colors transfered with cmd (#14).
|
||||
(NOTE: If using 'gif2sopt.exe' to generate a border, the
|
||||
range of the palette selections is 1-8 so select palette
|
||||
5 since that program only allows up to 16 colors.)
|
||||
|
||||
|
||||
Set Attribute from ATF ($15)
|
||||
----------------------------
|
||||
|
||||
The data for 45 Attribute files is transfered with
|
||||
this command. Each ATtribute File is 90 bytes so
|
||||
90x45 or 4050 bytes of data are transfered. Each
|
||||
attribute file uses 5 bytes per 8x8 horizontal
|
||||
line (20 x 4 char/byte x 2 bits/palette) to
|
||||
describe the color palettes of a line.
|
||||
Example ATF data:
|
||||
|
||||
DB $ff,$00,$00,$00,$02 ; Line #1
|
||||
DB $ff,$00,$00,$00,$02 ; Line #2
|
||||
DB $ff,$00,$00,$00,$02 ; Line #3
|
||||
.....
|
||||
DB $ff,$00,$00,$00,$02 ; Line #18
|
||||
|
||||
The above ATtribute File would set the SGB color
|
||||
palette selection to 3 for the first 4 columns
|
||||
of the main game action window. The last column
|
||||
would have SGB color palette 2 selected. All the
|
||||
other columns would have palette 0 selected.
|
||||
|
||||
|
||||
Set Data from ATF ($16) (data)
|
||||
------------------------------
|
||||
|
||||
Transfer specified ATtribute File to GameBoy window.
|
||||
|
||||
data:
|
||||
%/xyyyyyy x - 0 = No Change, 1 = Cancel mask after xfer ATF
|
||||
yyyyyy = ATtribute File number ($00-$2c)
|
||||
|
||||
|
||||
GameBoy Window Mask ($17) (data)
|
||||
--------------------------------
|
||||
data:
|
||||
$00 = Off
|
||||
$01 = Transfers VRAM to SNES until cancelled.
|
||||
$02 = Mask so that all color codes in SGB
|
||||
color palette are black.
|
||||
$03 = Mask so that all color codes in SBG
|
||||
color palette are white.
|
||||
|
||||
|
||||
Super NES OBJ Mode ($18)
|
||||
------------------------
|
||||
|
||||
SNES Color Palette Info
|
||||
-----------------------
|
||||
|
||||
The Nintendo Super Famicom is capable of displaying 256 colors from a
|
||||
palette of 32,768. These 256 colors are split into 16 palettes of 16 colors
|
||||
each. Only 8 of these palettes are accessible by the SGB.
|
||||
|
||||
Color data is made up of 3 components (Red,Green,Blue) each of 5 bits (The
|
||||
Amiga uses exactly the same system, but only using 4 bits per component).
|
||||
|
||||
00000 00000 00000
|
||||
\ / \ / \ /
|
||||
\ / \ / \ /
|
||||
B G R
|
||||
|
||||
Examples:
|
||||
~~~~~~~~~
|
||||
00000 10111 11100
|
||||
11111 00000 00000 = $7C00 (Bright Blue)
|
||||
00000 11111 00000 = $03E0 (Bright Green)
|
||||
00000 00000 11111 = $001F (Bright Red)
|
||||
00000 00000 00000 = $0000 (Black)
|
||||
11111 11111 11111 = $7FFF (White)
|
||||
|
||||
|
||||
SGB Palette Selection
|
||||
---------------------
|
||||
|
||||
There is actually only one color palette in the SNES
|
||||
but it is divided up into several sections for different
|
||||
SGB functions. These sections are referred to as Major (M)
|
||||
sections 0-7. Some SGB functions even divided some of these
|
||||
sections into sections. These sections are referred to as
|
||||
minor (m) sections. The large blocks below represent Major
|
||||
sections or palettes and smaller ones below them represent
|
||||
minor palettes. There are 4 colors per minor palette and
|
||||
16 colors per Major palette:
|
||||
|
||||
+-------+-------+-------+-------+-------+-------+-------+-------+
|
||||
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
+-------+-------+-------+-------+-------+-------+-------+-------+
|
||||
|0|1|2|3| ^ ^
|
||||
+-------+ | |
|
||||
^ ^ +--------------+--------------+
|
||||
+--+--+ |
|
||||
| |
|
||||
Set with Commands Set with 64 colors in Picture Transfer (#14)
|
||||
#00,#01,#02, & #03 (holds SGB Border colors)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,67 +0,0 @@
|
||||
Spare GBC Sprite RAM
|
||||
written by GeeBee
|
||||
|
||||
Thanks to tips from Shawn Freeman about there being
|
||||
an area that you can read/write from $fea0-$feff on
|
||||
the GBC I decided to research further and came up with
|
||||
the following info.
|
||||
|
||||
In theory the following information could also be used
|
||||
to detect emulators since none of them currently accurately
|
||||
emulate this RAM as far as I know.
|
||||
|
||||
The following RAM seems to be spare in the GBC and not
|
||||
read/written by the GBC itself. I'll add this info to
|
||||
the FAQs soon.
|
||||
|
||||
|
||||
Spare GBC Sprite RAM
|
||||
--------------------
|
||||
|
||||
1. There are 3 sets of 8 bytes of
|
||||
RAM available or 24 bytes total.
|
||||
This RAM is only available on the
|
||||
GBC and not on any of the older GBs.
|
||||
|
||||
Set #1 $fea0-$fea7
|
||||
Set #2 $fec0-$fec7
|
||||
Set #3 $fee0-$fee7
|
||||
|
||||
2. Each set is repeated 4 times in
|
||||
the following address spaces:
|
||||
|
||||
$fea0-$febf
|
||||
$fec0-$fedf
|
||||
$fee0-$feff
|
||||
|
||||
3. If you read/write any of the
|
||||
repeated data sets you will read/write
|
||||
the original data set itself. i.e. All
|
||||
repeated data bytes serve as echo RAM.
|
||||
|
||||
4. Reading/writing of this RAM is only
|
||||
reliable in video modes 0 & 1. As a result,
|
||||
it is best to wait for vblank to reliably
|
||||
read/write this area.
|
||||
|
||||
|
||||
Example data:
|
||||
|
||||
fea0 00 01 02 03 04 05 06 07
|
||||
fea8 00 01 02 03 04 05 06 07
|
||||
feb0 00 01 02 03 04 05 06 07
|
||||
feb8 00 01 02 03 04 05 06 07
|
||||
|
||||
fec0 10 11 12 13 14 15 16 17
|
||||
fec8 10 11 12 13 14 15 16 17
|
||||
fed0 10 11 12 13 14 15 16 17
|
||||
fed8 10 11 12 13 14 15 16 17
|
||||
|
||||
fee0 80 81 82 83 84 85 86 87
|
||||
fee8 80 81 82 83 84 85 86 87
|
||||
fef0 80 81 82 83 84 85 86 87
|
||||
fef8 80 81 82 83 84 85 86 87
|
||||
|
||||
|
||||
|
||||
|
@@ -1,28 +0,0 @@
|
||||
Timer Interrupt Document
|
||||
Written by Jonathan Waugh
|
||||
|
||||
There are 2 registers you need to set to use the timer interrupt:
|
||||
|
||||
|
||||
1) $FF07 (TAC) selects the clock frequency. You set it to 4 for a frequency of 4.096Khz
|
||||
But this is not when the interrupt occurs. It determines when the TIMA ($FF05) is incremented.
|
||||
ie. In your example, the TIMA is incremented 4096 times a sec.
|
||||
The interrupt occurs when the TIMA generates an overflow. ie goes from 255 -> 0
|
||||
|
||||
|
||||
2) $FF06 (TMA) sets the starting point for the TIMA register.
|
||||
If you set it to 255 (-1), a timer interrupt would occur at the clock frequency. (4096Hz)
|
||||
If you set it to 240 (-16), a timer interrupt will occur at the clock frequency /16 (256Hz)
|
||||
|
||||
|
||||
It follows that the largest time interval you can get via the timer interrupt is 16Hz. Clock = 4096Hz, TMA = 0 (-256)
|
||||
|
||||
|
||||
To get a delay of 2 secs, you will have to wait for 32 timer interrupts.
|
||||
|
||||
|
||||
Option: Set up your own variable that counts down from 32. When it reaches 0, reset to 32 and process your interrupt code.
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user