------------------------------------------------------------------------------- How To Code On The Game Boy ------------------------------------------------------------------------------- Written by THE RIDDLER/ACT a lot of information stolen from Dr. -Pan-/ATX (!sorry!) .oO Nintendo Inside Oo. The Game Boy really is a powerful machine! Here are some facts about it: CPU : a modified Z80 processor Screen size : resolution is 160*144 and the real image is 256*256 Colors : max. 12 colors selecting 4 shades of gray Sprites : PAN said 40 Sprites, but I can use 20 only... Sprite size : 8x8 or 8x16 .oO Memory Map Oo. ------- ------- $0000 $FE00 $7FFF - 32k ROM Bank $FE9F - OAM RAM ------- ---I--- $8000 $FF30 $9FFF - VRAM $FF3F - Internal sound RAM ------- ---I--- $A000 $FF00 $DFFF - Work RAM $FFFF - Hardware register area ------- ------- $E000 I $FFFF - OAM RAM and register area ---I ------- .oO The CPU Oo. I said the Game Boy's CPU is a modified Z80 processor. There are a lot of instructions which a different/new/not avivable. Here is a list of them (THIS LIST IS NOT COMPLETE): .oO NEW AND DIFFERENT INSTRUCTIONS Oo. ------------------------------------------------------------------------------ LD (HLI),A - Stores the accumulator at the memory location (HL) and increases HL by 1. MacroSimulation: ldHLI Opcode: Flags: don't know Before: A=$56 HL=$1234 After: A=$56 HL=$1235 Old MNE: ------------------------------------------------------------------------------ LD (HLD),A - Stores the accumulator at the memory loaction (HL) and decreases HL by 1. MacroSimulation: ldHLD Opcode: Flags: don't know Before: A=$56 HL=$1234 After: A=$56 HL=$1233 Old MNE: ------------------------------------------------------------------------------ LD ($xxxx),A - Stores the accumulator to the memory location $xxxx. MacroSimulation: STA $xxxx Opcode: $EA Flags: don't know Example: $000200: $EA $14 $C0 LD ($C014),A Old MNE: JP PE,$xxxx ------------------------------------------------------------------------------ LD A,($xxxx) - Loads the accumulator from the memory location $xxxx. MacroSimualtion: LDA $xxxx Opcode: $FA Flags: ZERO Example: $000200: $FA $14 $C0 LD ($C014),A Old MNE: JP M,$xxxx ------------------------------------------------------------------------------ LD ($xx),A - Stores the accumulator to the hardware register area $FFxx. This instruction is just for fast accessing on the hardware registers. MacroSimulation: STAB $xx Opcode: $E0 Flags: don't know Example: $000200: $E0 $40 LD ($40),A Old MNE: ------------------------------------------------------------------------------ ld A,($xx) - Loads the accumulator from the hardware register area $FFxx. This instruction is just for fast accessing on the hardware registers. MacroSimulation: LDAB $xx Opcode: $F0 Flags: ZERO Example: $000200: $F0 $44 LD A,($44) Old MNE: ------------------------------------------------------------------------------ .oO First Step Oo. So, now we know which instructions can be used and which not, let's write our first program - Let's code a simple text intro -. Since there is no unique Assembler for the GameBoy we have to think about a usefull concept. First we have to take a Z80-cross-assembler and write some macros which are simulating the new instructions of the GameBoy CPU. Second point we have to think where in the memory is what. As I said the ROM area is from $0000 to $7FFF, we have to org the program to the ROM area. So what is the structure of a GameBoy game??? On the SNES every program (except HiRom games) starts at $8000. Is there any similiar address on the GameBoy??? Yes, there is an internal info block and this block begins at $100 and its format is as follows: (stolen from PAN) $100-$101 - 00 C3 (C3 is the jump instruction) $102-$103 - Lo Hi (Start address for game, usually $150 it would be written as 50 01) $100-$133 - Nintendo Character Area, if this does not exist the game won't run! (maybe here Nintendo got a the copyright) 000100: 00 C3 50 01 CE ED 66 66 CC 0D 00 0B 03 73 00 83 000110: 00 0C 00 0D 00 08 11 1F 88 89 00 0E DC CC 6E E6 000120: DD DD D9 99 BB BB 67 63 6E 0E EC CC DD DC 99 9F 000130: BB B9 33 3E $134-$143 - Title Registration Area (title of the game in ASCII- 16 chars) $144-$146 - NOT USED $147 - CARTRIDGE TYPE 0 - ROM ONLY 1 - ROM+MBC1 2 - ROM+MBC1+RAM 3 - ROM+MBC1+RAM+BATTERY 4 - ROM+MBC2 5 - ROM+MBC2+BATTERY $148 - ROM SIZE 0 - 256 kbit 1 - 512 kbit 2 - 1 mbit 3 - 2 mbit 4 - 4 mbit $149 - RAM SIZE 0 - NONE 1 - 16 kbit 2 - 64 kbit 3 - 256 kbit So the only thing you have to do is to ORG the Nintendo Character Area to $100 and the code to $150 and it will run. Easy, isn't it???. So what makes my program do senseful things??? Yeah, first there has to be a little setup routine which initializies some hardware registers. (This setup is not necessary, but it's in almost every game.) InitGameBoy: di ; disable irqs ld SP,$DFFF ; stack to $DFFF (huuuh) ld A,$03 stab $0F ; enable VBI& stab $FF ; LCDCStatus irqs xor A ; tricky 8) stab $42 ; clear scroll Y stab $43 ; clear scroll X stab $41 ; clear LCDC Status stab $01 ; clear ser. trans. data stab $02 ; stop serial transfer ld A,$93 stab $40 ; screen control: ; object&tile vram ; located at $8000 ; map vram ; located at $9800 ; screen enabled ; sprites enabled ld A,$E4 stab $47 ; screen palette stab $48 ; sprites palette 0 stab $49 ; sprites palette 1 ei ; enable irqs .................................. ; some more code This is a well comented routine which should be understood and so I'll just give you more information on interrupts. There are two ways to enable and disable the IRQs. First way is to use the CPU instructions EI (enable interrupts) and DI (disable interrupts). Second way is to mask the Interrupt Enable register which is located at $FFFF. Here is a short information on this register: Address: - $FFFF Name: - IE Contents: - Interrupts Enable (R/W) Bit 4: Transition from High to Low of Pin number P10-P13 Bit 3: Serial I/O transfer end Bit 2: Timer overflow Bit 1: LDCD Bit 0: V-Blank 0: disable 1: enable In my programs (and in the most I disassembled) I use both ways to set the interrupts, but in a specified order. If you use the CPU instruction you prohibit or acknowledge ALL interrupts and you can do the hell with the Interrupt Enable Register and nothing will happen. So the best way to select the interrupts is to prohibit all interupts and then to set the IE Register and finally to acknowledge all interrupts again. But has the Game Boy something like interrupt vectors for routines or something else??? Yes: (<- pritty short answer, isn't it?!) 8) The priority and jump address for the 5 interrupts are: Interrupt Priority Start Address ---------------------------------------------------------------- V-Blank 1 $0040 LCDC Status 2 $0048 Timer Overflow 3 $0050 Serial Transfer 4 $0058 Hi-Lo of Pin 5 $0060 But how to use such an address??? I'll explain: ORG $40 jp VBLANK ORG $48 exx ; reti ORG $50 jp TIMER ORG $58 exx ; reti ORG $60 jp HI_LO_PIN ORG $400 VBLANK: exx ; reti TIMER: exx ; reti HI_LO_PIN exx ; reti If you take a look at the code you will probably wonder why I return from an interrupt using the instruction "exx" and not "reti". This is cause the f*#!ing NINTENDO idiots changed this instruction. "exx" is no more longer "exx", but "reti". Second point is, there is no GameBoy assembler and I was too lazy to create a macro called RTI or something else. So take good care when you're coding................................................ Actually you're now able to code a functionable intro on the GameBoy, but it will do nothing!!! So let's come to the second chapter. .oO The Screen Oo. (<- here we go) Oki, in this chapter we will learn how the graphic data are organized in memory and all that stuff to realize a simple text intro (do you remember?). Hmmmmm, where do we start??? Maybe we start at the most basic thing: THE VRAM The VRAM is located from $8000 to $9FFF. And it is subdivided into 4 parts: $8000-$8FFF BG (screen) and Object (sprite) character data area $8800-$97FF BG character data area $9800-$9BFF BG map data area 1 $9C00-$9FFF BG map data area 2 So if you want to display some text you have to copy the font data to the selected character data area and the ascii text map to the selected map data area. You can select it by setting some bits in register $FF40: Address - $FF40 Name - LCDC Contents - LCD Control (R/W) Bit 7 - LCD Control Operation 0 : Stop completely (no picture on screen) 1 : operation Bit 6 - Window Screen Display Data Select 0 : $9800-$9BFF 1 : $9C00-$9FFF Bit 5 - Use window or display whole screen 0 : display whole screen 1 : use Bit 4 - BG Character Data Select 0 : $8800-$97FF (<- Never used this, cause it never worked. I don't know why.) 1 : $8000-$8FFF (<- Same area as OBJ) Bit 3 - BG Map Data Select 0 : $9800-$9BFF 1 : $9C00-$9FFF Bit 2 - Obj construction 0 : 8x8 1 : 8x16 Bit 1 - Obj Display 0 : nope 1 : yap Bit 0 - BG Display 0 : nope 1 : yap In my programs I use the value $91 ($93 if I use objects). That means - LCD operation active - character data at $8000 - object data at $8000 - map data at $9800 - BG allowed (- Objects allowed) If you have selected all your stuff you can start to copy all the graphic data into the VRAM. STOP... What graphic data? I'll explain: The character data is organized INTERLEAVED: Block0: Line 0 of plane 0 Line 0 of plane 1 Line 1 of plane 0 Line 1 of plane 1 . . . Line 7 of plane 0 Line 7 of plane 1 Block1: Line 0 of plane 0 Line 0 of plane 1 Line 1 of plane 0 Line 1 of plane 1 . . . Line 7 of plane 0 Line 7 of plane 1 Block3: ........... Block255: Line 0 of plane 0 Line 0 of plane 1 Line 1 of plane 0 Line 1 of plane 1 . . . Line 7 of plane 0 Line 7 of plane 1 Only 256 blocks can be used. There is a characterset in my code... I ripped it from a how2code on SNES. Hope the author won't mind. The map data are organized in a 32x32 byte array. A BYTE is the reference to the block which will be displayed. Horizontally only 20 blocks can be displayed (160 of 256 pixel). Vertically just 18 (144 of 256). Now you know how the graphic stuff is arranged, so we can copy it to the VRAM. But there is a problem: The VRAM just can be acessed during V-Blank. So in our copy routine we have to wait and we do it like this: ld BC,Topaz_Font ; from ld DE,$0400 ; size ld HL,$9800 ; to Copy: ldab $44 ; get rasterbeam position cp $90 ; bigger than 144... jr C,Copy ; ... if OK the copy a byte ld A,(BC) ; get it ldHLI ; store it and increase HL by 1 inc BC ; increase BC by 1 dec DE ; decrease DE (size) by 1 ld A,D ; load D to A or E ; or E jr NZ,Copy ; if A=0 the size=0 the finish I hope you got the code. If not then practise with it. So that's it for the screen stuff. Alright. So far so good. Let's come to something more interesting: THE SPRITES .oO THE SPRITES Oo. Let me begin this chapter upside down and say that the graphic format of a sprite (or an object) is the same as of a character. But how can you move them??? Simple!!! There is a special part of the RAM called OAM (object attribute memory). This OAM is divided into 40 information blocks and an information block looks like this: 1 byte: Y position 2 byte: X position 3 byte: reference to charcter data 4 byte: flags bit 7 - Palette 6 - X flip 5 - Y flip 4 - Pri bit 3-0 are not used... So sprites can use 2 palettes and can have a size of 8x8 or 8x16 pixel. (To selecect 8x16 set bit in $FF40). But there are some problems coming up: PAN said that he is using the DMA to copy RAM or ROM to the OAM. He wrote a routine that I also found in TETRIS. But it doesn't work. MY GAMEBYO CRASHS AND I REALLY DON'T KNOW WHY... So I just copied the sprite information using the CPU. (YOU HAVE TO DO THAT DURING V-BLANK, otherwise you'll have veeery funny sprites!) .oO The End Oo. So I think that's all for my first part on How2Code the GAME BOY. Next parts will have an explaination on sound stuff. (I got really nice examples on this stuff, ask me for the source) .oO CREDIZ Oo. PAN (for sharing all this information), HEROZERO (great DisAss! If you want to contact send me a letter (I'm really interested in contacting you, but I don't have E-Mail). And, NICE JOB man.), CHIPSY, MONSUN, SUNNY, TSI, CHAOS, BOBBY, TOYO, CATWOMAN, DOMMI, TEASY, CONCH, ENZYM, WILDTHING, 2FAST, PRODIGY (the music group), STING, MR.HYDE, MR.DO, YOSHI, YOSHI (Jeremy), WISEMAN and all the others I've forgotten. If you want to get in contact with me write to: Sebastian "THE RIDDLER" Deutsch Rotteland 39 44797 Bochum GERMANY (<-I have a really bad english) or call (VOICE only): +49(0)234/799218 JUST LEGAL STUFF. I don't want to crack games and to code silly trainers. I'm just interested in coding. Also contact me if you got codes or something else for SNES, AMIGA, PC coding (REMEMBER: JUST LEGAL STUFF!!!). c Ya THE RIDDLER Disclaimer: I'm not responsible for anything. None of the information in this document comes from confidental Nintendo sources. All names are tradematks of theri respective owners. Enigma: A wierd picture of pure logic. DONE: IN MY SPAIN HOLIDAYS 6-8 1995 FINISHED: 9-7-95 .oO LAST HINT Oo. My code doesn't have a correct checksum. Use DIESEL formular to calculate complement check. (My develepment station and GB doesn't need this complent check) .oO THE HARDWARE REGISTERS Oo (Info and text stolen from PANs manual, hope he don't mind, but I think there's no better way to explain than he does.) ------------------------------------------------------------------------------ Address - $FF00 Name - P1 Contents - Register for reading joy pad info. (R/W) Bit 7 - Not used Bit 6 - Not used Bit 5 - P15 out port Bit 4 - P14 out port Bit 3 - P13 in port Bit 2 - P12 in port Bit 1 - P11 in port Bit 0 - P10 in port This is a very strange way of reading joypad info. There are only 8 possible button/switches on the Game Boy. A, B, Select, Start, Up, Down, Left, Right. Why they made their joypad registers in this way I'll never know. They could have used all 8 bits and you just read which one is on. This is the matrix layout for register $FF00: P14 P15 | | --P10-------O-Right------------O-A--------- | | --P11-------O-Left-------------O-B--------- | | --P12-------O-Up---------------O-Select---- | | --P13-------O-Down-------------O-Start----- | | This is the logic in reading joy pad data: Turn on P15 (bit 5) in $ff00 Wait a few clock cycles read $ff00 into A invert A - same as EOR #$FF - just reverse all bits apparently the joy pad info returned is like the C64 info. 0 means on, 1 means off. But logic tells us that it should be the other way around. So to make it less confusing we just flip the bits! AND A with #$0F - get only the first four bits By turning on P15 we are trying to read column P15 in the matrix layout. It contains A,B,SEL,STRT SWAP A - #$3f becomes #$f3, it swaps hi<->lo nibbles store A in B for backup Turn on P14 (bit 4) in $ff00 Wait a few more clock cycles read $ff00 into A invert A - just as above AND A with #$0F - get first 4 bits - By turning on P14 we get the data for column P14 in the matrix layout. It contains U,D,L,R OR A with B - put the two values together. turn on P14 and P15 in $ff00 to reset. The button values using the above method are such: $80 - Start $8 - Down $40 - Select $4 - Up $20 - B $2 - Left $10 - A $1 - Right Let's say we held down A, Start, and Up. The value returned in accumulator A would be $94 Let's see this method in action! Game: Ms. Pacman Address: $3b1 0003B1: 0003B1: 3E 20 LD A,#$20 <- bit 5 = $20 0003B3: 0003B3: EA 00 FF LD ($FF00),A <- turn on P15 0003B6: 0003B6: FA 00 FF LD A,($FF00) 0003B9: 0003B9: FA 00 FF LD A,($FF00) <- wait a few cycles 0003BC: 0003BC: 2F CPL <- complement (invert) EOR #$ff 0003BD: 0003BD: E6 0F AND #$0F <- get only first 4 bits 0003BF: 0003BF: CB 37 SWAP A <- swap it 0003C1: 0003C1: 47 LD B,A <- store A in B 0003C2: 0003C2: 3E 10 LD A,#$10 <- bit 4 = $10 0003C4: 0003C4: EA 00 FF LD ($FF00),A <- turn on P14 0003C7: 0003C7: FA 00 FF LD A,($FF00) 0003CA: 0003CA: FA 00 FF LD A,($FF00) 0003CD: 0003CD: FA 00 FF LD A,($FF00) 0003D0: 0003D0: FA 00 FF LD A,($FF00) 0003D3: 0003D3: FA 00 FF LD A,($FF00) 0003D6: 0003D6: FA 00 FF LD A,($FF00) <- Wait a few MORE cycles 0003D9: 0003D9: 2F CPL <- complement (invert) 0003DA: 0003DA: E6 0F AND #$0F <- get first 4 bits 0003DC: 0003DC: B0 OR B <- put A and B together The following routine is common on SNES as well. It clarifies that you've only pressed the specified button(s) once every other frame. That way the Joypad is less sensitive to wrong/bad/false movements. 0003DD: 0003DD: 57 LD D,A <- store A in D 0003DE: 0003DE: FA 8B FF LD A,($FF8B) <- read old joy data from ram 0003E1: 0003E1: AA XOR D <- toggle w/current button bit 0003E2: 0003E2: A2 AND D <- get current button bit back 0003E3: 0003E3: EA 8C FF LD ($FF8C),A <- save in new Joydata storage 0003E6: 0003E6: 7A LD A,D <- put original value in A 0003E7: 0003E7: EA 8B FF LD ($FF8B),A <- store it as old joy data 0003EA: 0003EA: 3E 30 LD A,#$30 <- turn on P14 and P15 0003EC: 0003EC: EA 00 FF LD ($FF00),A <- RESET Joypad?! 0003EF: 0003EF: C9 RET <- Return from Subroutine ------------------------------------------------------------------------------ Address - $FF01 Name - SB Contents - Serial transfer data (R/W) 8 Bits of data to be read/written Address - $FF02 Name - SC Contents - SIO control (R/W) Bit 7 - Transfer start flag 0: Non transfer 1: Start transfer Bit 0 - Shift Clock 0: External Clock 1: Internal Clock ------------------------------------------------------------------------------ Address - $FF04 Name - DIV Contents - Divider Register (R/W) ------------------------------------------------------------------------------ Address - $FF05 Name - TIMA Contents - Timer counter (R/W) The timer generates an interrupt when it overflows. Address - $FF06 Name - TMA Contents - Timer Modulo (R/W) When the TIMA overflows, this data will be loaded. Address - $FF07 Name - TAC Contents - Timer Control Bit 2 - Timer Stop 0: Stop Timer 1: Start Timer Bits 1+0 - Input Clock Select 00: 4.096 khz 01: 262.144 khz 10: 65.536 khz 11: 16.384 khz ------------------------------------------------------------------------------ Address - $FF0F Name - IF Contents - Interrupt Flag (R/W) Bit 4: Transition from High to Low of Pin number P10-P13 Bit 3: Serial I/O transfer end Bit 2: Timer Overflow Bit 1: LCDC (see STAT) Bit 0: V-Blank Address - $FFFF Name - IE Contents - Interrupt Enable (R/W) Bit 4: Transition from High to Low of Pin number P10-P13 Bit 3: Serial I/O transfer end Bit 2: Timer Overflow Bit 1: LCDC (see STAT) Bit 0: V-Blank 0: disable 1: enable Address - XXXX (CPU instruction command) Name - IME Content - Interrupt Master Enable To prohibit ALL interrupts use CPU instruction DI To acknowledge interrupt settings use CPU instruction EI DI - Disable Interrupts EI - Enable Interrupts The priority and jump address for the above 5 interrupts are: Interrupt Priority Start Address V-Blank 1 $0040 LCDC Status 2 $0048 - Modes 0, 01, 10 LYC=LY coincide (selectable) Timer Overflow 3 $0050 Serial Transfer 4 $0058 - when transfer is complete Hi-Lo Of Pin 5 $0060 * When more than 1 interrupts occur at the same time ONLY the interrupt with the highest priority can be acknowledged. When an interrupt is used a '0' should be stored in the IF register before the IE register is set. ----------------------------------------------------------------------------- Address - $FF40 Name - LCDC Contents - LCD Control (R/W) Bit 7 - LCD Control Operation 0: Stop completely (no picture on screen) 1: operation Bit 6 - Window Screen Display Data Select 0: $9800-$9BFF 1: $9C00-$9FFF Bit 5 - Window Display 0: off 1: on Bit 4 - BG Character Data Select 0: $8800-$97FF 1: $8000-$8FFF <- Same area as OBJ Bit 3 - BG Screen Display Data Select 0: $9800-$9BFF 1: $9C00-$9FFF Bit 2 - OBJ Construction 0: 8*8 1: 8*16 Bit 1 - OBJ Display 0: off 1: on Bit 0 - BG Display 0: off 1: on Address - $FF41 Name - STAT Contents - LCDC Status (R/W) Bits 6-3 - Interrupt Selection By LCDC Status Bit 6 - LYC=LY Coincidence (Selectable) Bit 5 - Mode 10 Bit 4 - Mode 01 Bit 3 - Mode 00 0: Non Selection 1: Selection Bit 2 - Coincidence Flag 0: LYC not equal to LCDC LY 1: LYC = LCDC LY Bit 1-0 - Mode Flag 00: Entire Display Ram can be accessed 01: During V-Blank 10: During Searching OAM-RAM 11: During Transfering Data to LCD Driver STAT shows the current status of the LCD controller. Mode 00: When the flag is 00 it is the H-Blank period and the CPU can access the display RAM ($8000-$9FFF) When it is not equal the display ram is being used by the LCD controller Mode 01: When the flag is 01 it is the V-Blank period and the CPU can access the display RAM ($800-$9FFF) Mode 10: When the flag is 10 then the OAM is being used ($FE00-$FE90) The CPU cannot access the OAM during this period Mode 11: When the flag is 11 both the OAM and CPU are being used. The CPU cannot access either during this period ----------------------------------------------------------------------------- Address - $FF42 Name - SCY Contents - Scroll Y (R/W) 8 Bit value $00-$FF to scroll BG Y screen position Address - $FF43 Name - SCX Contents - Scroll X (R/W) 8 Bit value $00-$FF to scroll BG X screen position Address - $FF44 Name - LY Contents - LCDC Y-Coordinate (R) The LY indicates the vertical line to which the present data is transferred to the LCD Driver The LY can take on any value between 0 through 153. The values between 144 and 153 indicate the V-Blank period. Writing will reset the counter. This is just a RASTER register. The current line is thrown into here. But since there are no RASTERS on an LCD display..... it's called the LCDC Y-Coordinate. Address - $FF45 Name - LYC Contents - LY Compare (R/W) The LYC compares itself with the LY. If the values are the same it causes the STAT to set the coincident flag. Address - $FF47 Name - BGP Contents - BG Palette Data (W) Bit 7-6 - Data for Dot Data 11 Bit 5-4 - Data for Dot Data 10 Bit 3-2 - Data for Dot Data 01 Bit 1-0 - Data for Dot Data 00 This selects the shade of gray you what for your BG pixel. Since each pixel uses 2 bits, the corresponding shade will be selected from here. The Background Color (00) lies at Bits 1-0, just put a value from 0-$3 to change the color. Address - $FF48 Name - OBP0 Contents - Object Palette 0 Data (W) This selects the colors for sprite palette 0. It works exactly as BGP ($FF47). See BGP for details. Address - $FF49 Name - OBP1 Contents - Object Palette 1 Data (W) This Selects the colors for sprite palette 1. It works exactly as BGP ($FF47). See BGP for details. Address - $FF4A Name - WY Contents - Window Y Position (R/W) 0 <= WY <= 143 WY must be greater than or equal to 0 and must be less than or equal to 143. Address - $FF4B Name - WX Contents - Window X Position (R/W) 7 <= WX <= 166 WX must be greater than or equal to 7 and must be less than or equal to 166. Lets say WY = 80 and WX = 80. The window would be positioned as so: 0 80 159 _________________________________________________ 0 | | | | | | | | | | | | | | | | | | | | | | | | | | | | |80 | 80 |-------------------+-----------------------------| | 80 | | | | | | | Window Display | | | | | | | | | Here | | | | | | | | | | 143 |___________________|_____________________________| OBJ Characters (Sprites) can still enter the window So can BG characters ----------------------------------------------------------------------------- Address - $FF46 Name - DMA Contents - DMA Transfer and Start Address (W) The DMA Transfer (40*28 bit) from internal ROM or RAM ($0000-$F19F) to the OAM (address $FE00-$FE9F) can be performed. It takes 160 nano-seconds for the transfer. 40*28 bit = #140 or #$8C. As you can see, it only transfers $8C bytes of data. OAM data is $A0 bytes long, from $0-$9F. But if you examine the OAM data you see that 4 bits are not in use. 40*32 bit = #$A0, but since 4 bits for each OAM is not used it's 40*28 bit. It transfers all the OAM data to OAM RAM. The DMA transfer start address can be designated every $100 from address $0000-$F100. That means $0000, $0100, $0200, $0300.... Example program: DI <- Disable Interrupt LD A,#$04 <- transfer data from $0400 LD ($FF46),A <- put A into DMA registers LD A,#40 <- #40 is the value to wait for. we need to wait 160 Wait: <- nano seconds DEC A <- decrease A by 1 JR NZ,Wait <- branch if Not Zero to Wait EI <- Enable Interrupt RET <- RETurn from sub-routine ----------------------------------------------------------------------------- Address - $FF10 Name - NR 10 Contents - Sound Mode 1 register, Sweep register (R/W) Bit 6-4 - Sweep Time Bit 3 - Sweep Increase/Decrease 0: Addition (frequency increases) 1: Subtraction (frequency increases) Bit 2-0 - Number of sweep shift (# 0-7) Sweep Time: 000: sweep off 001: 7.8 ms 010: 15.6 ms 011: 23.4 ms 100: 31.3 ms 101: 39.1 ms 110: 46.9 ms 111: 54.7 ms Address - $FF11 Name - NR 11 Contents - Sound Mode 1 register, Sound length/Wave pattern duty (R/W) Only Bits 7-6 can be read. Bit 7-6 - Wave Pattern Duty Bit 5-0 - Sound length data (# 0-63) Wave Duty: 00: 12.5% 01: 25% 10: 50% 11: 75% Address - $FF12 Name - NR 12 Contents - Sound Mode 1 register, Envelope (R/W) Bit 7-4 - Initial value of envelope Bit 3 - Envelope UP/DOWN 0: Decrease 1: Range of increase Bit 2-0 - Number of envelope sweep (# 0-7) Initial value of envelope is from %0000 to %1111 Address - $FF13 Name - NR 13 Contents - Sound Mode 1 register, Frequency lo (W) lower 8 bits of 11 bit frequency. Next 3 bit or in NR 14 ($FF14) Address - $FF14 Name - NR 14 Contents - Sound Mode 1 register, Frequency hi (R/W) Only Bit 6 can be read. Bit 7 - Initial (when set, sound restarts) Bit 6 - Counter/consecutive selection Bit 2-0 - Frequency's higher 3 bits Address - $FF16 Name - NR 21 Contents - Sound Mode 2 register, Sound Length; Wave Pattern Duty (R/W) Only bits 7-6 can be read. Bit 7-6 - Wave pattern duty Bit 5-0 - Sound length (# 0-63) Address - $FF17 Name - NR 22 Contents - Sound Mode 2 register, envelope (R/W) Bit 7-4 - Initial envelope value Bit 3 - Envelope UP/DOWN 0: decrease 1: range of increase Bit 2-0 - Number of envelope step (# 0-7) Address - $FF18 Name - NR 23 Contents - Sound Mode 2 register, frequency lo data (W) Frequency's lower 8 bits of 11 bit data Next 3 bits are in NR 14 ($FF19) Address - $FF19 Name - NR 24 Contents - Sound Mode 2 register, frequency hi data (R/W) Only bit 6 can be read. Bit 7 - Initial Bit 6 - Counter/consecutive selection Bit 2-0 - Frequency's higher 3 bits Address - $FF1A Name - NR 30 Contents - Sound Mode 3 register, Sound on/off (R/W) Only bit 7 can be read Bit 7 - Sound OFF 0: Sound 3 output stop 1: Sound 3 output OK Address - $FF1B Name - NR 31 Contents - Sound Mode 3 register, sound length (R/W) Bit 7-0 - Sound length Address - $FF1C Name - NR 32 Contents - Sound Mode 3 register, Select output level Only bits 6-5 can be read Bit 6-5 - Select output level 00: Mute 01: Produce Wave Pattern RAM Data as it is (4 bit length) 10: Produce Wave Pattern RAM data shifted once to the RIGHT (1/2) (4 bit length) 11: Produce Wave Pattern RAM data shifted twice to the RIGHt (1/4) (4 bit length) * - Wave Pattern RAM is located from $FF30-$FF3f Address - $FF1D Name - NR 33 Contents - Sound Mode 3 register, frequency's lower data (W) Lower 8 bits of an 11 bit frequency Address - $FF1E Name - NR 34 Contents - Sound Mode 3 register, frequency's higher data (R/W) Only bit 6 can be read. Bit 7 - Initial flag Bit 6 - Counter/consecutive flag Bit 2-0 - Frequency's higher 3 bits Address - $FF20 Name - NR 41 Contents - Sound Mode 4 register, sound length (R/W) Bit 5-0 - Sound length data (# 0-63) Address - $FF21 Name - NR 42 Contents - Sound Mode 4 register, envelope (R/W) Bit 7-4 - Initial value of envelope Bit 3 - Envelope UP/DOWN 0: decrease 1: range of increase Bit 2-0 - number of envelope step (# 0-7) Address - $FF22 Name - NR 43 Contents - Sound Mode 4 register, polynomial counter (R/W) Bit 7-4 - Selection of the shift clock frequency of the polynomial counter Bit 3 - Selection of the polynomial counter's step Bit 2-0 - Selection of the dividing ratio of frequencies Selection of the dividing ratio of frequencies: 000: f * 1/2^3 * 2 001: f * 1/2^3 * 1 010: f * 1/2^3 * 1/2 011: f * 1/2^3 * 1/3 100: f * 1/2^3 * 1/4 101: f * 1/2^3 * 1/5 110: f * 1/2^3 * 1/6 111: f * 1/2^3 * 1/7 f = 4.194304 Mhz Selection of the polynomial counter step: 0: 15 steps 1: 7 steps Selection of the shift clock frequency of the polynomial counter: 0000: dividing ratio of frequencies * 1/2 0001: dividing ratio of frequencies * 1/2^2 0010: dividing ratio of frequencies * 1/2^3 0011: dividing ratio of frequencies * 1/2^4 : : : : : : 0101: dividing ratio of frequencies * 1/2^14 1110: prohibited code 1111: prohibited code Address - $FF30 Name - NR 30 Contents - Sound Mode 4 register, counter/consecutive; inital (R/W) Only bit 6 can be read. Bit 7 - Inital Bit 6 - Counter/consecutive selection Address - $FF24 Name - NR 50 Contents - Channel control / ON-OFF / Volume (R/W) Bit 7 - Vin->SO2 ON/OFF Bit 6-4 - SO2 output level (volume) (# 0-7) Bit 3 - Vin->SO1 ON/OFF Bit 2-0 - SO1 output level (volume) (# 0-7) Vin->SO1 (Vin->SO2) By synthesizing the sound from sound 1 through 4, the voice input from Vin terminal is put out. 0: no output 1: output OK Address - $FF25 Name - NR 51 Contents - Selection of Sound output terminal (R/W) Bit 7 - Output sound 4 to SO2 terminal Bit 6 - Output sound 3 to SO2 terminal Bit 5 - Output sound 2 to SO2 terminal Bit 4 - Output sound 1 to SO2 terminal Bit 3 - Output sound 4 to SO1 terminal Bit 2 - Output sound 3 to SO1 terminal Bit 1 - Output sound 2 to SO1 terminal Bit 0 - Output sound 1 to SO1 terminal Address - $FF26 Name - NR 52 Contents - Sound on/off (R/W) Only Bit 7, 3-0 can be read. Bit 7 - All sound on/off 0: stop all sound circuits 1: operate all sound circuits Bit 3 - Sound 4 ON flag Bit 2 - Sound 3 ON flag Bit 1 - Sound 2 ON flag Bit 0 - Sound 1 ON flag