MetaProducts Inquiry - GameBoyColor

MetaProducts Inquiry Home Page » GameBoyColor

Gameboy (LR35902) OPCODES

URL: http://pastraiser.com/cpu/gameboy/gameboy_opcodes.html

Дата: 15.12.2014 14:38:30

Имя: Gameboy (LR35902) OPCODES

GameBoy Opcodes Summary

URL: http://gameboy.mongenel.com/dmg/opcodes.html

Дата: 15.12.2014 14:39:00

Имя: GameBoy Opcodes Summary

DuoDreamer's Dreamscape

URL: http://gameboy.mongenel.com/dmg/asmmemmap.html

Дата: 15.12.2014 14:39:30

Имя: DuoDreamer's Dreamscape

Programmer-Net : The home of all programming information on the net

URL: http://web.archive.org/web/20030206181618/http://www.enliten.force9.co.uk/gameboy/memory.htm

Дата: 15.12.2014 14:41:49

Имя: Programmer-Net : The home of all programming information on the net

Игровая система GameBoy (Часть 1) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

URL: http://gfaq.ru/publ/sistemy/portativnye_konsoli/igrovaja_sistema_gameboy_chast_1/36-1-0-1925

Дата: 15.12.2014 14:42:20

Имя: Игровая система GameBoy (Часть 1) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

Игровая система GameBoy (Часть 2) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

URL: http://gfaq.ru/publ/sistemy/portativnye_konsoli/igrovaja_sistema_gameboy_chast_2/36-1-0-1926

Дата: 15.12.2014 14:42:43

Имя: Игровая система GameBoy (Часть 2) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

Игровая система GameBoy (Часть 3) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

URL: http://gfaq.ru/publ/sistemy/portativnye_konsoli/igrovaja_sistema_gameboy_chast_3/36-1-0-1927

Дата: 15.12.2014 14:43:09

Имя: Игровая система GameBoy (Часть 3) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

Игровая система GameBoy (Часть 4) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

URL: http://gfaq.ru/publ/sistemy/portativnye_konsoli/igrovaja_sistema_gameboy_chast_4/36-1-0-1928

Дата: 15.12.2014 14:43:34

Имя: Игровая система GameBoy (Часть 4) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

The Cartridge Header - GbdevWiki

URL: http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header

Дата: 15.12.2014 14:44:09

Имя: The Cartridge Header - GbdevWiki

GameBoy CPU InstructionSet Sheet (GCISheet)

URL: http://marc.rawer.de/Gameboy/Docs/GBCPU_Instr.html#LD3

Дата: 15.12.2014 14:44:34

Имя: GameBoy CPU InstructionSet Sheet (GCISheet)

ECE238Spr08/tutorials/GameBoyAudioSa... - CRATEL Wiki

URL: http://cratel.wichita.edu/cratel/ECE238Spr08/tutorials/GameBoyAudioSamples

Дата: 15.12.2014 14:45:04

Имя: ECE238Spr08/tutorials/GameBoyAudioSa... - CRATEL Wiki

Programmer-Net : The home of all programming information on the net

URL: http://web.archive.org/web/20130721095956/http://www.enliten.force9.co.uk/gameboy/hardware.htm

Дата: 15.12.2014 14:45:55

Имя: Programmer-Net : The home of all programming information on the net

Specifications

URL: http://web.archive.org/web/20131221162708/http://nocash.emubase.de/pandocs.htm#cpucomparisionwithz80

Дата: 15.12.2014 14:47:17

Имя: Specifications

GB DEV FAQs

URL: http://www.devrs.com/gb/files/faqs.html#TileBased

Дата: 15.12.2014 14:47:42

Имя: GB DEV FAQs

Imran Nazar: GameBoy Z80 Opcode Map

URL: http://imrannazar.com/GameBoy-Z80-Opcode-Map

Дата: 15.12.2014 14:48:42

Имя: Imran Nazar: GameBoy Z80 Opcode Map

Imran Nazar: GameBoy Emulation in JavaScript: Graphics

URL: http://imrannazar.com/GameBoy-Emulation-in-JavaScript:-Graphics

Дата: 15.12.2014 14:49:10

Имя: Imran Nazar: GameBoy Emulation in JavaScript: Graphics

Пишем эмулятор Gameboy, часть 2 / Хабрахабр

URL: http://habrahabr.ru/post/155323/

Дата: 15.12.2014 14:49:36

Имя: Пишем эмулятор Gameboy, часть 2 / Хабрахабр

Пишем эмулятор Gameboy, часть 1 / Хабрахабр

URL: http://habrahabr.ru/post/154901/

Дата: 15.12.2014 14:50:07

Имя: Пишем эмулятор Gameboy, часть 1 / Хабрахабр

Пишем эмулятор Gameboy, часть 3 / Хабрахабр

URL: http://habrahabr.ru/post/156647/

Дата: 15.12.2014 14:50:30

Имя: Пишем эмулятор Gameboy, часть 3 / Хабрахабр

Programmer-Net : The home of all programming information on the net

URL: http://web.archive.org/web/20130721105029/http://www.enliten.force9.co.uk/gameboy/opcodes.htm

Дата: 15.12.2014 14:51:08

Имя: Programmer-Net : The home of all programming information on the net

Programmer-Net : The home of all programming information on the net

URL: http://web.archive.org/web/20130721095945/http://www.enliten.force9.co.uk/gameboy/memory.htm

Дата: 15.12.2014 14:52:23

Имя: Programmer-Net : The home of all programming information on the net

Imran Nazar: GameBoy Emulation in JavaScript: Interrupts

URL: http://imrannazar.com/GameBoy-Emulation-in-JavaScript:-Interrupts

Дата: 15.12.2014 14:52:48

Имя: Imran Nazar: GameBoy Emulation in JavaScript: Interrupts

The Lush Engine Tutorials - Gameboy colour - Palettes

URL: http://web.archive.org/web/20000416025620/http://www.sbirmc.ac.uk/lush/tutorial/gbc/palettes.html

Дата: 15.12.2014 14:54:05

Имя: The Lush Engine Tutorials - Gameboy colour - Palettes

GBDoK

URL: http://www.devrs.com/gb/files/gbdok.html

Дата: 15.12.2014 14:54:24

Имя: GBDoK

Nick Pelling - Fast multiply on the Game Boy?!?!

URL: http://www.nickpelling.com/gameboymultiply.html

Дата: 15.12.2014 14:54:52

Имя: Nick Pelling - Fast multiply on the Game Boy?!?!

GameBoy Sound Table

URL: http://www.devrs.com/gb/files/sndtab.html

Дата: 15.12.2014 14:55:10

Имя: GameBoy Sound Table


Gameboy (LR35902) OPCODES

Gameboy CPU (LR35902) instruction set

 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF
0x | NOP 1 4 - - - - | LD BC,d16 3 12 - - - - | LD (BC),A 1 8 - - - - | INC BC 1 8 - - - - | INC B 1 4 Z 0 H - | DEC B 1 4 Z 1 H - | LD B,d8 2 8 - - - - | RLCA 1 4 0 0 0 C | LD (a16),SP 3 20 - - - - | ADD HL,BC 1 8 - 0 H C | LD A,(BC) 1 8 - - - - | DEC BC 1 8 - - - - | INC C 1 4 Z 0 H - | DEC C 1 4 Z 1 H - | LD C,d8 2 8 - - - - | RRCA 1 4 0 0 0 C
1x | STOP 0 2 4 - - - - | LD DE,d16 3 12 - - - - | LD (DE),A 1 8 - - - - | INC DE 1 8 - - - - | INC D 1 4 Z 0 H - | DEC D 1 4 Z 1 H - | LD D,d8 2 8 - - - - | RLA 1 4 0 0 0 C | JR r8 2 12 - - - - | ADD HL,DE 1 8 - 0 H C | LD A,(DE) 1 8 - - - - | DEC DE 1 8 - - - - | INC E 1 4 Z 0 H - | DEC E 1 4 Z 1 H - | LD E,d8 2 8 - - - - | RRA 1 4 0 0 0 C
2x | JR NZ,r8 2 12/8 - - - - | LD HL,d16 3 12 - - - - | LD (HL+),A 1 8 - - - - | INC HL 1 8 - - - - | INC H 1 4 Z 0 H - | DEC H 1 4 Z 1 H - | LD H,d8 2 8 - - - - | DAA 1 4 Z - 0 C | JR Z,r8 2 12/8 - - - - | ADD HL,HL 1 8 - 0 H C | LD A,(HL+) 1 8 - - - - | DEC HL 1 8 - - - - | INC L 1 4 Z 0 H - | DEC L 1 4 Z 1 H - | LD L,d8 2 8 - - - - | CPL 1 4 - 1 1 -
3x | JR NC,r8 2 12/8 - - - - | LD SP,d16 3 12 - - - - | LD (HL-),A 1 8 - - - - | INC SP 1 8 - - - - | INC (HL) 1 12 Z 0 H - | DEC (HL) 1 12 Z 1 H - | LD (HL),d8 2 12 - - - - | SCF 1 4 - 0 0 1 | JR C,r8 2 12/8 - - - - | ADD HL,SP 1 8 - 0 H C | LD A,(HL-) 1 8 - - - - | DEC SP 1 8 - - - - | INC A 1 4 Z 0 H - | DEC A 1 4 Z 1 H - | LD A,d8 2 8 - - - - | CCF 1 4 - 0 0 C
4x | LD B,B 1 4 - - - - | LD B,C 1 4 - - - - | LD B,D 1 4 - - - - | LD B,E 1 4 - - - - | LD B,H 1 4 - - - - | LD B,L 1 4 - - - - | LD B,(HL) 1 8 - - - - | LD B,A 1 4 - - - - | LD C,B 1 4 - - - - | LD C,C 1 4 - - - - | LD C,D 1 4 - - - - | LD C,E 1 4 - - - - | LD C,H 1 4 - - - - | LD C,L 1 4 - - - - | LD C,(HL) 1 8 - - - - | LD C,A 1 4 - - - -
5x | LD D,B 1 4 - - - - | LD D,C 1 4 - - - - | LD D,D 1 4 - - - - | LD D,E 1 4 - - - - | LD D,H 1 4 - - - - | LD D,L 1 4 - - - - | LD D,(HL) 1 8 - - - - | LD D,A 1 4 - - - - | LD E,B 1 4 - - - - | LD E,C 1 4 - - - - | LD E,D 1 4 - - - - | LD E,E 1 4 - - - - | LD E,H 1 4 - - - - | LD E,L 1 4 - - - - | LD E,(HL) 1 8 - - - - | LD E,A 1 4 - - - -
6x | LD H,B 1 4 - - - - | LD H,C 1 4 - - - - | LD H,D 1 4 - - - - | LD H,E 1 4 - - - - | LD H,H 1 4 - - - - | LD H,L 1 4 - - - - | LD H,(HL) 1 8 - - - - | LD H,A 1 4 - - - - | LD L,B 1 4 - - - - | LD L,C 1 4 - - - - | LD L,D 1 4 - - - - | LD L,E 1 4 - - - - | LD L,H 1 4 - - - - | LD L,L 1 4 - - - - | LD L,(HL) 1 8 - - - - | LD L,A 1 4 - - - -
7x | LD (HL),B 1 8 - - - - | LD (HL),C 1 8 - - - - | LD (HL),D 1 8 - - - - | LD (HL),E 1 8 - - - - | LD (HL),H 1 8 - - - - | LD (HL),L 1 8 - - - - | HALT 1 4 - - - - | LD (HL),A 1 8 - - - - | LD A,B 1 4 - - - - | LD A,C 1 4 - - - - | LD A,D 1 4 - - - - | LD A,E 1 4 - - - - | LD A,H 1 4 - - - - | LD A,L 1 4 - - - - | LD A,(HL) 1 8 - - - - | LD A,A 1 4 - - - -
8x | ADD A,B 1 4 Z 0 H C | ADD A,C 1 4 Z 0 H C | ADD A,D 1 4 Z 0 H C | ADD A,E 1 4 Z 0 H C | ADD A,H 1 4 Z 0 H C | ADD A,L 1 4 Z 0 H C | ADD A,(HL) 1 8 Z 0 H C | ADD A,A 1 4 Z 0 H C | ADC A,B 1 4 Z 0 H C | ADC A,C 1 4 Z 0 H C | ADC A,D 1 4 Z 0 H C | ADC A,E 1 4 Z 0 H C | ADC A,H 1 4 Z 0 H C | ADC A,L 1 4 Z 0 H C | ADC A,(HL) 1 8 Z 0 H C | ADC A,A 1 4 Z 0 H C
9x | SUB B 1 4 Z 1 H C | SUB C 1 4 Z 1 H C | SUB D 1 4 Z 1 H C | SUB E 1 4 Z 1 H C | SUB H 1 4 Z 1 H C | SUB L 1 4 Z 1 H C | SUB (HL) 1 8 Z 1 H C | SUB A 1 4 Z 1 H C | SBC A,B 1 4 Z 1 H C | SBC A,C 1 4 Z 1 H C | SBC A,D 1 4 Z 1 H C | SBC A,E 1 4 Z 1 H C | SBC A,H 1 4 Z 1 H C | SBC A,L 1 4 Z 1 H C | SBC A,(HL) 1 8 Z 1 H C | SBC A,A 1 4 Z 1 H C
Ax | AND B 1 4 Z 0 1 0 | AND C 1 4 Z 0 1 0 | AND D 1 4 Z 0 1 0 | AND E 1 4 Z 0 1 0 | AND H 1 4 Z 0 1 0 | AND L 1 4 Z 0 1 0 | AND (HL) 1 8 Z 0 1 0 | AND A 1 4 Z 0 1 0 | XOR B 1 4 Z 0 0 0 | XOR C 1 4 Z 0 0 0 | XOR D 1 4 Z 0 0 0 | XOR E 1 4 Z 0 0 0 | XOR H 1 4 Z 0 0 0 | XOR L 1 4 Z 0 0 0 | XOR (HL) 1 8 Z 0 0 0 | XOR A 1 4 Z 0 0 0
Bx | OR B 1 4 Z 0 0 0 | OR C 1 4 Z 0 0 0 | OR D 1 4 Z 0 0 0 | OR E 1 4 Z 0 0 0 | OR H 1 4 Z 0 0 0 | OR L 1 4 Z 0 0 0 | OR (HL) 1 8 Z 0 0 0 | OR A 1 4 Z 0 0 0 | CP B 1 4 Z 1 H C | CP C 1 4 Z 1 H C | CP D 1 4 Z 1 H C | CP E 1 4 Z 1 H C | CP H 1 4 Z 1 H C | CP L 1 4 Z 1 H C | CP (HL) 1 8 Z 1 H C | CP A 1 4 Z 1 H C
Cx | RET NZ 1 20/8 - - - - | POP BC 1 12 - - - - | JP NZ,a16 3 16/12 - - - - | JP a16 3 16 - - - - | CALL NZ,a16 3 24/12 - - - - | PUSH BC 1 16 - - - - | ADD A,d8 2 8 Z 0 H C | RST 00H 1 16 - - - - | RET Z 1 20/8 - - - - | RET 1 16 - - - - | JP Z,a16 3 16/12 - - - - | PREFIX CB 1 4 - - - - | CALL Z,a16 3 24/12 - - - - | CALL a16 3 24 - - - - | ADC A,d8 2 8 Z 0 H C | RST 08H 1 16 - - - -
Dx | RET NC 1 20/8 - - - - | POP DE 1 12 - - - - | JP NC,a16 3 16/12 - - - - |  | CALL NC,a16 3 24/12 - - - - | PUSH DE 1 16 - - - - | SUB d8 2 8 Z 1 H C | RST 10H 1 16 - - - - | RET C 1 20/8 - - - - | RETI 1 16 - - - - | JP C,a16 3 16/12 - - - - |  | CALL C,a16 3 24/12 - - - - |  | SBC A,d8 2 8 Z 1 H C | RST 18H 1 16 - - - -
Ex | LDH (a8),A 2 12 - - - - | POP HL 1 12 - - - - | LD (C),A 2 8 - - - - |  |  | PUSH HL 1 16 - - - - | AND d8 2 8 Z 0 1 0 | RST 20H 1 16 - - - - | ADD SP,r8 2 16 0 0 H C | JP (HL) 1 4 - - - - | LD (a16),A 3 16 - - - - |  |  |  | XOR d8 2 8 Z 0 0 0 | RST 28H 1 16 - - - -
Fx | LDH A,(a8) 2 12 - - - - | POP AF 1 12 Z N H C | LD A,(C) 2 8 - - - - | DI 1 4 - - - - |  | PUSH AF 1 16 - - - - | OR d8 2 8 Z 0 0 0 | RST 30H 1 16 - - - - | LD HL,SP+r8 2 12 0 0 H C | LD SP,HL 1 8 - - - - | LD A,(a16) 3 16 - - - - | EI 1 4 - - - - |  |  | CP d8 2 8 Z 1 H C | RST 38H 1 16 - - - -

Prefix CB

 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | xA | xB | xC | xD | xE | xF
0x | RLC B 2 8 Z 0 0 C | RLC C 2 8 Z 0 0 C | RLC D 2 8 Z 0 0 C | RLC E 2 8 Z 0 0 C | RLC H 2 8 Z 0 0 C | RLC L 2 8 Z 0 0 C | RLC (HL) 2 16 Z 0 0 C | RLC A 2 8 Z 0 0 C | RRC B 2 8 Z 0 0 C | RRC C 2 8 Z 0 0 C | RRC D 2 8 Z 0 0 C | RRC E 2 8 Z 0 0 C | RRC H 2 8 Z 0 0 C | RRC L 2 8 Z 0 0 C | RRC (HL) 2 16 Z 0 0 C | RRC A 2 8 Z 0 0 C
1x | RL B 2 8 Z 0 0 C | RL C 2 8 Z 0 0 C | RL D 2 8 Z 0 0 C | RL E 2 8 Z 0 0 C | RL H 2 8 Z 0 0 C | RL L 2 8 Z 0 0 C | RL (HL) 2 16 Z 0 0 C | RL A 2 8 Z 0 0 C | RR B 2 8 Z 0 0 C | RR C 2 8 Z 0 0 C | RR D 2 8 Z 0 0 C | RR E 2 8 Z 0 0 C | RR H 2 8 Z 0 0 C | RR L 2 8 Z 0 0 C | RR (HL) 2 16 Z 0 0 C | RR A 2 8 Z 0 0 C
2x | SLA B 2 8 Z 0 0 C | SLA C 2 8 Z 0 0 C | SLA D 2 8 Z 0 0 C | SLA E 2 8 Z 0 0 C | SLA H 2 8 Z 0 0 C | SLA L 2 8 Z 0 0 C | SLA (HL) 2 16 Z 0 0 C | SLA A 2 8 Z 0 0 C | SRA B 2 8 Z 0 0 0 | SRA C 2 8 Z 0 0 0 | SRA D 2 8 Z 0 0 0 | SRA E 2 8 Z 0 0 0 | SRA H 2 8 Z 0 0 0 | SRA L 2 8 Z 0 0 0 | SRA (HL) 2 16 Z 0 0 0 | SRA A 2 8 Z 0 0 0
3x | SWAP B 2 8 Z 0 0 0 | SWAP C 2 8 Z 0 0 0 | SWAP D 2 8 Z 0 0 0 | SWAP E 2 8 Z 0 0 0 | SWAP H 2 8 Z 0 0 0 | SWAP L 2 8 Z 0 0 0 | SWAP (HL) 2 16 Z 0 0 0 | SWAP A 2 8 Z 0 0 0 | SRL B 2 8 Z 0 0 C | SRL C 2 8 Z 0 0 C | SRL D 2 8 Z 0 0 C | SRL E 2 8 Z 0 0 C | SRL H 2 8 Z 0 0 C | SRL L 2 8 Z 0 0 C | SRL (HL) 2 16 Z 0 0 C | SRL A 2 8 Z 0 0 C
4x | BIT 0,B 2 8 Z 0 1 - | BIT 0,C 2 8 Z 0 1 - | BIT 0,D 2 8 Z 0 1 - | BIT 0,E 2 8 Z 0 1 - | BIT 0,H 2 8 Z 0 1 - | BIT 0,L 2 8 Z 0 1 - | BIT 0,(HL) 2 16 Z 0 1 - | BIT 0,A 2 8 Z 0 1 - | BIT 1,B 2 8 Z 0 1 - | BIT 1,C 2 8 Z 0 1 - | BIT 1,D 2 8 Z 0 1 - | BIT 1,E 2 8 Z 0 1 - | BIT 1,H 2 8 Z 0 1 - | BIT 1,L 2 8 Z 0 1 - | BIT 1,(HL) 2 16 Z 0 1 - | BIT 1,A 2 8 Z 0 1 -
5x | BIT 2,B 2 8 Z 0 1 - | BIT 2,C 2 8 Z 0 1 - | BIT 2,D 2 8 Z 0 1 - | BIT 2,E 2 8 Z 0 1 - | BIT 2,H 2 8 Z 0 1 - | BIT 2,L 2 8 Z 0 1 - | BIT 2,(HL) 2 16 Z 0 1 - | BIT 2,A 2 8 Z 0 1 - | BIT 3,B 2 8 Z 0 1 - | BIT 3,C 2 8 Z 0 1 - | BIT 3,D 2 8 Z 0 1 - | BIT 3,E 2 8 Z 0 1 - | BIT 3,H 2 8 Z 0 1 - | BIT 3,L 2 8 Z 0 1 - | BIT 3,(HL) 2 16 Z 0 1 - | BIT 3,A 2 8 Z 0 1 -
6x | BIT 4,B 2 8 Z 0 1 - | BIT 4,C 2 8 Z 0 1 - | BIT 4,D 2 8 Z 0 1 - | BIT 4,E 2 8 Z 0 1 - | BIT 4,H 2 8 Z 0 1 - | BIT 4,L 2 8 Z 0 1 - | BIT 4,(HL) 2 16 Z 0 1 - | BIT 4,A 2 8 Z 0 1 - | BIT 5,B 2 8 Z 0 1 - | BIT 5,C 2 8 Z 0 1 - | BIT 5,D 2 8 Z 0 1 - | BIT 5,E 2 8 Z 0 1 - | BIT 5,H 2 8 Z 0 1 - | BIT 5,L 2 8 Z 0 1 - | BIT 5,(HL) 2 16 Z 0 1 - | BIT 5,A 2 8 Z 0 1 -
7x | BIT 6,B 2 8 Z 0 1 - | BIT 6,C 2 8 Z 0 1 - | BIT 6,D 2 8 Z 0 1 - | BIT 6,E 2 8 Z 0 1 - | BIT 6,H 2 8 Z 0 1 - | BIT 6,L 2 8 Z 0 1 - | BIT 6,(HL) 2 16 Z 0 1 - | BIT 6,A 2 8 Z 0 1 - | BIT 7,B 2 8 Z 0 1 - | BIT 7,C 2 8 Z 0 1 - | BIT 7,D 2 8 Z 0 1 - | BIT 7,E 2 8 Z 0 1 - | BIT 7,H 2 8 Z 0 1 - | BIT 7,L 2 8 Z 0 1 - | BIT 7,(HL) 2 16 Z 0 1 - | BIT 7,A 2 8 Z 0 1 -
8x | RES 0,B 2 8 - - - - | RES 0,C 2 8 - - - - | RES 0,D 2 8 - - - - | RES 0,E 2 8 - - - - | RES 0,H 2 8 - - - - | RES 0,L 2 8 - - - - | RES 0,(HL) 2 16 - - - - | RES 0,A 2 8 - - - - | RES 1,B 2 8 - - - - | RES 1,C 2 8 - - - - | RES 1,D 2 8 - - - - | RES 1,E 2 8 - - - - | RES 1,H 2 8 - - - - | RES 1,L 2 8 - - - - | RES 1,(HL) 2 16 - - - - | RES 1,A 2 8 - - - -
9x | RES 2,B 2 8 - - - - | RES 2,C 2 8 - - - - | RES 2,D 2 8 - - - - | RES 2,E 2 8 - - - - | RES 2,H 2 8 - - - - | RES 2,L 2 8 - - - - | RES 2,(HL) 2 16 - - - - | RES 2,A 2 8 - - - - | RES 3,B 2 8 - - - - | RES 3,C 2 8 - - - - | RES 3,D 2 8 - - - - | RES 3,E 2 8 - - - - | RES 3,H 2 8 - - - - | RES 3,L 2 8 - - - - | RES 3,(HL) 2 16 - - - - | RES 3,A 2 8 - - - -
Ax | RES 4,B 2 8 - - - - | RES 4,C 2 8 - - - - | RES 4,D 2 8 - - - - | RES 4,E 2 8 - - - - | RES 4,H 2 8 - - - - | RES 4,L 2 8 - - - - | RES 4,(HL) 2 16 - - - - | RES 4,A 2 8 - - - - | RES 5,B 2 8 - - - - | RES 5,C 2 8 - - - - | RES 5,D 2 8 - - - - | RES 5,E 2 8 - - - - | RES 5,H 2 8 - - - - | RES 5,L 2 8 - - - - | RES 5,(HL) 2 16 - - - - | RES 5,A 2 8 - - - -
Bx | RES 6,B 2 8 - - - - | RES 6,C 2 8 - - - - | RES 6,D 2 8 - - - - | RES 6,E 2 8 - - - - | RES 6,H 2 8 - - - - | RES 6,L 2 8 - - - - | RES 6,(HL) 2 16 - - - - | RES 6,A 2 8 - - - - | RES 7,B 2 8 - - - - | RES 7,C 2 8 - - - - | RES 7,D 2 8 - - - - | RES 7,E 2 8 - - - - | RES 7,H 2 8 - - - - | RES 7,L 2 8 - - - - | RES 7,(HL) 2 16 - - - - | RES 7,A 2 8 - - - -
Cx | SET 0,B 2 8 - - - - | SET 0,C 2 8 - - - - | SET 0,D 2 8 - - - - | SET 0,E 2 8 - - - - | SET 0,H 2 8 - - - - | SET 0,L 2 8 - - - - | SET 0,(HL) 2 16 - - - - | SET 0,A 2 8 - - - - | SET 1,B 2 8 - - - - | SET 1,C 2 8 - - - - | SET 1,D 2 8 - - - - | SET 1,E 2 8 - - - - | SET 1,H 2 8 - - - - | SET 1,L 2 8 - - - - | SET 1,(HL) 2 16 - - - - | SET 1,A 2 8 - - - -
Dx | SET 2,B 2 8 - - - - | SET 2,C 2 8 - - - - | SET 2,D 2 8 - - - - | SET 2,E 2 8 - - - - | SET 2,H 2 8 - - - - | SET 2,L 2 8 - - - - | SET 2,(HL) 2 16 - - - - | SET 2,A 2 8 - - - - | SET 3,B 2 8 - - - - | SET 3,C 2 8 - - - - | SET 3,D 2 8 - - - - | SET 3,E 2 8 - - - - | SET 3,H 2 8 - - - - | SET 3,L 2 8 - - - - | SET 3,(HL) 2 16 - - - - | SET 3,A 2 8 - - - -
Ex | SET 4,B 2 8 - - - - | SET 4,C 2 8 - - - - | SET 4,D 2 8 - - - - | SET 4,E 2 8 - - - - | SET 4,H 2 8 - - - - | SET 4,L 2 8 - - - - | SET 4,(HL) 2 16 - - - - | SET 4,A 2 8 - - - - | SET 5,B 2 8 - - - - | SET 5,C 2 8 - - - - | SET 5,D 2 8 - - - - | SET 5,E 2 8 - - - - | SET 5,H 2 8 - - - - | SET 5,L 2 8 - - - - | SET 5,(HL) 2 16 - - - - | SET 5,A 2 8 - - - -
Fx | SET 6,B 2 8 - - - - | SET 6,C 2 8 - - - - | SET 6,D 2 8 - - - - | SET 6,E 2 8 - - - - | SET 6,H 2 8 - - - - | SET 6,L 2 8 - - - - | SET 6,(HL) 2 16 - - - - | SET 6,A 2 8 - - - - | SET 7,B 2 8 - - - - | SET 7,C 2 8 - - - - | SET 7,D 2 8 - - - - | SET 7,E 2 8 - - - - | SET 7,H 2 8 - - - - | SET 7,L 2 8 - - - - | SET 7,(HL) 2 16 - - - - | SET 7,A 2 8 - - - -

         Misc/control instructions
         Jumps/calls
         8bit load/store/move instructions
         16bit load/store/move instructions
         8bit arithmetic/logical instructions
         16bit arithmetic/logical instructions
         8bit rotations/shifts and bit instructions

Length in bytes ›
            INS reg
2  8
Z N H C 		  ‹ Instruction mnemonic
‹ Duration in cycles
‹ Flags affected
  Duration of conditional calls and returns is different when action is taken or not. This is indicated by two numbers separated by "/". The higher number (on the left side of "/") means duration of instruction when action is taken, the lower number (on the right side of "/") means duration of instruction when action is not taken.

Instruction STOP has according to manuals opcode 10 00 and thus is 2 bytes long. Anyhow it seems there is no reason for it so some assemblers code it simply as one byte instruction 10.
Flags affected are always shown in Z H N C order. If flag is marked by "0" it means it is reset after the instruction. If it is marked by "1" it is set. If it is marked by "-" it is not changed. If it is marked by "Z", "N", "H" or "C" corresponding flag is affected as expected by its function.

d8  means immediate 8 bit data
d16 means immediate 16 bit data
a8  means 8 bit unsigned data, which are added to $FF00 in certain instructions (replacement for missing IN and OUT instructions)
a16 means 16 bit address
r8  means 8 bit signed data, which are added to program counter

LD A,(C) has alternative mnemonic LD A,($FF00+C)
LD C,(A) has alternative mnemonic LD ($FF00+C),A
LDH A,(a8) has alternative mnemonic LD A,($FF00+a8)
LDH (a8),A has alternative mnemonic LD ($FF00+a8),A
LD A,(HL+) has alternative mnemonic LD A,(HLI) or LDI A,(HL)
LD (HL+),A has alternative mnemonic LD (HLI),A or LDI (HL),A
LD A,(HL-) has alternative mnemonic LD A,(HLD) or LDD A,(HL)
LD (HL-),A has alternative mnemonic LD (HLD),A or LDD (HL),A
LD HL,SP+r8 has alternative mnemonic LDHL SP,r8

Registers

15 . . . 8 | 7 . . . 0
A (accumulator) | F (flags)
B | C
D | E
H | L

15 . . . 0
SP (stack pointer)
PC (program counter)
Flag register (F) bits:

7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
Z | N | H | C | 0 | 0 | 0 | 0

Z - Zero Flag
N - Subtract Flag
H - Half Carry Flag
C - Carry Flag
0 - Not used, always zero


GameBoy Opcodes Summary

GameBoy Opcode Summary

The GameBoy has instructions & registers similiar to the 8080, 8085, & Z80 microprocessors. The internal 8-bit registers are A, B, C, D, E, F, H, & L. Theses registers may be used in pairs for 16-bit operations as AF, BC, DE, & HL. The two remaining 16-bit registers are the program counter (PC) and the stack pointer (SP).

The F register holds the cpu flags. The operation of these flags is identical to their Z80 relative. The lower four bits of this register always read zero even if written with a one.

Flag Register
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
Z | N | H | C | 0 | 0 | 0 | 0

The GameBoy CPU is based on a subset of the Z80 microprocessor. A summary of these commands is given below.

Mnemonic Symbolic Operation Comments CPU Clocks Flags - Z,N,H,C

8-Bit Loads

LD r,s | r [image] s | s=r,n,(HL) | r=4, n=8, (HL)=8
LD d,r | d [image] r | d=r,(HL) | r=4, (HL)=8
LD d,n | d [image] n |  | r=8, (HL)=12
LD A,(ss) | A [image] (ss) | ss=BC,DE,HL,nn | [BC,DE,HL]=8, nn=16
LD (dd),A | (dd) [image] A | dd=BC,DE,HL,nn
LD A,(C) | A [image] ($FF00+C) | - | 8
LD (C),A | ($FF00+C) [image] A |  | 8
LDD A,(HL) | A [image] (HL), HL [image] HL - 1 |  | 8
LDD (HL),A | (HL) [image] A, HL [image] HL - 1 |  | 8
LDI A,(HL) | A [image] (HL), HL [image] HL + 1 |  | 8
LDI (HL),A | (HL) [image] A, HL [image] HL + 1 |  | 8
LDH (n),A | ($FF00+n) [image] A |  | 12
LDH A,(n) | A [image] ($FF00+n) |  | 12

16-Bit Loads

LD dd,nn | dd [image] nn | dd=BC,DE,HL,SP | 12 | - | - | - | -
LD (nn),SP | (nn) [image] SP | - | 20
LD SP,HL | SP [image] HL |  | 8
LD HL,(SP+e) | HL [image] (SP+e) |  | 12 | 0 | 0 | * | *
PUSH ss | (SP-1) [image] ssh, (SP-2) [image] ssl, SP [image] SP-2 | ss=BC,DE,HL,AF | 16 | - | - | - | -
POP dd | ddl [image] (SP), ddh [image] (SP+1), SP [image] SP+2 | dd=BC,DE,HL,AF | 12

8-Bit ALU

ADD A,s | A [image] A + s | CY is the carry flag. s=r,n,(HL) | r=4, n=8, (HL)=8 | * | 0 | * | *
ADC A,s | A [image] A + s + CY
SUB s | A [image] A - s |  |  | * | 1 | * | *
SBC A,s | A [image] A - s - CY
AND s | A [image] A [image] s |  |  | * | 0 | 1 | 0
OR s | A [image] A [image] s |  |  | * | 0 | 0 | 0
XOR s | A [image] A [image] s
CP s | A - s |  |  | * | 1 | * | *
INC s | s [image] s + 1 | s=r,(HL) | r=4, (HL)=12 | * | 0 | * | -
DEC s | s [image] s - 1 |  |  | * | 1 | * | -

16-Bit Arithmetic

ADD HL,ss | HL [image] HL + ss | ss=BC,DE,HL,SP | 8 | - | 0 | * | *
ADD SP,e | SP [image] SP + e |  | 16 | 0 | 0 | * | *
INC ss | ss [image] ss + 1 |  | 8 | - | - | - | -
DEC ss | ss [image] ss - 1 |  | 8

Miscellaneous

SWAP s |  | Swap nibbles. s=r,(HL) | r=8, (HL)=16 | * | 0 | 0 | 0
DAA | Converts A into packed BCD. | - | 4 | * | - | 0 | *
CPL | A [image] /A |  | 4 | - | 1 | 1 | -
CCF | CY [image] /CY | CY is the carry flag. | 4 | - | 0 | 0 | *
SCF | CY [image] 1 |  | 4 | - | 0 | 0 | 1
NOP | No operation. | - | 4 | - | - | - | -
HALT | Halt CPU until an interrupt occurs. |  | 4
STOP | Halt CPU. |  | 4
DI | Disable Interrupts. |  | 4
EI | Enable Interrupts. |  | 4

Rotates & Shifts

RLCA |  | 4 | 0 | 0 | 0 | *
RLA
RRCA
RRA
RLC s | s=A,r,(HL) | r=8,(HL)=16 | * | 0 | 0 | *
RL s
RRC s
RR s
SLA s | s=r,(HL) | r=8, (HL)=16
SRA s
SRL s

Bit Opcodes

BIT b,s | Z [image] /sb | Z is zero flag. s=r,(HL) | r=8, (HL)=12 | * | 0 | 1 | -
SET b,s | sb [image] 1 |  | r=8, (HL)=16 | - | - | - | -
RES b,s | sb [image] 0

Jumps

JP nn | PC [image] nn | - | 16
JP cc,nn | If cc is true, PC [image] nn, else continue. |  | If cc is true, 16 else 12.
JP (HL) | PC [image] HL |  | 4
JR e | PC [image] PC + e |  | 12
JR cc,e | if cc is true, PC [image] PC + e, else continue. |  | If cc is true, 12 else 8.

Calls

CALL nn | (SP-1) [image] PCh, (SP-2) [image] PCl, PC [image] nn, SP [image] SP-2 | - | 24
CALL cc,nn | If condition cc is false continue, else same as CALL nn. |  | If cc is true, 24 else 12.

Restarts

RST f (SP-1) [image] PCh, (SP-2) [image] PCl, PCh [image] 0, PCl [image] f, SP [image] SP-2 - 16

Returns

RET | pcl [image] (SP), pch [image] (SP+1), SP [image] SP+2 | - | 16
RET cc | If cc is true, RET else continue. |  | If cc is true, 20 else 8.
RETI | Return then enable interrupts. |  | 16

Terminology

- | Flag is not affected by this operation.
* | Flag is affected according to result of operation.
b | A bit number in any 8-bit register or memory location.
C | Carry flag.
cc | Flag condition code: C,NC,NZ,Z
d | Any 8-bit destination register or memory location.
dd | Any 16-bit destination register or memory location.
e | 8-bit signed 2's complement displacement.
f | 8 special call locations in page zero.
H | Half-carry flag.
N | Subtraction flag.
NC | Not carry flag
NZ | Not zero flag.
n | Any 8-bit binary number.
nn | Any 16-bit binary number.
r | Any 8-bit register. (A,B,C,D,E,H, or L)
s | Any 8-bit source register or memory location.
sb | A bit in a specific 8-bit register or memory location.
ss | Any 16-bit source register or memory location.
Z | Zero Flag.


DuoDreamer's Dreamscape

GameBoy Memory Map

The GameBoy contains an 8-bit processor, meaning it can access 8-bits of data at one time. To access this data, it has a 16-bit address bus, which can address 65,536 positions of memory. This address space is split into the following areas.

GameBoy Memory Areas
 | $FFFF | Interrupt Enable Flag
 | $FF80-$FFFE | Zero Page - 127 bytes
 | $FF00-$FF7F | Hardware I/O Registers
 | $FEA0-$FEFF | Unusable Memory
 | $FE00-$FE9F | OAM - Object Attribute Memory
 | $E000-$FDFF | Echo RAM - Reserved, Do Not Use
 | $D000-$DFFF | Internal RAM - Bank 1-7 (switchable - CGB only)
 | $C000-$CFFF | Internal RAM - Bank 0 (fixed)
 | $A000-$BFFF | Cartridge RAM (If Available)
 | $9C00-$9FFF | BG Map Data 2
 | $9800-$9BFF | BG Map Data 1
 | $8000-$97FF | Character RAM
 | $4000-$7FFF | Cartridge ROM - Switchable Banks 1-xx
 | $0150-$3FFF | Cartridge ROM - Bank 0 (fixed)
 | $0100-$014F | Cartridge Header Area
 | $0000-$00FF | Restart and Interrupt Vectors

$0000-$00FF - Restart and Interrupt Vector Table

The first 255 byte area in the Gameboy address space is reserved for Interrupt Vectors and Restart Vectors. Interrupts are a break in program code generated by a piece of the hardware when a certain condition is met. These conditions can be set by the programmer. There are several different types of interrupts, and when an interrupt is generated, the program stops where it is, and jumps to one of the specified locations in the Vector Table. The types of interrups are:
  • V-Blank - This interrupt can be generated when the LCD has drawn the last line on the screen and gone into the vertical blanking period. When this Interrupt is triggered, the CPU stores the current point of execution on the Stack, and jumps to $0040, which will usually contain another jump to the location of the code you want executed during the v-blank period.
  • LCDC - This interrupt can be generated by the LCD controller chip on various conditions, the most commonly used being the LYC condition, which is an interrupt generated when the LCD begins to draw a certain scanline. The scanline it generates an interrupt on is selectable. When triggered, this interrupt forces the CPU to jump to address $0048. For more information, see the section on I/O registers.
  • Timer - The system timer can be set to count at specified frequencies, and will increment a count register. When this register overflows, an interrupt is generated and the CPU jumps to $0050. More information in the I/O register section.
  • Serial - Generated by the serial link controller. The CPU jumps to $0058 when this interrupt is triggered. More info in the I/O register section.
  • Joypad - Generated when a key is pressed. Due to button "bounce", this interrupt may occur several times when a button is pressed. More info in the I/O register section.

$0100-$014F - Cartridge Header Area

This area in the GameBoy address space contains information about the cartridge that is inserted, including; type of cartridge, size of rom, size of ram, a Nintendo logo, and other information. Also, at $0100, there is a NOP instruction, followed by a JUMP to the start of the program, usually $0150. The GameBoy's CPU begins execution at $0100, that is why this is included. If the Nintendo logo bytes are not correct, the GameBoy will not execute the game. Upon power up, the system ROM in a GameBoy will verify that the logo is correct. Nintendo has tried to use the logo to keep only authorized developers from publishing games, since the logo is a registered trademark of Nintendo. But, a legal precedent was set in the United States in a case of Sega vs. Accolade (c.1993). Sega's Genesis console also has a signature that is a copyright of Sega, and Accolade used this logo so that their games, which were not approved by Sega, would be able to play on the Genesis console. The judge ruled in favor of Accolade, stating that using a logo to that degree was an anti-competetive practice, and therefore, Accolade was allowed to continue selling their games. At least, that's what I heard. =P

Cart Header
$0100-$0103 | NOP / JP $0150
$0104-$0133 | Nintendo Logo
$0134-$013E | Game Title (Uppercase ASCII)
$013F-$0142 | 4-byte Game Designation
$0143 | Color Compatibility byte
$0144-$0145 | New Licensee Code
$0146 | SGB Compatibility byte
$0147 | Cart Type
$0148 | Cart ROM size
$0149 | Cart RAM size
$014A | Destination code
$014B | Old Licensee code
$014C | Mask ROM version
$014D | Complement checksum
$014E-$014F | Checksum

$0150-$3FFF - ROM Bank 0

Realistically, ROM Bank 0 includes all the data from $0000 to $014Fm but they were special areas, so I made them separate. ROM Bank 0 is the home bank. It's the fist 16K bank of any ROM image, and is a fixed bank, which means you can't switch it out with other banks, like you can with the next section (which is discussed in the next section). When the GameBoy powers up, program execution starts at $0100, which almost always has a jump to $0150 (jp $0150), the first byte of free space. So really, $0150 will be the start of your programs. Since this bank is fixed and CANNOT be changed out, it usually contains the majority of the game's engine, or core routines. So I guess you could say that instead of programming a 64K address space, you're actually restricted to 16K for most things.

$4000-$7FFF - ROM Bank n

This is where alot of your game data will exist... in this 16K space. This area is switchable or banked. You can switch in 16K chunks of the entire ROM in this area, through the use of the Memory Bank Controller (MBC) on the cartridge. How the MBC works is you basically "write" to an area in ROM, and since ROM is by nature READ ONLY MEMORY, writing a value to ROM is futile, except in the case of the MBC. It intercepts the attempted write to ROM and interprets it into a bank switch. the value you try to write to ROM is generally the bank number you want to change to. For example, when using MBC1, one would do this to change to bank 5:
  • LD A,5
  • LD HL,$2000
  • LD (HL),A
The first line loads the value 5 into register A. The second line loads register pair HL with $2000, our destination address. The third line loads the value in register A into the address indexed by register pair HL. Easy, right?

$8000-$97FF - Character RAM

This area is RAM inside the GB unit, and is used exclusively for video purposes. This area is also known as Tile RAM, since it holds tiles. Each tile is 8x8 pixels of 2-bit color, which makes each tile 16 bytes long. This area is also divided up into two modes of tiles, signed and unsigned. In unsigned mode, tiles are numbered from 0-255 at $8000-$9000. In signed mode, tiles are numbered in two's complement from -127 to 128 at $87FF-$97FF. I think... lol. Generally most ppl use 0-255 tiles, since it's nice and easy.

$9800-$9BFF - BG Map Data 1

This 1024-byte long area is what the video processor uses to build the display. Each byte in this space represnts an 8x8 pixel space on the display. This area is 32x32 tiles large... EG: 1024 bytes. The display processor takes each byte and then goes into the Character RAM area and gets the corresponding tile from that area and draws it to the screen. So, if the first byte in the Map area contained $40, the display processor would get tile $40 from the Character RAM and put it in the top-left corner of the virtual screen. I say virtual screen because the actual LCD is only 160x144 pixels in size and is basically a viewport that can be scrolled around the 32x32 tile (256x256 pixel) background area in video memory.

$9C00-$9FFF - BG Map Data 2

This area is just a second background map area like the previous one. To specify which map the video processor uses to build the background image, change the apropriate bit in the LCDC I/O register, explained later.

$A000-$BFFF - External (cartridge) RAM

If present on the cartridge, this area is mapped to the RAM on the cartridge.

$C000-$DFFF - Internal Work RAM

This RAM in inside the GameBoy. Generally used for most common variables and such in games.

$E000-$FDFF - Reserved Area/Echo RAM

This area echoes internal ram, but is specified by Nintendo as reserved and shouldn't be used at all. To keep with standards and to keep compatibility, don't use this area.

$FE00-$FE9F - Object Attribute Memory (OAM)

OAM is sprite RAM. This area is 40 sprites X 4 bytes long. When you with to display an object (sprite) you write 4 corresponding bytes to OAM. These 4 bytes are:
  • Byte 1: X Location
  • Byte 2: Y Location
  • Tile Number (0-255)
  • Attributes
The tile number is taken from the Character RAM, just a BG tiles are. The X and Y locations are slightly offset (8 pixels and 16 pixels), so you can have sprites partially off of the left and top of the LCD. So if you set the location to 0,0 then the sprite would be off of the screen. To set a sprite to the top-left corner, you'd set it's location to 8,16. Piece of cake. ;-)

$FEA0-$FEFF - Unused

And it'd be smart to leave it that way. =)

$FF00-$FF7F - Hardware I/O Registers

This area contains all the control registers for all the hardware on the GameBoy and is basically a memory-mapped I/O area. Details on this area are in another section.

$FF80-$FFFE - High RAM Area

Originally intended to be used as 127 bytes of stack space, this area is better suited for use as a Zero-Page, or a quick RAM access area, since there is an instruction that accesses the area $FF00-$FFFF quicker than a regular LD instruction. Most coders nowadays just set the stack to the TOP of internal RAM, since it works the same and frees the high RAM for quick variables and such.

$FFFF - Interrupt Enable Register

This is another I/O register, which controls the interrupts.

This webpage was designed to be viewed in any HTML 4.0/CSS1 compliant browser, but since Netscape sucks (opinions are like ass holes, everybody has got one), everything looks like sh*t in Netscape, so go get IE 5.5 or Opera 4.0, which both are great browsers (and consequently, display my webpage correctly).

DISCLAIMER: You will NOT find commercial ROMS on this website, nor will I answer e-mail in regards to where you can find ROM's on the Internet. Downloading a ROM image of a game you do not own is illegal. I will not answer e-mail regarding PokemonTM. This website, to the best of my knowledge does not contain any illegally obtained information, nor any illegal content. Not everything on this webpage was created by me. Some of the content, images, etc, have come from many places, and I thank the friends who have helped me with everything (that means you Sacher!). Should you not like my views, opinions, ideas, etc, etc, then pardon me and PISS OFF.


Programmer-Net : The home of all programming information on the net

Wayback Machine
ДЕК | ФЕВ | АПР
Previous capture | 6 | Next capture
2001 | 2003 | 2004
33 captures
24 янв 01 - 21 июл 13
sparklines
Close Help

NEWS
+ Current News

GAMEBOY
+ Emulators
 + Tutorials
 + Cribsheet
 - Opcodes and Timings
 - Memory Map
 - Cartridge Header
 - Hardware Registers
 + Downloads
 + Links

PC
+ Source Code
- C++
- ASM
+ Links

CONTACT
+ Adrian@enliten.force9.co.uk
+ ICQ 8715274

You are visitor

Since 21st May 2000

Copyright (c) 1999, 2000 Adrian Brown
If you have any complaints about this website or its contents, please email me and I will remove or update the offending section.

Gameboy Memory Map

The Gameboy has 64k of memory that is accessable at any one time, this is split up into sections such as ROM, RAM and hardware ports. Follows is a breakdown of the different sections of memory.

MEMORY ADDRESS (in Hex) | CONTENTS
FFFF - FFFF | Interrupt Enable Flag (see below)
FF80 - FFFE | Zero Page (127 bytes)
FF00 - FF7F | Hardware Registers (see Hardware section)
FEA0 - FEFF | Unusable Memory
FE00 - FE9F | Object Attribute Memory (OAM)
E000 - FDFF | Echo RAM (Reserved - Not Usable)
D000 - DFFF | Internal RAM - Banks 1 - 7 (CGB is switchable as it has 32k Internal RAM)
C000 - CFFF | Internal RAM - Bank 0 (fixed)
A000 - BFFF | Switchable 8k Game Cartridge RAM
9C00 - 9FFF | Background Display Data 2, Tile Indices/Attributes (bank switched)
9800 - 9BFF | Background Display Data 1, Tile Indices/Attributes (bank switched)
8000 - 97FF | Bank 0 and 1 Character Data (bank switched)
4000 - 7FFF | User Program Area - Bank 1 to n (switchable)
0150 - 3FFF | User Program Area - Bank 0 (fixed)
0100 - 014F | Cartridge Header (see Cart. Header section)
0000 - 00FF | Interrupt vectors and RST # vectors

The different sections of memory have different attributes. Some of the areas can only be written to while others can only be read from. The following table shows which areas have which attributes.

 | DESCRIPTION | ATTRIBUTES | MEMORY
VRAM Memory Map | Tile Map 2 | Read / Write | 9C00 - 9FFF
 | Tile Map 1 | Read / Write | 9800 - 9BFF
 | Tiles 00 - 7F (FF40, bit 4 = 0) | Read / Write | 9000 - 97FF
 | Tiles 80 - FF | Read / Write | 8800 - 8FFF
 | Tiles 00 - 7F (FF40, bit 4 = 1) | Read / Write | 8000 - 87FF
Memory Map (Read / Write) | Interrupt Enable | Read / Write | FFFF - FFFF
 | High RAM | Read / Write | FF80 - FFFE
 | I/O Registers | Read / Write | FF00 - FF7F
 | OAM RAM | Read / Write | FE00 - FE9F
 | Low RAM | Read / Write | C000 - DFFF
 | Cartridge RAM | Read / Write | A000 - BFFF
 | Video RAM | Read / Write | 8000 - 9FFF
Memory Map (Read Only) | ROM Bank #1 to n | Read Only | 4000 - 7FFF
 | ROM Bank #0 | Read Only | 0000 - 3FFF
VRAM Memory Map (Write Only) | RAM/ROM Select (MBC1) | Write Only | 6000 - 7FFF
 | RAM Bank Select | Write Only | 4000 - 5FFF
 | ROM Bank Select MSB (MBC5) | Write Only | 3000 - 3FFF
 | ROM Bank Select LSB | Write Only | 2000 - 2FFF
 | RAM Bank Enable | Write Only | 0000 - 1FFF


Игровая система GameBoy (Часть 1) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

Главная
Всё для геймера: Обзоры игр, Игровые новости, Читы, Советы и пр.
Всё о компьютерных и консольных играх.
Статьи про игры | Регистрация | Вход | PDA-вид | RSS
Приветствую Вас Гость
Меню геймера
  • Игровой Форум
  • Новости и события
  • Сборник статей
  • Видео-прохождения игр
  • Файлы для геймера
    Дополнительно:
  • Игры для всех
  • Игровые ресурсы
  • Поиск для геймера
  • Комментарии геймеров
    Сайт:
  • Информация о сайте
Разделы каталога
Консоли стационарные [21]
Информация про консоли и приставки, комплектующие: картриджи, геймпады и пр.
Портативные консоли [6]
Переносные, ручные игровые консоли, т.н. handheld’ы, а также их комплектующие
Мобильные системы [1]
Мобильные телефоны, смартфоны, КПК, наладонники, планшеты и подобные устройства
PC и железо [3]
Персональный компьютер, микросхемы, джойстики, видеокарты и прочие комплектующие
Ретрокомпьютеры [3]
Бытовые компьютеры прошлого, архитектура, комплектующие и пр.
Игровые автоматы [9]
Статьи про аркадные системы, игровые автоматы, как электрические, так и механические
Опрос геймеров
Сколько часов в сутки вы играете в игры?
[ Результаты · Архив опросов ]
Всего ответов: 12244
Главная » Статьи » Игровые системы » Портативные консоли
Игровая система GameBoy (Часть 1)
Игровая система GameBoy

Игровая система GAME BOY производится фирмой NINTENDO и относится к числу ее наиболее удачных разработок.
Несмотря на относительную простоту, GAME BOY на протяжении более 10 лет остается одной из самых популярных игровых систем. Новую волну интереса к ней вызвал выпуск приставки COLOR GAME BOY, в которой используется уже не черно-белый, а цветной дисплей.
Основным преимуществом системы GAME BOY является ее компактность. Приставку не нужно подключать к телевизору, вся информация отображается на встроенном дисплее ЖКИ.
Для питания устройства применяются четыре (а в версии POCKET GAME BOY всего две) стандартные батарейки типа АА, что позволяет играть в любимые игры дома, в транспорте, на работе.
Еще одна причина широкого распространения приставки - наличие последовательного порта, обеспечивающего соединение двух или четырех (посредством специального адаптера) игровых систем с целью совместной игры.
Для данной системы разработано множество дополнительных устройств. Так, например, портативный принтер, фотокамера, полноразмерная клавиатура и специальный картридж превратят ваш GAME BOY в персональный компьютер.
Внутри корпуса располагаются центральный процессор, являющийся упрощенной версией известного процессора Z80, звуковой процессор и видеопроцессор, формирующий изображение на экране ЖКИ размером 144x160 точек, а также встроенные ОЗУ и ПЗУ.
Картридж с программным обеспечением подключается к игровой системе через 32-контактный разъем. Внутри картриджа находится ПЗУ с программой, а иногда и ОЗУ, предназначенное для сохранения игровой ситуации.
На передней панели имеется крестовина управления и четыре отдельные кнопки. Лампа подсветки экрана отсутствует, но при необходимости ее можно приобрести отдельно.

2.1. Технические характеристики

Центральный процессор
Тип упрощенный Z80
Разрядность шины данных 8 бит
Разрядность шины адреса 16 бит
Тактовая частота 4,19 МГц

Память
ОЗУ , 8 Кб
ПЗУ 256 байт
Видеопамять 8 Кб

Видеопроцессор
Тип графики спрайтовая
Адресное пространство 8 Кб
Разрядность шины данных 8 бит
Количество спрайтов 40
Размер спрайтов 8x8 или 8x16
Разрешение 256x256
Размер экрана 160x144
Количество цветов 1 (4 градации яркости)

2.2. Архитектура и принцип работы

В этом разделе рассматриваются структура игровой системы GAME BOY и особенности написания программного обеспечения для нее. Подробно рассказывается об архитектуре центрального процессора, звукового сопроцессора и видеопроцессора, а также о назначении внутренних регистров микросхем и их использовании программами.

2.2.1. Структурная схема

Внутренняя архитектура приставки GAME BOY типична для разработок фирмы NINTENDO и не содержит сложных схемотехнических решений.
Структурная схема игровой системы GAME BOY приведена на рис, 2.1.
Основу схемы составляет центральный процессор (CPU), являющийся упрощенной версией распространенного 8-разрядного процессора Z80. От оригинала он отличается сокращенным набором команд и меньшим числом внутренних регистров.
К ядру процессора подсоединены встроенное ОЗУ микропроцессора объемом 256 байт и внутреннее ПЗУ объемом 256 байт, а также переключатель банков памяти, осуществляющий дешифрацию адреса и формирование сигналов выборки внутреннего ОЗУ и ПЗУ процессора.
Система ввода/вывода состоит из контроллера клавиатуры и контроллера последовательного интерфейса, связывающего приставки посредством соединительного кабеля.
Функционирование всех узлов процессора синхронизирует внутренний генератор тактовой часто ты и таймер, формирующий запросы на прерывание для процессора. Частота внутреннего генератора (4,19 МГц) стабилизируется кварцевым резонатором.
Встроенный контроллер прерываний обрабатывает сигналы, поступающие от видеопроцессора, таймера, контроллера последовательного интерфейса и контроллера клавиатуры, и посылает ядру процессора запрос на прерывание.
На одном кристалле с центральным процессором размещается музыкальный сопроцессор, поддерживающий четыре независимых аудиоканала. Два выходных сигнала звукового процессора поступают на вход усилителя 34 и на контакты разъема подключения головных телефонов. Усиленные сигналы звукового сопровождения смешиваются и подаются на встроенный динамик приставки.
В модуль видеопроцессора входят непосредственно схема видеопроцессора, схема интерфейса видеопамяти и контроллер прямого доступа к памяти.
В видеопамяти хранится информация, необходимая при создании изображения. Видеопроцессор получает эту информацию и формирует сигналы для схемы управления матрицей ЖКИ. Схема интерфейса видеопамяти вырабатывает все необходимые сигналы для чтения или записи в видеопамять и следит за разрешением конфликтов при одновременном обращении к видеопамяти графического и центрального процессоров.
Контроллер прямого доступа к памяти (ПДП) позволяет быстро пересылать данные из встроенного ОЗУ или картриджа в видеопамять без участия центрального процессора. Данная возможность применяется для обновления изображения во время кадрового синхроимпульса.
Следует отметить, что все рассмотренные выше блоки аппаратно реализованы в виде одной 80-выводной БИС, к которой подключены две микросхемы ОЗУ объемом по 8 Кб. Одна из этих микросхем используется как память центрального процессора, а другая - как видеопамять.
Сигналы шины данных, шины адреса и шины управления центрального процессора выведены на 32-контактный разъем, через который подсоединяется картридж с программным обеспечением. В картридже располагается ПЗУ с программой, микросхема контроллера страниц памяти и иногда дополнительное ОЗУ. Клавиатура системы представляет собой матрицу нормально разомкнутых контактов.

Ha отдельной плате помещается матрица ЖКИ, служащая экраном системы, и схемы для управления ею. На эту плату поступают сигналы, сформированные видеопроцессором, с помощью которых схемы управления создают изображение на экране.
Размер экрана системы GAME BOY составляет 160x144 точки, но видеопроцессор может работать с картинкой большего размера - 256x256 точек. Фрагмент, который будет выведен на экран, определяется программным путем. Для этого в управляющие регистры видеопроцессора записываются координаты верхнего левого угла отображаемого фрагмента.
Блок питания GAME BOY несколько сложнее обычного блока питания 8-разрядной игровой системы. Внешний сетевой адаптер формирует напряжение, подаваемое на внутренний стабилизатор приставки. Для питания матрицы ЖКИ в блоке питания кроме линейного стабилизатора предусмотрен импульсный преобразователь, вырабатывающий напряжение -18 В.

2.2.2. Центральный процессор

В игровой системе GAME BOY в качестве ядра используется упрощенный вариант широко распространенного процессора Z80 производства фирмы ZILOG, отличающийся от оригинала сокращенным набором команд и меньшим числом внутренних регистров. Тактовая частота процессора GAME BOY составляет 4,19 МГц, SUPER GAME BOY -4,29 МГц.
Микропроцессор связан с другими устройствами посредством 16-разрядной шины адреса, 8-разрядной шины данных и шины управления. Шина адреса позволяет процессору адресоваться к 64 Кб памяти (65535 байт). Регистры внешних устройств адресуются процессором так же, как и ячейки памяти, и находятся в общем адресном пространстве микропроцессора.
Структурная схема центрального процессора им ровой системы GAME BOY приведена на рис. 2.2.

Как видно из рис. 2.2, процессор содержит вычислительный блок с набором регистров общего назначения (АЛУ), внутреннее ОЗУ и внутреннее ПЗУ объемом по 256 байт, дешифратор адреса и схему выборки памяти. Дешифратор вырабатывает сигналы, которые разрешают различным модулям занимать шины процессора для обмена информацией. Внутренней памятью процессора управляет схема выбора блока памяти.

Рис. 2.2. Структурная схема центрального процессора игровой системы GAME BOY
В следующих разделах подробно рассматриваются программная архитектура процессора, внутренние регистры, способы адресации, система команд и система прерываний; указываются отличия процессора GAME BOY от оригинального Z80.
Более полную информацию по архитектуре процессора и программированию на языке ассемблера Z80 можно найти в печатных изданиях и на сайтах Internet, перечисленных в конце книги.

Регистры процессора
Центральный процессор игровой системы GAME BOY содержит восемь 8-разрядных регистров и два 16-разрядных. Все регистры имеют уникальные имена и различаются по назначению. Ниже подробно описано назначение отдельных регистров.
Аккумулятор А. Этот регистр наиболее часто используется в процессоре. С его помощью производится обмен данными с памятью и портами ввода/вывода, а также выполнение любой арифметической или логической операции.

Регистры общего назначения В, С, D, Е, Н, L.
Шесть 8-разрядных регистров процессора могут применяться для хранения промежуточных данных. Чтобы обработать 16-разрядные значения, эти регистры допустимо объединить в регистровые пары: ВС, DE, HL.

Регистр состояния процессора F.
Каждый бит данного регистра является флагом и изменяет свое значение в зависимости от результата выполненной команды.
Структура регистра состояния центрального процессора GAME BOY приведена на рис. 2.3.

Рис. 2.3. Структура регистра состояния центрального процессора игровой системы GAME BOY
В регистре состояния используются только четыре разряда из восьми. Младшие четыре разряда всегда равны 0. Назначение старших разрядов приводится ниже.
Флаг С является флагом переноса, который применяется при выполнении операций сложения и вычитания. Разряд С регистра состояния процессора устанавливается в 1, когда результат сложения больше 255 (0FFh) или если результат операции вычитания меньше 0.
Флаг N - флаг вычитания; устанавливается в 1, если последней выполненной процессором арифметической операцией была команда вычитания (SUB или SBC). Используется при работе с числами в двоично-десятичном коде.
Флаг Н - флаг вспомогательного переноса; устанавливается в 1, если при выполнении арифметической операции произошел перенос из D3 в D4. Применяется внутренними схемами процессора при работе с числами в двоично-десятичном коде.
Флаг Z - флаг нулевого результата; устанавливается в 1, если результат выполнения операции равен 0.

Указатель стека SP.
Этот 16-разрядный регистр указывает на первую свободную ячейку стека. В процессоре игровой системы GAME BOY стек может располагаться в любом месте памяти системы, хотя обычно его размещают в верхних адресах ОЗУ. Стек растет вниз, то есть от адреса, содержащегося в регистре SP, к адресу 0000h.

Счетчик команд PC.
Это еще один 16-разрядный регистр процессора. Его назначение - указывать адрес команды, которая должна быть выполнена. Команды перехода позволяют изменить содержимое данного регистра и задать необходимую последовательность выполнения команд, находящихся в памяти.

Способы адресации
Чтобы процессор работал правильно, в коде команды требуется указать местоположение необходимых для работы данных. Это делается с помощью различных методов адресации. Методы адресации для центрального процессора игровой системы GAME BOY подробно рассмотрены ниже.
Регистровая адресация. Операнды находятся в регистрах центрального процессора. Названия используемых регистров указываются непосредственно в команде.
Например:

INC А ; А = А + 1.
ADD HL, DE ; HL = HL + DE.

Неявная адресация. Расположение операндов для команд строго фиксировано, дополнительных указаний не требуется.
Например:

RLA ; Сдвиг содержимого
; аккумулятора влево.

Непосредственная адресация. 8-разрядный операнд хранится в памяти сразу за кодом команды, то есть по адресу РС+1.
Например:

LD А, 16 ; А = 16.
; Код команды ЗЕ 10.

Косвенная адресация. В команде указывается адрес операнда в памяти.
Например:

LD А, (#ЗЕ00) ; Загрузка в А значения
; из ячейки памяти
; с адресом 3E00h.

Регистровая косвенная адресация. Адрес операнда содержится в одной из регистровых пар процессора.
Например:

LD A, (DE) ; Загрузка в А значения
; из ячейки памяти,
; адрес которой
; хранится в регистровой
; паре DE.

Побитовая адресация. Указание непосредственного разряда операнда, с которым производится необходимое действие, в коде команды. Данный метод используют три команды: BIT, SET и RES.
Например:

RES 7, А ; Сброс старшего разряда
; аккумулятора.

Относительная адресация. Применяется в командах перехода. Во втором байте команды задается 8-разрядное смещение в диапазоне от -128 до 127, которое прибавляется к текущему содержимому счетчика команд для получения адреса следующей выполняемой команды.
Например:

JR $+20 ; Переход на 20 байт
; вперед.

Система команд
Центральный процессор игровой системы GAME BOY может выполнять около 120 различных команд. Они делятся на следующие функциональные группы:

* команды пересылки данных: LD, LDHL, LDD, LDI;
* команды арифметических и логических операций: ADD, ADC, SUB, SBC, DAA, INC, DEC, CP, AND, OR, XOR, СPL;
* команды работы с отдельными разрядами:
BIT, SET, RES;
* команды сдвига: RLCA, RRCA, RLA, RRA, SWAP, RLC, RL, RRC, RR, SLA, SRA, SRL;
* команды перехода: JR, JP;
* команды работы с подпрограммами: CALL, RST, RET, RETI;
* команды управления процессором: NOP, HALT, STOP, DI, EI, CCF, SCF;
* команды работы со стеком: PUSH, POP.

Полная информация о системе команд центрального процессора игровой системы GAME BOY приведена в приложении 1.

Система прерываний
Центральный процессор игровой системы GAME BOY обрабатывает несколько сигналов прерываний, которые поступают от различных устройств При этом выполнение программы приостанавливается, и вызывается программа обслуживания прерывания, расположенная по определенному адресу. Адреса, по которым передается управление, задаются аппаратно; их модификация запрещена.
Далее рассмотрены возможные типы прерываний и процедуры их обработки.
Сброс микропроцессора. Это прерывание происходит при включении питания системы, а также при поступлении сигнала низкого логического уровня по линии RESET. Центральный процессор в таком случае обнуляет регистры, подсоединяет встроенное ПЗУ системы и начинает выполнение программы с адреса 000h.
Прерывание по сигналу кадровой синхронизации. Каждый раз, когда видеопроцессор завершает формирование очередного кадра, выдается запрос на обслуживание данного прерывания, а управление передается на адрес 0040. Запрос генерируется видеопроцессором с частотой 59,7 Гц (в приставке GAME BOY) или 61,1 Гц (в приставке SUPER GAME BOY). Во время кадрового синхроимпульса видеопроцессор не обращается к видеопамяти, освобождая доступ к ней для центрального процессора, поэтому процедура обработки данного прерывания часто изменяет информацию в видеопамяти и, следовательно, изображение на экране.
Прерывание от видеопроцессора. Видеопроцессор вырабатывает запрос на обслуживание данного прерывания, если происходит одно из событий, указанных программистом. Как правило, генерацию запроса прерывания используют при формировании строки экрана с заданным номером. Программа обслуживания запроса должна начинаться с адреса 0048h. Более подробно данное прерывание будет рассмотрено при описании видеопроцессора.
Прерывание при переполнении таймера. Прерывание происходит каждый раз, когда внутренний счетчик таймера переполняется (содержимое регистра TIMА изменяется с FFh на 00h). При этом управление передается на адрес 0050h. Более детально этот запрос прерывания рассматривается при описании таймера.
Прерывание контроллера последовательного интерфейса. Данный запрос на обслуживание прерывания генерируется контроллером последовательного интерфейса каждый раз, когда завершается передача байта. Адрес начала процедуры обработки - 0058h.
Прерывание порта клавиатуры. Запрос на прерывание выдается внутренними схемами каждый раз, когда на какой-либо из входных линий порта клавиатуры появляется низкий логический уровень. Точка входа в программу, обслуживающую этот запрос, должна находиться по адресу 0060h.
Программные прерывания. Такие прерывания происходят при выполнении процессором команды рестарта из диапазона RST 00h - RST 38h: вызывается процедура, расположенная по одному из восьми фиксированных адресов. В программах названные команды удобны для вызова многократно используемых процедур, поскольку занимают меньше места и выполняются быстрее, чем стандартная команда вызова подпрограммы CALL.
Чтобы корректно работать с прерываниями, следует изучить программную архитектуру контроллера прерываний, который получает запросы от остальных блоков и формирует адрес перехода, передаваемый ядру процессора.
С точки зрения программирования обслуживание запросов на прерывание зависит от состояния внутреннего флага процессора IME и данных, записанных в регистр управления контроллера прерываний IE (FFFFh). В регистре состояния контроллера прерываний IF (FF0Fh) отображаются сигналы на линиях запроса прерывания.
Внутренний флаг процессора IME сбрасывается командой DI, а устанавливается командой EI. В регистр управления контроллером прерываний IE записывается число, разрешающее контроллеру обслуживать соответствующий запрос. В результате процедура обработки запроса на прерывание в игровой приставке GAME BOY выглядит следующим образом.

1. При поступлении запроса на прерывание от какого-либо устройства в регистре IF соответствующий разряд становится равным 1.
2, Если установлен флаг IME центрального процессора, а соответствующий разряд регистра управления контроллером прерываний IE равен 0 то обработка запроса на прерывание продолжается, в противном случае никаких действий не производится.
3. Сбрасывается флаг IME, и запрещается прием новых запросов на прерывание.
4.Состояние счетчика команд (PC) сохраняется в стеке.
5. Управление передается на стартовый адрес процедуры обслуживания соответствующего прерывания.

В начале процедуры обработки прерывания необходимо сохранить в стеке все данные регистров процессора, которые могут быть изменены. В конце процедуры информация, хранившаяся в регистрах, восстанавливается.
Возврат из процедуры обработки прерывания осуществляется командой RETI или RET, Команда RETI устанавливает флаг IME, разрешая продолжение обработки запросов на прерывание, и передает управление по адресу, сохраненному в стеке. Если вы пользуетесь командой RET, установите флаг IME с помощью команды EI.
Как отмечалось выше, во время выполнения процедуры обслуживания прерывания запросы на прерывание от других устройств игнорируются. Однако можно реализовать и вложенную обработку запросов, управляя флагом IME и регистрами контроллера прерываний внутри процедуры обработки.
Рассмотрим более подробно структуру регистров контроллера прерываний игровой системы GAME BOY.

Флаг IME
Внутренний флаг центрального процессора, который устанавливается командой EI, а сбрасывается командой DI. Процессор будет обслуживать поступившие запросы на прерывание, только если этот флаг равен 1.

Регистр состояния контроллера прерываний IF
Адрес: FF0Fh.
Тип: чтение/запись.
Назначение разрядов.
Отдельные разряды регистра устанавливаются в 1 при поступлении запроса на прерывание от соответствующего устройства — независимо от состояния флага IME и регистра IE.
D7 - D5 - не используются.
D4 - поступило прерывание от порта клавиатуры.
D3 — поступило прерывание от контроллера последовательного интерфейса.
D2 - поступило прерывание от таймера.
D1 - поступило прерывание от видеопроцессора.
D0 - поступило прерывание, по сигналу кадровой синхронизации.

Регистр управления контроллером прерываний IE
Адрес: FFFFh.
Тип: чтение/запись.
Назначение разрядов.
Отдельные разряды этого регистра разрешают контроллеру прерываний обрабатывать запросы от устройств. Если соответствующий разряд установлен в 1, то при поступлении запроса контроллер формирует сигнал прерывания для центрального процессора. Если разряд равен 0, запрос на прерывание игнорируется.
D7 - D5 - не используются.
D4 — разрешено прерывание от порта клавиатуры.
D3 - разрешено прерывание от контроллера последовательного интерфейса.
D2 - разрешено прерывание от таймера.
D1 - разрешено прерывание от видеопроцессора.
D0 - разрешено прерывание по сигналу кадровой синхронизации.

Специальные режимы работы процессора
Существует два специальных режима работы центрального процессора игровой системы GAME BOY, связанных с особенностями выполнения команд HALT и STOP. Как правило, они используются с целью уменьшения энергопотребления системы и соответствующего увеличения срока службы батарей.
Режим остановки процессора. Если выполняется команда STOP, центральный процессор и видеопроцессор останавливаются и экран выключается. Выход из этого режима осуществляется нажатием какой-либо клавиши. Выполнение программы продолжается с команды, следующей за STOP.
Данный режим удобен, если программа ожидает нажатия клавиши в течение долгого времени (более 5 мин). Благодаря переходу в режим остановки процессора заряд батарей сохраняется дольше.
Обратите внимание на две важные особенности:

1. Перед остановкой процессора необходимо выключить звуковое сопровождение, записав 0 в разряд D7 регистра NR52 (FF26h).
2. Реакция игровой системы на нажатие клавиш в режиме остановки зависит от содержимого регистра Р1 (FF00h). Возможны следующие варианты:
00h-работа продолжается при нажатии любой клавиши;
10h - работа продолжается только после нажатия клавиш А, В, SELECT или START;
20h - работа продолжается только после нажатия клавиш крестовины;
30h - приставка не реагирует на нажатие клавиш.

Режим пониженного энергопотребления. Если процессор исполняет команду HALT, его дальнейшие действия определяются состоянием флага IME. Когда флаг установлен (прерывания разрешены), работа процессора останавливается, пока не будет получен запрос на прерывание; в противном случае команда HALT игнорируется.
В аппаратном обеспечении существует ошибка, которую следует учитывать при использовании команды HALT. Когда процессор читает байт, следующий за командой HALT, приращения содержимого счетчика команд PC не происходит, поэтому такой байт считывается процессором дважды. Проиллюстрируем сказанное на примерах.
Команда, занимающая в памяти один байт и расположенная после команды HALT, исполняется два раза. После выполнения приведенного ниже фрагмента содержимое аккумулятора будет увеличено на 2:

76 HALT
ЗС INC А

Если за командой HALT встречается команда, занимающая несколько байтов, результат работы предсказать трудно.
Например, вместо фрагмента

76 HALT
FA 34 12 LD A, (1234h)

будет выполнен фрагмент

76 HALT
FA FA 34 LD A, (34FAh)
12 LD (DE) ,A

И наконец, исполнение двух расположенных друг за другом команд HALT остановит процессор.
Чтобы избежать подобных проблем, за командой HALT необходимо располагать команду NOP.
В заключение рассмотрим пример использования команды HALT для снижения энергопотребления системы. Построение основного цикла программы так, как показано ниже, увеличит срок службы батарей на 5-50%.

; Основной цикл игры. Main:
HALT ; Останов процессора
NOP ; для экономии заряда!
; батарей.

LD A,(VblkFlag) ; Произошло
; прерывание
OR A ; по кадровому
; синхроимпульсу?
JR Z,Main ; Нет, ожидание
; продолжается.
XOR A ; Да, флаг
; сбрасывается.
LD (VblkFlag),A
CALL Controls ; Проверка состояния
; клавиш.
CALL Game ; Выполнение
; необходимых
; для игры действий.
JR Main ; Процедура обработки прерывания
; по сигналу кадровой синхронизации.

Vblank:
PUSH AF ; Сохранение
; регистров.
PUSH ВС
PUSH DE
PUSH HL
CALL SpriteDMA ; Изменение
; информации
; о спрайтах.
LD A,1 ; Установка флага.
LD (VblkFlag),А
POP HL ; Восстановление
; содержимого
; регистров.
POP DE
POP ВС
POP AF
RETI

Прокомментируем приведенную программу. Остановка процессора и ожидание запроса на прерывание происходят в начале основного цикла. При поступлении запроса процессор возобновляет работу и обслуживает его. Если осуществляется процедура обработки прерывания по сигналу кадровой синхронизации, то после ее выполнения будет установлен флаг VblkFlag. В основном цикле проверяется состояние этого флага; если он был установлен, теперь сбрасывается, а потом вызываются подпрограмма проверки состояния клавиш и подпрограмма, производящая необходимые для процесса игры действия. Затем основной цикл повторяется.

Отличия от процессора Z80
В России широкое распространение получил процессор Z80, являющийся усовершенствованным вариантом процессора 18080.
Ниже приводится краткий обзор основных отличий центрального процессора игровой системы GAME BOY от оригинального процессора Z80. Эта информация поможет при переносе программного обеспечения, написанного для компьютеров с процессором Z80 (MSX, ZX SPECTRUM и т.п.), на приставку GAME BOY. Итак, в процессоре GAME BOY:

* отсутствует альтернативный набор регистров, индексные регистры IX, IY и регистры I и R;
* изменена структура регистра состояния процессора;
* отсутствуют команды, использующие индексные регистры, команды с префиксом EDh, все команды ввода/вывода (IN и OUT), все команды обмена (ЕХ и ЕХХ) и все условные переходы (JP), условные вызовы подпрограмм (CALL) и возвраты (RET), которые применяют флаг четности/переполнения и флаг знака;
* команды LDА, (xxxxh); LD (xxxxh) , А и RETI имеют другой код;
*добавлены новые команды, с которыми можно ознакомиться в приложении 1.

2.2.3. Адресное пространство
Центральный процессор игровой системы GAME BOY способен обращаться к 65536 ячейкам памяти с адресами 0000h - FFFFh. В едином адресном пространстве располагаются внутреннее ПЗУ и ОЗУ системы, регистры ввода/вывода, память видеопроцессора и память подключенного картриджа.
Распределение адресного пространства игровой системы GAME BOY показано на рис. 2.4.
Рассмотрим более подробно назначение основных областей памяти.
Адреса 0000h - 00FFh: здесь располагается область встроенного ПЗУ системы с программой начального запуска, предназначенной для считывания информации из картриджа, сравнения ее с записанным образцом и передачи управления в ПЗУ картриджа по адресу 0100h. После передачи управления внутреннее ПЗУ отключается, и при обращении по этим адресам становится доступным ПЗУ картриджа.
Стартовая программа, содержащаяся в ПЗУ системы, выполняет следующие действия:

1. Считывает данные, записанные в картридже по адресам 0104h- 0l33h, и передает их в видеопамять. В результате такой операции в верхней части экрана появляется логотип NINTENDO.
2. Выведенное изображение плавно перемещается в центр экрана и сопровождается воспроизведением двух музыкальных нот.
3. Данные, записанные по адресам 0104h - 0l33h, сравниваются с образцом, хранящимся во встроенном ПЗУ. При обнаружении различий игровая приставка прекращает работу.

Рис. 2.4. Распределение адресного пространства игровой системы GAME BOY
4.Байты, расположенные по адресам 0134h- 014Dh, последовательно суммируются, и к полученному результату прибавляется 25.
5. Приставка прекращает работу, если результат предыдущей операции отличается от 0.
б.. Управление передается по адресу 0100h, и внутреннее ПЗУ отключается.
Адреса 0000h - 3FFFh: непереключаемый банк ПЗУ картриджа. Присутствует в любом картридже независимо от модели. По этим адресам всегда будут расположены первые 16 Кб ПЗУ картриджа независимо от его объема.
Адреса 4000h - 7FFFh: переключаемый банк постоянной памяти картриджа. Микросхема контроллера страниц памяти, установленная в картридже, подсоединяет по этим адресам произвольный 16-килобайтный блок ПЗУ, номер которого задается программным путем.
Адреса 8000h - 9FFFh: память видеопроцессора. Более подробно структура данной области памяти и особенности ее использования рассматриваются в разделе, посвященном архитектуре видеопроцессора.
Адреса A000h - BFFFh: переключаемый банк ОЗУ картриджа. В некоторых картриджах оперативная память может отсутствовать.
Адреса С000h - DFFFh: встроенное ОЗУ системы, применяемое для хранения данных во время работы программы.
Адреса E000h - FDFFh: чтение или запись по этим адресам приведет к выбору встроенного ОЗУ, расположенного в диапазоне C000h - DDFFh. Таким образом, обращение к памяти по адресу Е000h аналогично обращению к памяти по адресу C000h, E001h - C001lh и т.д.
Адреса FE00h - FE9Fh: память спрайтов. Подробно рассматривается при описании видеопроцессора.
Адреса FEA0 - FEFFh: не используются.
Адреса FF00h - FF4Ch: порты ввода/вывода.
Адреса FF4Dh - FF7Fh: не используются.
Адреса FF80h - FFFEh: внутреннее ОЗУ центрального процессора. Обычно применяется для хранения стека.
Адрес FFFFh: регистр управления контроллером прерываний.

Дадее >>
Категория: Портативные консоли | Добавил: G-GURU (04.11.2010)
Просмотров: 3324 | Рейтинг: 5.0/1 |
Вы можете отправить сообщение на e-mail друга:
Интересное геймерам: Rainbow Saga

Смотрите другие материалы по темам: Структурная схема, Gameboy, Технические характеристики, Центральный процессор, Игровая система.

Также вам предлагаются схожие с «Игровая система GameBoy (Часть 1)» материалы:

  • Игровая приставка Dendy (Часть 1)
  • Игровая система GameBoy (Часть 2)
  • Игровая система GameBoy (Часть 3)
  • Игровая система GameBoy (Часть 4)
  • Игровая приставка Sega MegaDrive (Часть 1)
  • Игровая приставка Sony PlayStation (Часть 1)
  • Деревянная игровая система

Если понравился материал «Игровая система GameBoy (Часть 1)» и вы можете обсудить его ниже.

Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск информации
Игровые братья
Создание игр, Конструкторы игр, Игровые движки Перевод консольных игр Скачать файлы: софт, видео Игорный бизнес в России Разработка игр в СНГ Пранк - телефонные шутки
10 новых описаний
Vagrant Story [PS1]
Tomb Raider: The Prophecy [GBA]
"Война Грибов: В Космос!" - Стратегия №1 возвращается!
Астролорды: Облако Оорта
Пушистый котёнок в вашем кармане
Skyrim Jumping
Shards of War
Dendy MegaBoy
Bliss Charger BC-46: возможность зарядки до 4 устройств одновременно!
400 игр для Dendy от CoolBoy
Все права сохранены за сайтом GFAQ.ru © 2014. Games Frequently Asked Questions
Полные прохождения, Секреты, Коды, Пароли, Советы, Уловки, Читы, Описания для Компьютерных и Консольных Игр. Хостинг от uCoz
Обращаем особое внимание на то, что при цитировании и размещении данных текстовых материалов, обязательно прикрепляйте ссылку на официальный сайт "GFAQ.ru".


Игровая система GameBoy (Часть 2) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

Главная
Всё для геймера: Обзоры игр, Игровые новости, Читы, Советы и пр.
Всё о компьютерных и консольных играх.
Статьи про игры | Регистрация | Вход | PDA-вид | RSS
Приветствую Вас Гость
Меню геймера
  • Игровой Форум
  • Новости и события
  • Сборник статей
  • Видео-прохождения игр
  • Файлы для геймера
    Дополнительно:
  • Игры для всех
  • Игровые ресурсы
  • Поиск для геймера
  • Комментарии геймеров
    Сайт:
  • Информация о сайте
Разделы каталога
Консоли стационарные [21]
Информация про консоли и приставки, комплектующие: картриджи, геймпады и пр.
Портативные консоли [6]
Переносные, ручные игровые консоли, т.н. handheld’ы, а также их комплектующие
Мобильные системы [1]
Мобильные телефоны, смартфоны, КПК, наладонники, планшеты и подобные устройства
PC и железо [3]
Персональный компьютер, микросхемы, джойстики, видеокарты и прочие комплектующие
Ретрокомпьютеры [3]
Бытовые компьютеры прошлого, архитектура, комплектующие и пр.
Игровые автоматы [9]
Статьи про аркадные системы, игровые автоматы, как электрические, так и механические
Опрос геймеров
Сколько часов в сутки вы играете в игры?
[ Результаты · Архив опросов ]
Всего ответов: 12244
Главная » Статьи » Игровые системы » Портативные консоли
Игровая система GameBoy (Часть 2)
2.2.4. Видеопроцессор

Видеопроцессор игровой системы GAME BOY вырабатывает управляющие сигналы для схем, которые формируют изображение на экране ЖКИ. Он располагается на одном кристалле с центральным процессором и соединен с микросхемой видеопамяти объемом 8 Кб отдельными шинами данных, адреса и управления. Видеопамять входит в адресное пространство центрального процессора и доступна для записи и чтения обычным образом.
Видеопроцессор игровой приставки ориентирован на блочную графику с использованием спрайтов. Изображение строится из заранее заданных , блоков размером 8x8 точек. Разрешение выводимой на экран картинки составляет 160x144 точки (20x18 символов). Однако общий размер хранящегося в видеопамяти рисунка составляет 256x2561 точек. Какой именно фрагмент видеопамяти будет отображен на экране, определяется программным путем.
Распределение видеопамяти и процесс формирования изображения на экране приставки GAME] BOY более подробно описываются ниже.

Карта видеопамяти
Видеопамять системы GAME BOY имеет размер 8 Кб и располагается в адресном пространстве центрального процессора в диапазоне 8000h - 9FFFh. Эту область памяти занимают два частично перекрывающихся знакогенератора и две экранные страницы. Здесь также хранятся битовые образы выводимых символов (под символом подразумевается участок изображения размером 8x8 точек).
Первый знакогенератор размещается по адресам 8000h - 8FFFh, второй - по адресам 8800h - 97FFh. В них содержатся символы для изображения фона и образы спрайтов. Назначение знакогенератора определяется программным путем при установке соответствующего флага в регистре управления видеопроцессором.

Распределение адресного пространства видеопамяти игровой системы GAME BOY показано на рис. 2.5.
Как видно из приведенной карты памяти, две страницы частично перекрываются, следовательно, часть символов будет одинаковой для обоих знакогенераторов. В диапазоне 8000h - 87FFh хранятся 128 битовых образов первого знакогенератора для его символов с кодами 00h - 7Fh, в диапазоне 9000h - 97FFh расположены образы символов с кодами 00h - 7Fh второго знакогенератора. Область памяти с адреса 8800h по адрес 8FFFh используется знакогенераторами совместно, поэтому образы символов с кодами 80h - FFh, содержащиеся здесь, будут одинаковыми для обоих знакогенераторов.
Любой символ занимает в памяти знакогенератора 16 байт. Видеопроцессор совмещает два битовых образа каждой строки и получает изображение с четырьмя градациями яркости.
Процесс получения изображения поясняется на рис. 2.6.
Первым этапом, необходимым для вывода изображения на экран игровой системы, является создание одного или нескольких знакогенераторов, на основе которых будет формироваться фоновое изображение и спрайты. Знакогенераторы записываются в ПЗУ картриджа и во время работы программы по мере необходимости копируются в видеопамять.
Экранные страницы игровой системы GAME BOY начинаются с адресов 9800h и 9C00h и располагаются в видеопамяти по адресам 9800h - 9FFFh.
Структура экранной страницы игровой системы GAME BOY приведена на рис. 2.7.
Как уже отмечалось, размер экранной страницы составляет 32x32 символа. Однако на экране отображается только фрагмент картинки - 20x18 символов. Местоположение выводимого фрагмента задается путем записи координат его верхнего левого угла в регистры видеопроцессора SCY (FF42h) и SCX (FF43h).
Одна экранная страница служит для хранения фонового рисунка, другая содержит изображение, называемое окном. Окно обычно используется для показа информационной панели состояния игры внизу экрана. Оно выводится поверх фона, а его местоположение задается путем записи координат верхнего левого угла в регистры видеопроцессора WY (FF4Ah) И WX (FF4Bh).

Рис. 2.5. Распределение адресного пространства видеопроцессора игровой системы GAME BOY

Рис. 2.7. Структура экранной страницы игровой системы GAME BOY

Рис. 2.6. Формирование цветного изображения в видеопроцессоре игровой системы GAME BOY

Возможности видеопроцессора
В этом разделе рассматриваются встроенные графические функции видеопроцессора GAME BOY, способы формирования изображения и особенности создания программного обеспечения.
Видеопроцессор игровой системы GAME BOY предназначен для работы с блочной графикой, основанной на спрайтах. Термин «блочная графика» означает, что изображение на экране формируется из заранее нарисованных блоков.
Видеопроцессор способен использовать 40 независимых друг от друга спрайтов размером 8x8 или 8x16 точек. Размер всех отображаемых на экране спрайтов одинаков и задается программным путем. С помощью манипуляций с регистрами видеопроцессора допустимо плавно перемещать картинку и реализовывать нестандартные графические режимы, которые значительно расширяют возможности игровой системы.
Спрайты. Изображения спрайтов хранятся в одном из знакогенераторов, определяемом программным путем. Изображения спрайтов размером 8x16 символов занимают два последовательно расположенных символьных образа, при этом номер первого символа всегда четный. В связи с использованием спрайтов видеопроцессор системы налагает одно важное ограничение: на одной строке экрана может находиться не более десяти спрайтов одновременно.
Память спрайтов. Память спрайтов игровой системы GAME BOY представляет собой отдельную область в общем адресном пространстве центрального процессора. Этот блок памяти находится в диапазоне адресов FE00h - FE9Fh и содержит всю необходимую информацию о расположении каждого спрайта на экране и режиме его отображения. На каждый из 40 спрайтов в памяти отведено по четыре байта. В этих байтах, называемых описанием спрайта, имеется следующая информация:

1. Координата верхнего левого угла спрайта по вертикали. Если это значение равно 0 или больше 160 (144 + 16), то спрайт на экране не отображается.
2. Координата верхнего левого угла спрайта по горизонтали. Если это значение равно 0 или больше 168 (160 + 8), то спрайт на экране не отображается.
3. Номер символа знакогенератора, хранящего изображение спрайта. При использовании спрайтов размером 8x16 точек номера используемых символов могут быть только четными. Если же в таблице спрайтов указан нечетный символ (например, 3), он будет уменьшен на 1 (то есть станет символом 2).
4.Атрибуты спрайта (применяются старшие четыре разряда):
D7 - приоритет спрайта:
0 - спрайт выводится поверх изображения фона и окна;
1 - спрайт скрыт за изображением фона и окна. Он виден только в том случае, если скрывающие его части фона и окна имеют цвет с кодом 0, который интерпретируется видеопроцессором как прозрачный.
D6 - отражение спрайта относительно вертикальной оси:
0 - выводится обычное изображение;
1 - выводится зеркальное отражение.
D5 - отражение спрайта относительно горизонтальной оси:
0 - выводится обычное изображение;
1 - выводится зеркальное отражение.
D4 - номер палитры спрайта (OBJ0PAL или OBJ1PAL):
0 - палитра OBJ0PAL;
1 - палитра OBJ1PAL.

Размещая спрайт, важно помнить, что его положение на экране не совпадает со значениями, хранящимися в первом и втором байтах описания.
Координаты спрайта на экране можно вычислить по приведенным формулам:

Экранная координата у = координата у (байт 1) - 16;
Экранная координата х = координата х (байт 2) - 8.

Следовательно, чтобы спрайт находился в левом верхнем углу экрана и был полностью видим, его координаты нужно установить таким образом:

у = 16; х = 8.

Информацию в память спрайтов разрешается записать непосредственным обращением по требуемому адресу или при помощи контроллера ПДП.
На экране спрайты всегда отображаются в соответствии со своим приоритетом. Если вывести в одно и то же место экрана два спрайта, то верхним окажется спрайт с меньшим номером. Номер спрайта определяется положением его описания в памяти спрайтов. Таким образом, информация о спрайте О занимает адреса FE00h - FE03h, а информация о спрайте 1 - адреса FE04h - FE07h и т.д.
При написании программы важно учитывать особенность игровой системы GAME BOY: из-за аппаратной ошибки, допущенной при проектировании видеопроцессора, нельзя выполнять команды LDI (HL) , A; LDD (HL) , A; LDI А, (HL) и LDDA, (HL), если в регистровой паре HL содержится число из диапазона FE00h - FEFFh, поскольку это приводит к изменению содержимого памяти спрайтов.

Формирование изображения на экране
Видеопроцессор во время работы совмещает фоновое изображение, спрайты и окно, после чего выводит полученный результат на экран ЖКИ игровой системы.
Перед началом формирования изображения в памяти должны находиться два знакогенератора, изображения фона и окна и информация о спрайтах. Следует также помнить, что цвет с двумя младшими битами 00 всегда определяется как прозрачный и не влияет на окраску картинки.
Сначала показываются спрайты, у которых бит приоритета (D7) в четвертом байте описания равен 1. Они закрываются изображениями фона и окна. Приоритет соответствует нумерации, так что спрайты с меньшим номером при совпадении координат будут выводиться поверх спрайтов с большим номером.
Затем отображается фрагмент экранной страницы с фоновым рисунком размером 160x144 точки. Координаты левого верхнего угла показанного фрагмента записываются в регистры SCX и SCY видеопроцессора. При изменении содержимого регистров SCX и SCY фон плавно перемещается.
Следующим шагом в формировании картинки является вывод окна, хранящегося во второй экранной странице. Позиция на экране, начиная с которой будет располагаться его изображение, зависит от значений, записанных в регистры WX и WY. Помните, что в регистр WX необходимо помещать значение на 7 меньше, чем координата положения окна на экране. Отметим также, что при передвижении фона окно остается на прежнем месте. Для его перемещения необходимо изменить содержимое регистров WX и WY.
Последней операцией является вывод спрайтов с установленным в 0 битом приоритета - поверх изображения фона и окна. Приоритет этих спрайтов также определяется по номерам.
Процесс совмещения экранной страницы, выводимого фрагмента и окна представлен на рис. 2.8.

Режимы работы видеопроцессора
В любой момент времени видеопроцессор игровой системы GAME BOY находится в одном из четырех режимов, обозначенных числами от 0 до 3. Чтобы узнать, в каком режиме работает видеопроцессор, необходимо считать содержимое регистра STAT (FF41h) и проверить два младших разряда. Режим видеопроцессора влияет на доступность видеопамяти для центрального процессора.
Режим 0. В этом режиме генерируется строчный синхроимпульс, длительность которого зависит от числа спрайтов в формируемой строке экрана. Минимальная длительность синхроимпульса при 10 спрайтах в строке составляет 18,72 мкс; максимальная длительность, когда в строке нет ни одного спрайта, равна 48,64 мкс.
Режим 1. Когда видеопроцессор находится в таком режиме, вырабатывается кадровый синхроимпульс. Центральный процессор может свободно

Рис. 2.8. Формирование изображения на экране игровой системы GAME BOY
обращаться к видеопамяти и к памяти спрайтов. Длительность кадрового синхроимпульса в игровой приставке GAME BOY составляет 1,09 мс.
Режим 2. Видеопроцессор обращается к памяти спрайтов. Доступ к памяти спрайтов для центрального процессора в течение этого периода запрещен. Длительность работы процессора в данном режиме равна 19,31 мкс.
Режим 3. В режиме 3 происходит чтение данных из видеопамяти. Доступ к видеопамяти для центрального процессора запрещен. Продолжительность функционирования видеопроцессора в таком режиме зависит от числа спрайтов в текущей строке и находится в пределах 41,37-70,69 мкс.
Работу видеопроцессора игровой системы GAME BOY поясняет временная диаграмма, приведенная на рис. 2.9.
Формирование строки изображения начинается с обращения видеопроцессора к памяти спрайтов (режим 2), занимающего 80 тактов работы процессора. Затем видеопроцессор переходит в режим 3 и считывает данные из видеопамяти. После получения строки изображения он генерирует строчный синхроимпульс. Таким образом, видеопроцессор последовательно переключается между режимами 0, 2 и 3.

Рис. 2.9. Временная диаграмма работы видеопроцессора игровой системы GAME BOY

Вывод полной строки изображения в игровой системе GAME BOY происходит за 109 мкс (456 тактов процессора). После завершающей строки видеопроцессор переключается в режим 1 и генерирует кадровый синхроимпульс. В этом режиме видеопроцессор находится 1,09 мс (4560 тактов процессора), после чего начинает формирование нового кадра.
Полный цикл отображения кадра в системе GAME BOY происходит за 70224 такта центрального процессора.

Контроллер прямого доступа к памяти
Контроллер прямого доступа к памяти (ПДП) игровой системы GAME BOY осуществляет быструю передачу блока информации из ПЗУ или ОЗУ в память спрайтов. Наиболее часто эта функция требуется во время кадрового синхроимпульса для обновления информации о положении спрайтов на экране. Процесс пересылки данных занимает 160 мкс.
Чтобы включить режим прямого доступа к памяти, необходимо записать старший байт адреса начала пересылаемого блока в регистр DMA (FF46h); младший байт всегда равен 0.
При использовании ПДП следует помнить о двух особенностях аппаратуры игровой системы GAME BOY.
Во-первых, память спрайтов недоступна во время формирования видеопроцессором изображения на экране, поэтому пересылку данных посредством прямого доступа к памяти нужно производить, когда видеопроцессор генерирует кадровый синхроимпульс. Как правило, вызов процедуры, работающей с контроллером ПДП, осуществляют во время обслуживания соответствующего запроса на прерывание.
Во-вторых, при работе контроллера ПДП центральный процессор системы может задействовать только свою внутреннюю память, расположенную по адресам FF80h - FFFEh. Поэтому программа должна переместить фрагмент, взаимодействующий с контроллером ПДП к памяти, в верхнюю область ОЗУ, находящуюся в диапазоне адресов FF00h -FFFCH.
Рассмотрим пример программы, использующей прямой доступ к памяти для изменения информации о спрайтах.

; Сюда будет передано управление
; при поступлении запроса на обработку
; прерывания по кадровому синхроимпульсу.
ORG $0040
JP $FF80 ; С этого места начинается
; выполнение программы.
ORG $0100 NOP
JP Start
; Здесь располагается служебная
; информация о картридже.

Start:
DI
LD HL, $FF80 ; Код процедуры
LD DE, Vblk ; обработки
; прерывания
; перемещается
LD В,20h ; из ПЗУ картриджа
; во внутреннюю
Loop: LD A, (DE) ; память процессора,
LDI (HL), А ; начиная с адреса
; FF80h.
INC DE DEC В
JR NZ, Loop EI
; Здесь располагается остальной код игры.
; Процедура обработки запроса ; на прерывание.

Vblk: PUSH AF ; Сохранение
; регистров.
LD A, (SPR_TAB) ; Запись адреса ; начала
LD ($FF46), A ; пересылаемого ; блока
; в регистр DMA.
LD A, 28h Wait: DEC A ; Ожидание
; в течение 160 мкс,
JR NZ,Wait ; пока производится
; пересылка.
POP AF ; Восстановление
; регистров.
RETI ; Завершение
; обработки
; прерывания.

Итак, с точки зрения программирования контроллер прямого доступа к памяти игровой системы GAME BOY представляет собой один регистр.

Регистры видеопроцессора
Управление работой видеопроцессора осуществляется через 11 портов ввода/вывода посредством записи туда значений, определяющих требуемые параметры изображения и режимы работы. Информация о текущем режиме считывается из регистра состояния.
В адресном пространстве игровой системы GAME BOY регистры видеопроцессора располагаются в диапазоне FF40h - FF4Bh.
Регистр управления видеопроцессором LCDC
Адрес: FFA0h.
Тип: запись/чтение.
Назначение разрядов:
D7 - разрешение работы видеопроцессора:
0 - видеопроцессор выключен, изображение на экране отсутствует. Для обеспечения корректной работы выключение видеопроцессора (перевод разряда из 1 в 0) должно осуществляться в течение периода, пока длится кадровый синхроимпульс;
1 - видеопроцессор включен.
D6 - выбор экранной страницы, хранящей изображение окна:
0 - 9800h - 9BFFh;
1 - 9C00h - 9FFFh.
D5 - разрешение вывода окна на экран:
0 - не выводится;
1 - выводится.
D4 - выбор знакогенератора для хранения элементов фона и окна:
0 - знакогенератор расположен по адресам 8800h - 97FFh;
1 - знакогенератор расположен по адресам 8000h - 8FFFh (здесь же содержатся изображения спрайтов).
D3 - выбор экранной страницы для хранения фона:
0 - 9800h - 9BFFh;
1 - 9C00h- 9FFFh.
D2 - задание размера спрайтов:
0 - 8x8;
1 - 8x16.
D1 - разрешение вывода спрайтов на экран:
0 - не выводятся;
1 - выводятся.
D0 - разрешение вывода фона и окна на экран:
0 - не выводятся;
1 - выводятся.

Регистр состояния видеопроцессора STAT
Адрес: FF41h.
Тип: чтение/запись.
Назначение разрядов:
D7 - не используется.
Разряды D6 - D3 доступны для записи и задают события, при наступлении которых видеопроцессор генерирует запрос на прерывание. Запрос на прерывание от видеопроцессора выдается только в том случае, если соответствующий разряд установлен в 1.
D6 - видеопроцессор формирует строку, номер которой указан в регистре LYC (FF45h).
D5 - видеопроцессор перешел в режим 3.
D4 - видеопроцессор перешел в режим 2.
D3 - видеопроцессор перешел в режим 0.
Оставшиеся разряды регистра доступны для чтения и передают информацию о текущем состоянии видеопроцессора.
D2 - совпадение номера строки:
0 - номер формируемой видеопроцессором строки не равен номеру в регистре LYC;
1 - номер формируемой строки равен номеру в регистре LYC.
D1 - D0 - режим работы видеопроцессора:
00 - режим 0, формирование строчного синхроимпульса;
01 - режим 1, формирование кадрового синхроимпульса;
10 - режим 2, чтение из памяти спрайтов;
11 - режим 3, чтение из видеопамяти.

Регистр вертикальной координаты экрана SCY
Адрес: FF42h.
Тип: запись/чтение.
Назначение разрядов.
Записываемое в регистр 8-разрядное значение задает вертикальную координату верхнего левого угла выводимого фрагмента экранной страницы. Изменяя его, можно плавно прокручивать фоновое изображение на экране.
Использование регистра поясняется на рис. 2.8.

Регистр горизонтальной координаты экрана SCX
Адрес: FF43h.
Тип: запись/чтение.
Назначение разрядов.
Записываемое в регистр 8-разрядное значение задает горизонтальную координату верхнего левого угла выводимого фрагмента экранной страницы. Изменяя его, можно плавно прокручивать фоновое изображение на экране.
Использование регистра представлено на рис. 2.8.

Регистр номера формируемой строки LY
Адрес: FF44h.
Тип: чтение.
Назначение разрядов.
В регистре находится информация о номере строки экрана, формируемой в данный момент видеопроцессором. Значения 0-143 соответствуют номерам строк экрана, начиная с верхней. Во время генерации кадрового синхроимпульса значение, хранящееся здесь, изменяется от 144 до 153.

Регистр номера строки LYC
Адрес: FF45h.
Тип: чтение/запись.
Назначение разрядов.
В регистр записывается произвольное 8-разрядное значение. Когда номер формируемой строки, находящийся в регистре LY, совпадает с значением, заданным в регистре LYC, разряд D2 регистра состояния видеопроцессора STAT становится равен 1.
Если при этом разряд D6 регистра STAT также установлен в 1, видеопроцессор генерирует запрос на прерывание.

Регистр контроллера прямого доступа к памяти DMA
Адрес: FF46h.
Тип: запись.
Назначение разрядов.
В регистр помещается 8-разрядное значение, определяющее адрес страницы в основной памяти. Контроллер ПДП скопирует содержимое выбранной страницы основной памяти в память спрайтов. Например, после записи в регистр FF46h значения 13h в память спрайтов видеопроцессора переписывается содержимое основной памяти игровой системы, расположенное по адресам 1300h - 139Fh.

Регистр палитры фона BGP
Адрес: FF47h.
Тип: запись/чтение.
Назначение разрядов.
Когда видеопроцессор совмещает два битовых образа строки символа из знакогенератора, каждая точка на экране кодируется комбинацией из двух битов: 00, 01, 10 или 11. Однако это значение является не кодом выводимого цвета, а индексом определенной палитры. Из регистра палитры выбираются два бита, соответствующие коду точки, которые и являются цветом, отображаемым на экране. Содержимое регистра влияет на изображение фона и окна.
D7, D6 - цвет точки с кодом 11;
D5, D4 - цвет точки с кодом 10;
D3, D2 - цвет точки с кодом 01;
D1, D0 - цвет точки с кодом 00.

Регистр палитры спрайтов 1 ОВР0
Адрес: FF48h.
Тип: чтение/запись.
Назначение разрядов.
Этот регистр используется так же, как и регистр BGP, только его содержимое влияет на цвет спрайтов, у которых разряд D4 в байте атрибутов равен 0.

Регистр палитры спрайтов 2 ОВР1
Адрес: FF49h.
Тип: чтение/запись.
Назначение разрядов.
Регистр применяется так же, как и регистр BGP, только его содержимое влияет на цвет спрайтов, у которых разряд D4 в байте атрибутов равен 1.

Регистр вертикальной координаты окна WY
Адрес: FF4Ah. Тип: чтение/запись.
Назначение разрядов.
В регистр записывается номер строки экрана, начиная с которой видеопроцессор отображает окно. Если значение превышает 143, окно выводиться не будет.
Использование регистра поясняется на рис. 2.8.

Регистр горизонтальной координаты окна WX
Адрес: FF4Bh.
Тип: чтение/запись.
Назначение разрядов.
В регистр записывается номер точки в строке экрана, начиная с которой видеопроцессор отображает окно. Если значение превышает 166, окно выводиться не будет. Содержимое регистра должно быть на 7 больше, чем реальная координата границы окна на экране.
Применение регистра показано на рис. 2.8.

2.2.5. Звуковой процессор

Аудиопроцессор игровой системы GAME BOY формирует звуковое сопровождение, освобождая тем самым время центрального процессора для других задач.
В этом разделе приводится краткое описание архитектуры звукового процессора и его регистров. Методы воспроизведения музыки и спецэффектт с его помощью представляют собой отдельную область и здесь рассматриваться не будут. Информацию по данной теме можно найти на сайтах Internet.
Структурная схема звукового процессора игровой системы GAME BOY показана на рис. 2.10.
Как видно из структурной схемы, аудиопроцессор представляет собой четыре независимых генератора сигналов, выходы которых соединены через аналоговые коммутаторы. Выходные сигналы коммутаторов сгруппированы в две шины: правого и левого каналов. Далее два сигнала поступают на электронные регуляторы громкости, а затем с выходов звукового процессора передаются на внешний усилитель.
Отдельный блок, состоящий из регистров NR50 -NR52, предназначен для управления внутренними цепями звукового процессора. Путем записи определенных значений в эти регистры осуществляется включение и выключение генераторов, коммутация выходных сигналов и регулировка громкости.
Помимо общих регистров звукового процессора в каждом генераторе есть собственные регистры управления. Понять их назначение поможет структурная схема, приведенная на рис. 2.11. В качестве примера рассматривается структурная схема второго звукового генератора. Схемы других генераторов несколько отличаются, но принцип работы остается прежним.
Основой генератора является программируемый счетчик-делитель частоты. На его вход поступает опорная частота звукового процессора, деленная на четыре. С выхода программируемого делителя снимается сигнал, частота которого (fd) определяется значением, записанным в регистры NR23, NR24. Затем выходной сигнал через схему регулировки длительности импульсов поступает на вход цифро-аналогового преобразователя (ЦАП), формирующего выходной сигнал генератора.
На другие входы ЦАП приходят сигналы от счетчика огибающей, которые определяют амплитуду выходного сигнала. Счетчик огибающей соединен с регистром NR22, который позволяет программировать форму огибающей выходного сигнала звукового генератора.
Генерация звукового сигнала начинается после установки соответствующего разряда в регистре NR24. После окончания формирования сигнала устанавливается внутренний триггер генератора, что приводит к прекращению работы программируемого счетчика-делителя частоты.
Разряды регистра регулировки NR21, управляющие длительностью звучания, соединены со схемой регулировки длительности импульсов, которая контролирует поступление сигнала на вход ЦАП. По истечении заданного периода времени счетчик обнуляется и формирование звука прекращается.

Регистры звукового процессора
С точки зрения программирования звуковой процессор игровой системы GAME BOY представляет собой 21 регистр ввода/вывода, расположенный поадресам FF10h - FF26h, и область данных, определяющих форму выходного сигнала, которая находится в диапазоне FF30h - FF3Fh.
Ниже приводится подробное описание регистров аудиопроцессора. Все регистры имеют однотипное обозначение, состоящее из двух букв (NR) и двух цифр. Первая цифра - номер звукового генератора, к которому относится данный регистр. Если это цифра 5, то регистр является управляющим для всего звукового процессора. Вторая цифра - порядковый номер регистра. Таким образом, регистр NR22 -это регистр 2 второго звукового генератора.
Сведения обо всех регистрах аудиопроцессора игровой системы GAME BOY и назначении отдельных разрядов каждого регистра приведены ниже.

Регистр управления изменением частоты канала 1 (NR10)
Адрес: FF10h.
Тип: чтение/запись.
Назначение разрядов:
D7 - не используется.
D6 - D4 - служат для хранения кода, определяющего время, через которое происходит модификация частоты:
000 - частота остается постоянной;
001 - 111 - частота изменяется через (7,8 х Т) мс, где Т - десятичное значение, которое соответствует двоичному коду, записанному в данных разрядах.
D3 - задание направления изменения частоты:
0 — увеличение;
1 - уменьшение.

РИС. 2.11. СТРУКТУРНАЯ СХЕМА ОТДЕЛЬНОГО КАНАЛА ЗВУКОВОГО ПРОЦЕССОРА ИГРОВОЙ СИСТЕМЫ GAME BOY

D2 - D0 - регулировка скорости изменения частоты n = 0-7.
Новая частота рассчитывается по формуле:

Частота f0 (начальное значение частоты сигнала) задается содержимым регистров NR13, NR14.

Регистр регулировки сигнала канала 1 (NR11)
Адрес: FF11h.
Тип: чтение/запись.
Назначение разрядов:
D7, D6 - код, задающий длительность импульсов.
Зависимость длительности импульсов от кода, записываемого в регистр, поясняется на рис. 2.12.
D5 - D0 - код, указывающий длительность звучания.
Длительность звучания определяет время работы генератора аудиосигнала. Время (в секундах) рассчитывается по следующей формуле:
t = (64 - t1) х (1 / 256),
где t1 - десятичное значение кода, записанного в данных разрядах.

Регистр управления огибающей канала 1 (NR12)
Адрес: FF12h.
Тип: чтение/запись.
Назначение разрядов:
D7 - D4 - начальное значение огибающей: 0000 - минимум (звук отсутствует); 1111 - максимум.

РИС. 2.12. ЗАВИСИМОСТЬ ДЛИТЕЛЬНОСТИ ИМПУЛЬСОВ ОТ КОДА, ЗАПИСЫВАЕМОГО В РЕГИСТР
D3 - направление изменения огибающей:
0 - затухание;
1 - нарастание.
D2 - D0 - время изменения огибающей, N: N = 0 - огибающая не используется; N = 1 -7 - огибающая изменяется через N/64 с.

Регистр управления частотой канала 1 (NR13)
Адрес: FF13h.
Тип: чтение/запись.
Назначение разрядов:
В регистр записываются младшие восемь разрядов 11-разрядного значения кода начальной частоты сигнала аудиоканала 1.

Регистр управления канала 1 (NR14)
Адрес: FF14h.
Тип: чтение/запись.
Назначение разрядов:
D7 - при записи 1 в этот разряд происходит перезапуск звукового генератора канала 1 с начальными значениями параметров сигнала.
D6 - циклическая/последовательная работа:
0 - звук генерируется постоянно, независимо от значения в разрядах D5 - D0 регистра NR11;
1 - звук генерируется в течение времени, которое определяется значением, записанным в разрядах D5 - D0 регистра NR11, после чего канал выключается (сбрасывается разряд D0 в регистре NR52).
D5 - D3 - не используются.
D2 - D0 - три старших разряда 11-разрядного значения кода начальной частоты сигнала аудиоканала 1. Частота (в герцах) рассчитывается по следующей формуле:

F = 131072 / (2048 - f),

где f - десятичное число, эквивалентное 11-разрядному коду из регистров NR13 и NR14.

Регистр регулировки сигнала канала 2 (NR21)
Адрес: FF16h.
Tип: чтение/запись.
Назначение разрядов:
D7, D6 - код длительности импульсов. Понятие длительности импульсов поясняется на рис. 2.12.
D5 - D0 - код, задающий длительность звучания.
Длительность звучания определяет время работы генератора звукового сигнала. Время (в секундах) рассчитывается по следующей формуле:

t= (64 - t1) x (1/256),

где t1 - десятичное значение кода, записанного в данных разрядах.

Регистр управления огибающей канала 2 (NR22)
Адрес: FF17h.
Тип: чтение/запись.
Назначение разрядов:
D7 - D4 - начальное значение огибающей:
0000 - минимум;
1111 - максимум.
D3 - направление изменения огибающей:
0 - затухание;
1 - нарастание.
D2 - D0 - код времени изменения огибающей, N:
N = 0 - огибающая не используется;
N = 1-7 — огибающая изменяется через N/64 с.

Регистр управления частотой канала 2 (NR23)
Адрес: FF18h.
Тип: чтение/запись.
Назначение разрядов.
В регистр записываются младшие восемь разрядов 11-разрядного значения начальной частоты сигнала аудиоканала 2.

Регистр управления канала 2 (NR24)
Адрес: FF19h.
Тип: чтение/запись.
Назначение разрядов:
D7 - при записи в этот разряд 1 происходит перезапуск звукового генератора канала 2 с начальными значениями параметров сигнала.
D6 — циклическая/последовательная работа:
0 - звук генерируется постоянно, независимо от значения в разрядах D5 - D0 регистра NR21;
1 - звук генерируется в течение времени, которое определяется значением, записанным в разрядах D5 - D0 регистра NR21, после чего канал выключается (сбрасывается разряд D1 в регистре NR52).
D5 - D3 - не используются.
D2 - D0 - три старших разряда 11-разрядного значения начальной частоты сигнала звукового канала 1. Частота (в герцах) рассчитывается по следующей формуле:

F - 131072 / (2048 - f),

где f - десятичное число, эквивалентное 11 -разрядному коду из регистров NR23 и NR24.

Регистр выключения канала 3 (NR30)
Адрес: FF1Ah.
Тип: чтение/запись.
Назначение разрядов:
D7 - работа генератора канала 3:
0 - генератор выключен;
1 - генератор работает. D6 - D0 - не используются.

Регистр длительности звучания канала 3 (NR31)
Адрес: FF1Bh. Тип: чтение/запись. Назначение разрядов.
В этот регистр записывается 8-разрядное число, определяющее время работы t1 генератора канала 3. Время (в секундах) рассчитывается по формуле:

t = (256 - t1) / 2,

где t1 - десятичное число, эквивалентное коду, который записан в регистре.

Регистр управления громкостью канала 3 (NR32)
Адрес: FF1Ch.
Тип: чтение/запись.
Назначение разрядов:
D7 - не используется.
D6, D5 - громкость канала 3:
00 - звук не выводится;
01 - громкость определяется значением, считанным из ОЗУ паттерна. ОЗУ паттерна
расположено по адресам FF30h - FF3Fh и описано далее в этом разделе;
10 - громкость определяется значением, считанным из ОЗУ паттерна и разделенным на 2;
11 - громкость определяется значением, считанным из ОЗУ паттерна и разделенным на 4.

Регистр управления частотой канала 3 (NR33)
Адрес: FF1Dh.
Tип: чтение/запись.
Назначение разрядов.
В регистр записываются младшие восемь разрядов 11-разрядного значения начальной частоты сигнала аудиоканала 3.

Регистр управления канала 3 (NR34)
Адрес: FF1Eh.
Тип: чтение/запись.
Назначение разрядов:
D7 - при записи в этот разряд 1 происходит перезапуск звукового генератора канала 3 с начальными значениями параметров сигнала.
D6 - циклическая/последовательная работа:
0 - звук генерируется постоянно, независимо от значения в регистре NR31;
1 - звук генерируется в течение времени, которое определяется значением, записанным в регистре NR31, после чего канал выключается (сбрасывается разряд D2 в регистре NR52).
D5 - D3 - не используются.
D2 - D0 - три старших разряда 11-разрядного значения начальной частоты сигнала аудиоканала 3. Частота (в герцах) рассчитывается по следующей формуле:

F = 131072 / (2048 - f).

где f - десятичное число, эквивалентное 11-разрядному коду из регистров NR33 и NR34.

Регистр управления длительностью сигнала канала 4 (NR41)
Адрес: FF20h.
Тип: чтение/запись.
Назначение разрядов:
D7, D6 - не используются.
D5 - D0 - код длительности звучания tx = 0-63. Длительность звучания определяет время работы генератора звукового сигнала. Время (в секундах) вычисляется по следующей формуле:

t = (64 -tx) x (1/256).

Регистр управления огибающей канала 4 (NR42)
Адрес: FF21h.
Тип: чтение/запись.
Назначение разрядов:
D7 - D4 - начальное значение огибающей: 0000 - минимум;
1111 - максимум, D3 - направление изменения огибающей:
0 - затухание;
1 - нарастание. D2 - D0 - время изменения огибающей, N:
N = 0 - огибающая не используется; N = 1-7 - огибающая изменяется через N/64 с.

Регистр управления счетчиком канала 4 (NR43)
Адрес: FF22h.
Тип: чтение/запись.
Назначение разрядов:
D7 - D4 - изменение частоты счетчика:
0000 - Х/2;
0001 - Х/22; 0010 - Х/23; 0011 - Х/24;
1101 - Х/214, где X - коэффициент деления! частоты, определяемый разрядами D2 - D0;
1110, 1111 - неиспользуемые значения.
D3 - код числа шагов счетчика: 0-15 шагов; 1-7 шагов.
D2 - D0 - код коэффициента деления частоты X: 000-f/ 4;
001 - f / 8;
010 -f / 16;
011 -f / 24;
100 - f / 32;
101 - f / 40;
110 -f / 48;
111 -f / 56,
где f = 4,194304 МГц.

Регистр управления канала 4 (NR44)
Адрес: FF23h.
Тип: чтение/запись.
Назначение разрядов:
D7 - при записи в этот разряд 1 происходит перезапуск звукового генератора канала 3 с начальными значениями параметров сигнала.
D6 - циклическая/последовательная работа:
0 - звук генерируется постоянно, независимо от значения в регистре NR41;
1 - звук генерируется в течение времени, которое определяется значением в регистре NR41, после чего канал выключается (сбрасывается разряд D3 в регистре NR52).
D5 - D0 - не используются.

Регистр регулировки громкости (NR50)
Адрес: FE2 4h.
Тип: чтение/запись.
Назначение разрядов:
D7 - если этот разряд установлен в 1, сигнал с входа внешнего аудиосигнала V IN поступает на выход S02 звукового процессора. D6 - D4 - установка громкости сигнала на вы ходе S02:

000 - минимум;
111 - максимум.
D3 - если этот разряд установлен в 1, сигнал с входа внешнего аудиосигнала V IN поступает на выход S01 звукового процессора.
D2 - D0 - громкость сигнала на выходе S01:
000 - минимум;
111 - максимум.

<< Назад
Далее >>
Категория: Портативные консоли | Добавил: G-GURU (04.11.2010)
Просмотров: 1831 | Рейтинг: 5.0/1 |
Вы можете отправить сообщение на e-mail друга:
Интересное геймерам: Rainbow Saga

Смотрите другие материалы по темам: GB, программирование, Видеопроцессор, Gameboy, asm, Структурная схема.

Также вам предлагаются схожие с «Игровая система GameBoy (Часть 2)» материалы:

  • Игровая приставка Dendy (Часть 1)
  • Игровая приставка Dendy (Часть 2)
  • Игровая система GameBoy (Часть 1)
  • Игровая система GameBoy (Часть 3)
  • Игровая система GameBoy (Часть 4)
  • Игровая приставка Sega MegaDrive (Часть 2)
  • Игровая приставка Sony PlayStation (Часть 1)
  • Игровая приставка Sony PlayStation (Часть 3)
  • Программирование для приставок
  • Написание программ-эмуляторов
  • Система команд центрального процессора игровой приставки DENDY
  • Система команд центрального процессора игровой приставки GAME BOY
  • Программа Sokoban для DENDY
  • Программа работы со звуковыми процессорами для игровой приставки SEGA MEGA DRIVE
  • Программа для работы с графическим процессором приставки SONY PLAYSTATION
  • Portal clipping, трёхмерная графика, программирование 3D-движка
  • Portal clipping technology in 3D modelling
  • Sumo Slam

Если понравился материал «Игровая система GameBoy (Часть 2)» и вы можете обсудить его ниже.

Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск информации
Игровые братья
Создание игр, Конструкторы игр, Игровые движки Перевод консольных игр Скачать файлы: софт, видео Игорный бизнес в России Разработка игр в СНГ Пранк - телефонные шутки
10 новых описаний
Vagrant Story [PS1]
Tomb Raider: The Prophecy [GBA]
"Война Грибов: В Космос!" - Стратегия №1 возвращается!
Астролорды: Облако Оорта
Пушистый котёнок в вашем кармане
Skyrim Jumping
Shards of War
Dendy MegaBoy
Bliss Charger BC-46: возможность зарядки до 4 устройств одновременно!
400 игр для Dendy от CoolBoy
Все права сохранены за сайтом GFAQ.ru © 2014. Games Frequently Asked Questions
Полные прохождения, Секреты, Коды, Пароли, Советы, Уловки, Читы, Описания для Компьютерных и Консольных Игр. Хостинг от uCoz
Обращаем особое внимание на то, что при цитировании и размещении данных текстовых материалов, обязательно прикрепляйте ссылку на официальный сайт "GFAQ.ru".


Игровая система GameBoy (Часть 3) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

Главная
Всё для геймера: Обзоры игр, Игровые новости, Читы, Советы и пр.
Всё о компьютерных и консольных играх.
Статьи про игры | Регистрация | Вход | PDA-вид | RSS
Приветствую Вас Гость
Меню геймера
  • Игровой Форум
  • Новости и события
  • Сборник статей
  • Видео-прохождения игр
  • Файлы для геймера
    Дополнительно:
  • Игры для всех
  • Игровые ресурсы
  • Поиск для геймера
  • Комментарии геймеров
    Сайт:
  • Информация о сайте
Разделы каталога
Консоли стационарные [21]
Информация про консоли и приставки, комплектующие: картриджи, геймпады и пр.
Портативные консоли [6]
Переносные, ручные игровые консоли, т.н. handheld’ы, а также их комплектующие
Мобильные системы [1]
Мобильные телефоны, смартфоны, КПК, наладонники, планшеты и подобные устройства
PC и железо [3]
Персональный компьютер, микросхемы, джойстики, видеокарты и прочие комплектующие
Ретрокомпьютеры [3]
Бытовые компьютеры прошлого, архитектура, комплектующие и пр.
Игровые автоматы [9]
Статьи про аркадные системы, игровые автоматы, как электрические, так и механические
Опрос геймеров
Сколько часов в сутки вы играете в игры?
[ Результаты · Архив опросов ]
Всего ответов: 12244
Главная » Статьи » Игровые системы » Портативные консоли
Игровая система GameBoy (Часть 3)
Регистр коммутации каналов (NR51)
Адрес: FF25h.
Тип: чтение/запись.
Назначение разрядов:
D7 - если этот разряд установлен в 1, сигнал канала 4 передается на выход S02 звукового процессора.
D6 - если этот разряд установлен в 1, сигнал канала 3 передается на выход S02 звукового процессора.
D5 - если этот разряд установлен в 1, сигнал канала 2 передается на выход S02 звукового процессора.
D4 - если этот разряд установлен в 1, сигнал канала 1 передается на выход S02 звукового процессора.
D3 - если этот разряд установлен в 1, сигнал канала 4 передается на выход S01 звукового процессора.
D2 - если этот разряд установлен в 1, сигнал канала 3 передается на выход S01 звукового процессора.
D1 - если этот разряд установлен в 1, сигнал канала 2 передается на выход S01 звукового процессора.
D0 - если этот разряд установлен в 1, сигнал канала 1 передается на выход S01 звукового процессора.

Регистр управления звуковым процессором (NR52)
Адрес: FF2 6h.
Тип: чтение/запись.
Назначение разрядов:
D7 - управление работой звукового процессора:
0 - выключен;
1 - включен.
D6 - D4 - не используются.
D3 - выключение канала 4 звукового процессора:
0 - выключен;
1 - включен.
D2 - выключение канала 3 звукового процессора:
0 - выключен;
1 - включен.
D1 - выключение канала 2 звукового процессора:
0 - выключен;
1 - включен.
D7 - выключение канала 1 звукового процессора:
0 - выключен;
1 - включен.

ОЗУ паттернов
Адрес: FF30h - FF3Fh.
Тип: чтение/запись.
Назначение.
В ОЗУ паттернов хранятся 32 4-разрядных значения, определяющие форму выходного сигнала звукового процессора. Эти числа записываются в ячейки ОЗУ попарно, причем сначала используется значение из старших четырех разрядов, а затем из младших.

2.2.6. Структура картриджа

Структурная схема картриджа игровой системы GAME BOY приведена на рис. 2.13.
Обязательной частью любого картриджа является микросхема ПЗУ с кодами программы. В некоторые картриджи для увеличения объема памяти игровой системы встроено также внутреннее ОЗУ
Для сохранения игровой ситуации на время выключения системы микросхема ОЗУ может получать питание от литиевой батарейки, размещенной внутри картриджа.
Если объем ПЗУ превышает 32 Кб, в картридже имеется микросхема контроллера страниц памяти, представляющая собой программируемый дешифратор адреса.
ПЗУ с программным обеспечением подсоединяется в адресное пространство центрального процессора в диапазоне 0000h - 7FFFh. При этом по адресам 0000h - 3FFFh всегда располагаются первые 16 Кб ПЗУ, независимо от его объема. По адресам 4000h - 7FFFh может быть подключена любая из оставшихся 16-килобайтных страниц ПЗУ. Номер используемой страницы записывается в регистр микросхемы контроллера страниц памяти.
ОЗУ картриджа становится доступным для центрального процессора по адресам А000h - BFFFh. Если объем ОЗУ превышает 8 Кб, оно также делится на несколько страниц, переключением которых

РИС. 2.13. Структурная схема картриджа игровой системы GAME BOY

управляет микросхема контроллера страниц памяти (МВС).
В первой 16-килобайтной странице ПЗУ должно содержаться несколько блоков данных, причем их расположение строго фиксировано.
Ниже эти блоки и данные, которые должны в них храниться, рассмотрены подробно.
Точки входа для команд RST. При выполнении центральным процессором одной из команд RST запоминается адрес следующей команды в стеке и Осуществляется переход по одному из перечисленных адресов: 0000h, 0008h, 0010h, 0018h, 0020h, 0028h, 0030h, 0038h. Команда RST удобна для вызова часто используемых подпрограмм. По указанным адресам должны находиться точки входа в соответствующие подпрограммы. Следует отметить, что эта функция процессора применяется не всегда.
Точки входа процедур обработки прерывания. При поступлении запроса на прерывание центральный процессор игровой системы GAME BOY передает управление по одному из следующих адресов: 0040h, 0048h, 0050h, 0058h, 0060h. Обычно здесь расположены команды перехода (JP), передающие управление соответствующей процедуре обработки прерывания. Если для какого-либо прерывания процедура обслуживания не используется, рекомендуется разместить по данному адресу команду RETI.
Рассмотрим блок описания картриджа. Он обязательно должен присутствовать, причем должен быть правильно заполненным. Ошибка в этом блоке приведет к неработоспособности картриджа. Блок занимает адреса 0100h - 014Fh. Ниже подробно рассматривается назначение отдельных байтов.
0100h - 0103h. Точка входа в программу картриджа. Исполнение записанной в ПЗУ программы начинается с адреса 0100h. По этим адресам помещается команда NOP и команда перехода на начало программы JP.
Этот фрагмент выглядит следующим образом:

0100 00 NOP
0101 СЗ хх XX JP xxxxh
; хххх - адрес запуска.

0104h - 0133h. Здесь хранится изображение торгового знака фирмы NINTENDO, показываемое на экране в начале игры. Изменение хотя бы одного байта в этой области памяти приведет к неработоспособности картриджа.
Ниже приведено содержимое данной области:

0104 СЕ ED 66 66 СС 0D 00 0В
010С 03 73 00 83 00 ОС 00 0D
0114 00 08 11 1F 88 89 00 0Е
011С DC СС 6Е Е6 DD DD D9 99
0124 ВВ ВВ 67 63 6Е 0Е ЕС СС
012С DD DC 99 9F ВВ В9 33 3Е

0134h - 0142h. Название игры, записанное заглавными английскими буквами в кодировке ASCII. Свободные ячейки заполняются кодом 00h.
014 3h. Если здесь содержится код 80h, программа предназначена для игровой системы COLOR GAME BOY.
0144h, 0145h. Лицензионный код. Если содержимое ячейки с адресом 014Bh не равно 33h, то по этим адресам записаны нули.
0146h. Тип системы: 00h для GAME BOY, 03h для SUPER GAME BOY. Если здесь записан код 00h, дополнительные функции игровой системы SUPER GAME BOY не используются.
0147h. Информация о структуре картриджа. Возможны следующие значения: 00 - имеется только ПЗУ; 01h - имеется ПЗУ и контроллер МВС1; 02h - имеется ПЗУ, контроллер МВС1 и ОЗУ; 03h - имеется ПЗУ, контроллер MBC1 ОЗУ и батарея;
05h - имеется ПЗУ и контроллер МВС2;
0 6h - имеется ПЗУ, контроллер МВС2 и батарея; 08h - имеется ПЗУ и ОЗУ;
09h - имеется ПЗУ, ОЗУ и батарея; 0Bh - имеется ПЗУ и контроллер МММ01; 0Сh - имеется ПЗУ, контроллер МММ01 и ОЗУ;
0Dh - имеется ПЗУ, контроллер МММ01, ОЗУ и батарея;
0Fh - имеется ПЗУ, контроллер МВСЗ, таймер) и батарея;
10h - имеется ПЗУ, контроллер МВСЗ, ОЗУ, таймер и батарея;
1 lh - имеется ПЗУ и контроллер МВСЗ;
12h - имеется ПЗУ, контроллер МВСЗ и ОЗУ;
13h - имеется ПЗУ, контроллер МВСЗ, ОЗУ и батарея;
19h - имеется ПЗУ и контроллер МВС5;
1Ah - имеется ПЗУ, контроллер МВС5 и ОЗУ;
1Bh - имеется ПЗУ, контроллер МВС5, ОЗУ; и батарея;
1Ch - имеется ПЗУ, контроллер МВС5 и карта RUMBLE;
1Dh - имеется ПЗУ, контроллер МВС5, карта RUMBLE и ОЗУ;
1Eh - имеется ПЗУ, контроллер МВС5, карта RUMBLE, ОЗУ и батарея;
1Fh - камера для GAME BOY;
FDh - картридж BANDAI ТАМА5 (тамагочи);
FEh - контроллер HUDSON HUC3;
FFh - контроллер HUDSON HUC1.

0148h. Информация об объеме установленного в картридже ПЗУ:
00h - 32 Кб (2 банка);
01h - 64 Кб (4 банка);
02h - 128 Кб (8 банков);
03h - 256 Кб (16 банков);
04h - 512 Кб (32 банка);
05h- 1 Мб (64 банка);
06h- 2 Мб (128 банков);
52h - 1,1 Мб (72 банка);
53h - 1,2 Мб (80 банков);
54h - 1,5 Мб (96 банков).

0149h. Информация об объеме установленного в картридже ОЗУ:
00h - ОЗУ не установлено;
01h - 2 Кб (1 банк);
02h - 8 Кб (1 банк);
03h - 32 Кб (4 банка);
04h - 128 Кб (16 банков).

014Ah. Код страны распространения картриджа:
00h - Япония;
01h - другие страны.

014Bh. Лицензионный код производителя. Для использования дополнительных функций системы SUPER GAME BOY в этом разряде должен быть записан код 33h.
33h - лицензионный код указан в байтах с адресами 0144h, 0145h.
79h - Accolade;
A4h - Konami.

014Ch. Код микросхемы ПЗУ. Обычно в этом байте записано значение 00h.
014Dh. Дополнительный код блока данных. Программа, расположенная во встроенном ПЗУ системы, вычисляет сумму блока данных, которые находятся в ПЗУ картриджа по адресам 0134h - 014Dh. Затем к результату прибавляется 25. Выполнение программы, записанной в картридже, начинается только в том случае, если младший байт полученной суммы равен 0.
014Eh, 014Fh. Контрольная сумма содержимого картриджа, которая вычисляется путем последовательного сложения всех байтов, записанных в ПЗУ картриджа (кроме байтов с контрольной суммой). Система GAME BOY игнорирует это значение. Для корректной работы системы SUPER GAME BOY здесь должна храниться правильная контрольная сумма.
Основной задачей микросхемы контроллера страниц памяти является переключение страниц ОЗУ и ПЗУ, позволяющее использовать картриджи с объемом ПЗУ более 32 Кб и ОЗУ более 8 Кб. С точки зрения программирования микросхема контроллера страниц памяти представляет собой несколько регистров, доступных при записи по адресам 0000h -7FFFh. Ниже в качестве примера рассмотрен контроллер страниц памяти МВС1.

Подробное описание всех моделей контроллеров не входит в задачу данного издания. В случае необходимости эту информацию можно найти на сайтах Internet, указанных в конце книги.

Контроллер МВС1
Микросхема контроллера страниц памяти МВС1 может работать в двух режимах: в первом (режим 16/8) предусмотрено использование ПЗУ объемом до 2 Мб (16 Мбит) и ОЗУ объемом до 8 Кб; во втором (режим 4/32) за счет уменьшения максимального размера применяемого в картридже ПЗУ до 512 Кб (4 Мбит) объем оперативной памяти увеличивается до 32 Кб. Сразу после включения питания картридж переходит в режим 16/8.
С точки зрения программирования данный контроллер страниц представляет собой три порта, доступных для записи. Рассмотрим назначение каждого из них.

Регистр разрешения записи в ОЗУ
Адрес: любой из диапазона 0000h - 1FFFh.
Тип: запись.
Назначение разрядов.
Сразу после подачи питания на картридж микросхема контроллера запрещает запись в ОЗУ картриджа. Чтобы разрешить модификацию содержимого ОЗУ, необходимо поместить в этот регистр число 0Ah. При любом другом числе, хранящемся здесь, ОЗУ картриджа доступно только для чтения. Фирма NINTENDO рекомендует для блокирования записи использовать число 00h.

Регистр номера страницы ПЗУ
Адрес: любой из диапазона 2000h - 3FFFh.
Тип: запись.
Назначение разрядов:
D7 - D5 - не используются.
D4 - D0 - если картридж работает в режиме 4/32, то в этот регистр помещается номер страницы ПЗУ, подсоединяемой к центральному процессору по адресам 4000 - 7FFFh. Страница с номером 0 всегда находится в диапазоне 0000h - 3FFFh, поэтому когда в регистр номера страницы ПЗУ записан 0, то по адресам 4000h - 7FFFh будет подключена первая страница. Если картридж работает в режиме 16/8, то в регистр помещаются пять младших разрядов номера страницы ПЗУ. Два старших разряда записываются в регистр номера страницы ОЗУ.

Регистр номера страницы ОЗУ
Адрес: любой из диапазона 4000h - 5FFFh.
Тип: запись.
Назначение разрядов:
D7 - D2 - не используются.
D1, D0 - если картридж работает в режиме 4/32, то в два младших разряда этого регистра заносится код номера задействованной страницы ОЗУ; в режиме 16/8 туда помещаются два старших разряда кода номера страницы ПЗУ, подключенной по адресам 4000h - 7FFFh. Не забудьте перед применением ОЗУ картриджа передать в регистр разрешения записи код 0 Ah.

Регистр режима работы контроллера
Адрес: любой из диапазона 6000h - 7FFFh.
Тип: запись.
Назначение разрядов:
D7 - D1 - не используются.
D0- режим работы контроллера:
0 - режим 16/8;
1 - режим 4/32.

2.2.7. Организация ввода/вывода

К устройствам ввода/вывода, используемым игровой системой GAME BOY, относятся таймер, контроллер последовательного интерфейса и контроллер клавиатуры.
С точки зрения программирования все эти устройства представляют собой доступные для чтения и записи порты, расположенные по адресам FF00-FF07h

Таймер
Иногда полезно иметь встроенный таймер, с определенной периодичностью генерирующий запросы на прерывания. Часто запрос, полученный от таймера, служит для вызова подпрограммы, которая должна выполняться через заданные промежутки времени.
Игровая система GAME BOY оснащена встроенным таймером с программным управлением. Возможные значения частоты таймера составляют 4096, 16384, 65536 и 262144 Гц. Это число обозначает, насколько часто значение, хранящееся в регистре счетчика таймера TIMA, увеличивается на 1.
Когда счетчик таймера переполняется (его значение изменяется с FFh на 00h), формируется запрос на прерывание. После этого значение из регистра модуля таймера ТМА копируется в регистр счетчика таймера TIMA, и счет продолжается.
Для правильного взаимодействия с таймером следует ознакомиться с его регистрами.

Регистр делителя частоты DIV
Адрес: FF04h.
Тип: запись/чтение.
Назначение разрядов.
Содержимое регистра увеличивается на 1 с частотой 16384 Гц (16779 Гц в игровой системе SUPER GAME BOY). Запись любого значения по этому адресу приведет к установке в регистр значения 00
Регистр счетчика таймера TIMA
Адрес: F05h. Тип: запись/чтение. Назначение разрядов.
Значение, хранящееся здесь, увеличивается на 1 с частотой, которая зависит от режима работы таймера, задаваемого через регистр ТАС. Когда эта величина изменяется с FFh на OOh, таймер генерирует запрос на прерывание и копирует в регистр счетчика таймера TIMA значение из регистра модуля таймера ТМА.

Регистр модуля таймера ТМА
Адрес: FF06h.
Тип: запись/чтение.
Назначение разрядов.
В регистре хранится 8-разрядное.значение, которое копируется в регистр счетчика таймера TIMA каждый раз после переполнения последнего. Таким образом, это значение задает делитель опорной частоты таймера. Более подробно работа с данным регистром объясняется в приводимых ниже примерах.

Регистр режима работы таймера ТАС
Адрес: FF07h.
Тип: запись/чтение.
Назначение разрядов:
D7 - D3 - не используются.
D2 — включение таймера:
0 — таймер выключен;
1 - таймер включен.
D1 D0- код, определяющий опорную частоту таймера; значения в скобках приведены для системы SUPER GAME BOY:
00 - 4096 (4194) Гц;
01 -262144 (268400) Гц;
10-65536 (67110) Гц;
11 - 16384 (16780) Гц.
Рассмотрим два примера, которые помогут лучше понять принципы программирования встроенного таймера.

; Пример 1. Запрос на прерывание
; генерируется 4096 раз в секунду. Timerl:
LD A, -1
LD ($FF06) , А ; Коэффициент деления
; равен 1.
LD А,4
LD ($FF07),А ; Частота таймера
; составляет 4096 Гц.

Записав в регистр ТМА (FF06h) значение -1 (0FFh), мы устанавливаем коэффициент деления частоты, равный 1. При этом первый же импульс от генератора вызовет переполнение счетчика и формирование запроса на прерывание. Соответственно следующий цикл счета снова начнется со значения FFh. Два младших разряда регистра ТАС (FF07h) задают рабочую частоту таймера 4096 Гц. Таким образом, запрос на прерывание будет генерироваться 4096 раз в секунду.

; Пример 2. Запрос на прерывание
; генерируется 1024 раза в секунду.

Timer2:
LD А,-16
LD ($FF06),A ; Коэффициент деления
; равен 16.
LD А, 7
LD ($FF07),A ; Частота таймера
; составляет 16384 Гц.

Записав в регистр ТМА (FF06h) значение -16 (0F0h), мы устанавливаем в делителе частоты коэффициент деления, равный 16. При этом шестнадцатый импульс от генератора вызовет переполнение счетчика и формирование запроса на прерывание. Следующий цикл счета снова начнется со значения F0h.
Код в двух младших разрядах регистра ТАС (FF07h) определяет рабочую частоту таймера, равную в нашем случае 16384 Гц. Таким образом, запрос на прерывание будет поступать 16384 / 16 = = 1024 раза в секунду.

Контроллер последовательного интерфейса
Последовательный порт ввода/вывода игровой системы GAME BOY очень прост по сравнению со стандартными последовательными портами RS-232 (IBM PC) или RS-485 (Macintosh). Стартовые и стоповые биты отсутствуют, так что использовать такой порт нужно осторожно.
Сначала передаваемый байт записывается в регистр данных последовательного интерфейса SB (FF01h). Для пересылки байта разряд D7 регистра управления последовательным интерфейсом SC (FF02h) необходимо установить в 1. Обмен данными происходит поразрядно, начиная со старшего разряда. Следует помнить, что во время передачи записанный в регистр SB байт также сдвигается.
Когда выведены все восемь разрядов байта, разряд D7 регистра управления последовательным интерфейсом SC автоматически устанавливается в 0 и формируется запрос на прерывание.

Для синхронизации процесса обмена данными используется внутренний или внешний тактовый генератор. Передача каждого разряда сопровождается появлением импульса низкого логического уровня на линии синхронизации. Частота внутреннего генератора фиксирована и составляет 8192 Гц; частота внешнего генератора может принимать любое значение от 1 Гц до 500 кГц.
Полученный контроллером последовательного интерфейса байт также хранится в регистре данных SB. Если к последовательному порту не подключено никакое устройство, из регистра SB будет считан код FFh.
Рассмотрим фрагмент программы, осуществляющий передачу кода 63h через последовательный порт.

SerialOut:
LD А,$63
LD ($FF01),A ; Запись байта в регистр
; данных. LD А, $81, ; Включение передачи
; данных (D7 = 1)
LD ($FF02),А ; с внутренней
; синхронизацией (D0 = 1) .

В заключение приведем краткое описание регистров контроллера последовательного интерфейса системы GAME BOY.

Регистр данных последовательного интерфейса SB
Адрес: FF0h.
Тип: запись/чтение.
Назначение разрядов.
В регистр записывается передаваемый через последовательный порт байт данных. Отсюда же считывается байт, принятый через этот порт.

Регистр управления последовательным интерфейсом SC
Адрес: FF02h.
Тип: запись/чтение.
Назначение разрядов:
D7 - разрешение передачи байта из регистра данных:
0 - нет передачи;
1 - запуск процесса передачи байта.
D6 - D1 - не используются.
D0 - определение типа синхронизации:
0 - внешняя синхронизация (1 Гц - 500 кГц);
1 - внутренняя синхронизация (8192 Гц).

Контроллер клавиатуры
Последним из рассматриваемых нами устройств ввода/вывода является клавиатура. Чтобы правильно описывать процедуры определения нажатой
клавиши, необходимо сначала изучить принципы работы и схемотехнику клавиатуры игровой системы.
Принципиальная схема клавиатуры системы GAME BOY приведена на рис. 2.14.

Рис. 2. 74. Принципиальная схема клавиатуры игровой системы GAME BOY

С точки зрения схемотехники клавиатура представляет собой восемь клавиш с нормально разомкнутыми контактами, организованных в виде матрицы из двух столбцов по четыре клавиши в каждом.
По шинам Р14 и Р15 от процессора поступает сигнал сканирования клавиатуры. Низкий логический уровень сигнала на одной из шин определяет, какой из столбцов клавиатуры будет сканироваться. По шинам Р10 - Р13 данные от клавиш поступают на входы процессора.
Если ни одна клавиша не нажата, то на шинах Р10 - Р13 будет высокий логический уровень.
Нажатие какой-либо клавиши приведет к подаче сигнала на соответствующую шину с линии сканирования Р14 или Р15. Если на линии сканирования высокий уровень, нажатие клавиши не повлияет на состояние выходных сигналов блока клавиатуры; в противном случае на соответствующей выходной шине будет низкий логический уровень.
Таким образом, порядок опроса состояния клавиш игровой системы имеет следующий вид:

1. Установить низкий уровень на линии Р14 и высокий уровень на линии Р15.
2. Определить нажатую клавишу крестовины (RIGHT, LEFT, UP и DOWN) по наличию низкого логического уровня на шинах Р10 - Р13.
3. Установить высокий уровень на линии Р14 и низкий уровень на линии Р15.
4.0пределить нажатую клавишу А, В, SELECT или START по наличию низкого логического уровня на шинах Р10 - Р13.

С точки зрения программирования клавиатура системы GAME BOY представляет собой один порт ввода/вывода с адресом FF00h. В документации и программах этот порт обозначен как Р1.

Регистр контроллера клавиатуры Р1
Адрес: FF00h.
Тип: чтение/запись.
Назначение разрядов:
D7, D6 - не используются.
D5 - состояние линии Р15 (запись).
D4 - состояние линии Р14 (запись).
D3 - состояние линии Р13 (чтение).
D2 - состояние линии Р12 (чтение).
D1 - состояние линии Р11 (чтение).
D0 - состояние линии Р10 (чтение).
В заключение рассмотрим фрагмент игры Ms Pacman, осуществляющий опрос клавиш и формирующий байт состояния клавиатуры.

; Процедура чтения состояния клавиатуры.

Pad_in:
LD А,$20
LD ($FF0O),A ; Установка Р15 = 1
; и Р14 = 0.
LD A,($FF00) ; Считывание
; состояния
; клавиш крестовины.
LD A, ($FF00) ; Две команды чтения
; необходимы
; для формирования
; задержки.
CPL ; Теперь нажатой
; клавише
; соответствует 1.
AND $0F ; Удаление ненужных
; разрядов.
SWAPA ; Внесение результата
LD В,А ; в четыре старших
LD А,$10 ; разряда.
LD ($FF00) ,А ; Установка Р15 = 0
; и Р14 = 1.
LD A,($FF00) ; Считывание
; состояния клавиш
LD A,($FF00) ; А, В, START
; и SELECT.
LD A,($FF00) ; Несколько команд
LD A,($FF00) ; чтения необходимы
LD A, ($FF00) ; для формирования
LD A,($FF00) ; задержки.
CPL ; Теперь нажатой
; клавише
; соответствует 1.
AND $0F ; Удаление ненужных
; разрядов.
OR В ; Объединение
; результатов
; сканирования
; двух столбцов.
LD В,А ; Сохранение результата
LD А,$30 ; в регистре В.
LD ($FF00) ,А ; Установка Р15 = 1
; и Р14 = 1.
LD А, В RET

В результате выполнения описанных действий в регистре А процессора будет сформирован 8-разрядный код, установленные биты которого соответствуют нажатым клавишам.
В данном случае разряды байта соотносятся со следующими клавишами на пульте системы:

D7- START
D6 - SELECT
D5- В D4-А
D3 - DOWN
D2-UP
D1 - LEFT
D0 - RIGHT

2.3. Принципиальная схема

Данный раздел посвящен описанию аппаратного обеспечения и принципов работы системы GAME BOY. Приведены подробные принципиальные схемы
игровой системы и осциллограммы сигналов в контрольных точках.

2.3.1. Процессорный модуль
Процессорный модуль GAME BOY разделен на два функционально законченных блока, которые конструктивно размещены на различных платах.
Принципиальная схема модуля центрального процессора игровой системы GAME BOY приведена на рис. 2.15. Принципиальная схема модуля контроллера ЖКИ представлена на рис. 2.16. Расположение контактов в разъемах игровой системы GAME BOY показано на рис. 2.17. Основой процессорного модуля GAME BOY является микросхема IC1 (LR35902), объединяющая центральный, графический и звуковой процессоры, Микросхема выполнена в 80-выводном корпусе, предназначенном для поверхностного монтажа. Центральный процессор игровой системы синхронизируется от внутреннего тактового генератора, работающего на частоте 4,194304 М Гц. Стабилизация частоты генератора осуществляется кварцевым резонатором X1, подключенным к выводам ICl/73,74. Центральный процессор соединен с микросхемой видеопамяти IC2 (LH5264) посредством шины адреса (VA0 - VA12) и шины данных (VD0 - VD7).

Центральный процессор системы при обращении к видеопамяти генерирует три управляющих сигнала.
Обращаясь к микросхеме видеопамяти для чтения или записи, центральный процессор формирует на выходе VCS (вывод IC1/43) сигнал низкого логического уровня. Во время чтения из микросхемы видеопамяти на выходе VWR (вывод IC1/49) присутствует высокий логический уровень, запрещающий запись в память, а на выходе VOE (вывод IC1/45) - низкий уровень, позволяющий микросхеме памяти IC2 передавать данные из адресуемой ячейки на шину данных.
Во время записи в видеопамять на выходе VWR центрального процессора имеется низкий уровень, разрешающий запись данных в адресуемую ячейку микросхемы памяти. _
Высокий логический уровень на линии VOE блокирует передачу данных на шину.
С помощью отдельных шин (адреса, данных и управления) к центральному процессору IC1 подключена микросхема IC3 (LH5264) внутреннего ОЗУ системы с объемом памяти 8 Кб.
Выбор микросхемы внутреннего ОЗУ осуществляется, если на вход СЕ1 (вывод IC3/20) поступает сигнал низкого логического уровня, а на вход СЕ2 (вывод IC3/26) - высокого логического уровня.
При обращении к микросхеме ОЗУ для чтения центральный процессор подает на вход ОЕ (вывод IC3/22) сигнал низкого логического уровня, разрешающий выдачу данных из адресуемой ячейки на шину. При записи в ОЗУ на вход WR (вывод IСЗ/ 27) поступает сигнал низкого логического уровня, при этом полученный по шине данных байт помещается в ячейку памяти с заданным адресом.
Все сигналы Шин данных, адреса и управления приходят на 32-контактный разъем, через который подключается картридж. Кроме восьми сигналов шины данных (D0 - D7) и шестнадцати сигналов шины адреса (А0 — А15) на этот разъем также выведены пять сигналов управления и напряжение питания +5 В.
Сигнал WR, поступающий на контакт 3 разъема XS1, принимает низкий логический уровень, когда центральный процессор выполняет операцию записи байта в память. Низкий уровень сигнала RD сигнализирует о чтении процессором информации из памяти или внешнего устройства, подключенного к шине данных. Низкий уровень сигнала CS (контакт 5 разъема XS1) показывает, что центральный процессор обращается к внешней памяти, расположенной в диапазоне адресов 0000h - 8000h или А000h - FDFFh. Дальнейшая дешифрация адреса для определения микросхемы, с которой будет

Рис. 2.15. Принципиальная схема процессорного модуля игровой системы GAME BOY

производиться обмен информацией, выполняется путем анализа состояния старших разрядов шины адреса.
На вывод IС1/71 процессора подается сигнал начальной установки RESET. Если он имеет низкий логический уровень, игровая система перезапускается, после чего выполняется программа из внутреннего ПЗУ центрального процессора.
К выводам IC1/62-69 центрального процессора подключена клавиатура игровой системы, представляющая собой матрицу из нормально разомкнутых контактов. Для защиты от перегрузки к выходам процессора (Р14 и Р15) подсоединены диодные сборки DA1 - DA4.
Последовательный интерфейс игровой системы осуществляется через выводы 1С1/68-70. Тактовые импульсы, синхронизирующие пересылку данных по двум однонаправленным линиям, поступают с выхода SCX (вывод 1С1/68). Данные от игровой системы передаются на внешние устройства через выход S-OUT (вывод 1С 1/70). Данные от внешних устройств поступают на вход S-IN (вывод IС1/ 69). Через резисторы R2 — R4 эти сигналы приходят на контакты 2, 3 и 5 разъема XS2 (GB LINK).

Рис. 2.16. Принципиальная схема модуля контроллера ЖКИ игровой системы GAME BOY

Рис. 2.17. Разъемы игровой системы GAME BOY
Ha контакт 4 разъема последовательного интерфейса подается сигнал выхода Р14 контроллера клавиатуры. Диоды, подключаемые между контактами выходного разъема, шиной +5 В и общим проводом, предназначены для защиты выходов процессора от помех. Два аудиосигнала с выводов IС1/59,60 поступают на микросхему усилителя 34 (выводы IC4/7.8).
Внешний звуковой сигнал с контакта 31 разъема для подключения картриджа подается на вход V IN (вывод IC1/61). Он может быть микширован (объединен) внутри звукового процессора с выходными аудиосигналами. Размах входного сигнала, поступающего на усилитель, регулируется с помощью переменного резистора VR1, который используется как регулятор громкости.
С выводов IC4/12.6 усиленные сигналы передаются на разъем подключения наушников. Монофонический сигнал с выхода IC4/3 поступает на встроенный динамик игровой системы.
При подсоединении наушников замыкаются контакты переключателя S1, вывод IC4/4 соединяется с общим проводом и выходной каскад усилителя, формирующий сигнал для встроенного динамика, отключается.
Модуль процессора питается стабилизированным напряжением +5 В.
Для связи центрального процессора игровой системы с контроллером, генерирующим изображение на экране матрицы ЖКИ, предназначена группа из восьми выходов (выводы IC1/50-57).
Сигналы с выходов LD1, LD0 (выводы IC1/50,51), определяющие яркость точек экрана, поступают на контакты DATA0 и DATA1 разъема CN1. Информация о яркости передается последовательно, точка за точкой, начиная с верхнего левого угла экрана. Данные фиксируются в регистре контроллера ЖКИ по отрицательному фронту сигнала DATA LCH (вывод IС1/55).
Сигнал, синхронизирующий работу контроллера дисплея (матрицы ЖКИ) и центрального процессора, поступает на контакт разъема CLOCK с выхода CP (вывод IC1/53).
В конце передачи данных, образующих одну строку на экране, центральный процессор формирует для контроллера дисплея сигнал строчной.
синхронизации, передаваемый через выход ST (вывод IC1/54) на контакт разъема HSYNC.
После получения полного кадра изображения процессор генерирует сигнал кадровой синхронизации, передаваемый с выхода S (вывод IС1/59) в модуль контроллера дисплея через контакт разъема V SYNC.
Сигналы CONTROL (с выхода IC1/52) и ALTSIGL (с выхода IС1/56) предназначены для управления режимами работы контроллера дисплея.
На плате модуля контроллера дисплея игровой системы GAME BOY (рис. 2.16) располагаются матрица ЖКИ с контроллером IC8 (LH5028 или S890623), микросхема формирователя напряжений для индикатора IC9 (IR3E02) и несколько дискретных элементов.
Микросхема контроллера IC8 получает данные от центрального процессора системы и выводит изображение на экран матрицы ЖКИ.
Формирователь напряжений IC9 получает опорное напряжение питания (вывод IC9/18) и генерирует отдельные напряжения, необходимые для работы матрицы ЖКИ на выводах 1С9/12-16. Регулируя опорное напряжение питания с помощью переменного резистора VR1, можно изменять контрастность картинки.
Для питания модуля контроллера дисплея необходимо напряжение питания -18 В.

<< Назад
Далее >>
Категория: Портативные консоли | Добавил: G-GURU (04.11.2010)
Просмотров: 2080 | Рейтинг: 5.0/2 |
Вы можете отправить сообщение на e-mail друга:
Интересное геймерам: Rainbow Saga

Смотрите другие материалы по темам: GB, Структура картриджа, Gameboy, организация, Принципиальная схема, Структурная схема.

Также вам предлагаются схожие с «Игровая система GameBoy (Часть 3)» материалы:

  • Игровая приставка Dendy (Часть 1)
  • Игровая система GameBoy (Часть 1)
  • Игровая система GameBoy (Часть 2)
  • Игровая система GameBoy (Часть 4)
  • Игровая приставка Sony PlayStation (Часть 1)
  • Система команд центрального процессора игровой приставки GAME BOY

Если понравился материал «Игровая система GameBoy (Часть 3)» и вы можете обсудить его ниже.

Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск информации
Игровые братья
Создание игр, Конструкторы игр, Игровые движки Перевод консольных игр Скачать файлы: софт, видео Игорный бизнес в России Разработка игр в СНГ Пранк - телефонные шутки
10 новых описаний
Vagrant Story [PS1]
Tomb Raider: The Prophecy [GBA]
"Война Грибов: В Космос!" - Стратегия №1 возвращается!
Астролорды: Облако Оорта
Пушистый котёнок в вашем кармане
Skyrim Jumping
Shards of War
Dendy MegaBoy
Bliss Charger BC-46: возможность зарядки до 4 устройств одновременно!
400 игр для Dendy от CoolBoy
Все права сохранены за сайтом GFAQ.ru © 2014. Games Frequently Asked Questions
Полные прохождения, Секреты, Коды, Пароли, Советы, Уловки, Читы, Описания для Компьютерных и Консольных Игр. Хостинг от uCoz
Обращаем особое внимание на то, что при цитировании и размещении данных текстовых материалов, обязательно прикрепляйте ссылку на официальный сайт "GFAQ.ru".


Игровая система GameBoy (Часть 4) - Портативные консоли - Игровые системы - Статьи про игры - Games Frequently Asked Questions

Главная
Всё для геймера: Обзоры игр, Игровые новости, Читы, Советы и пр.
Всё о компьютерных и консольных играх.
Статьи про игры | Регистрация | Вход | PDA-вид | RSS
Приветствую Вас Гость
Меню геймера
  • Игровой Форум
  • Новости и события
  • Сборник статей
  • Видео-прохождения игр
  • Файлы для геймера
    Дополнительно:
  • Игры для всех
  • Игровые ресурсы
  • Поиск для геймера
  • Комментарии геймеров
    Сайт:
  • Информация о сайте
Разделы каталога
Консоли стационарные [21]
Информация про консоли и приставки, комплектующие: картриджи, геймпады и пр.
Портативные консоли [6]
Переносные, ручные игровые консоли, т.н. handheld’ы, а также их комплектующие
Мобильные системы [1]
Мобильные телефоны, смартфоны, КПК, наладонники, планшеты и подобные устройства
PC и железо [3]
Персональный компьютер, микросхемы, джойстики, видеокарты и прочие комплектующие
Ретрокомпьютеры [3]
Бытовые компьютеры прошлого, архитектура, комплектующие и пр.
Игровые автоматы [9]
Статьи про аркадные системы, игровые автоматы, как электрические, так и механические
Опрос геймеров
Сколько часов в сутки вы играете в игры?
[ Результаты · Архив опросов ]
Всего ответов: 12244
Главная » Статьи » Игровые системы » Портативные консоли
Игровая система GameBoy (Часть 4)
2.3.2. Картридж

Картридж для игровой системы GAME BOY содержит ПЗУ объемом от 16 Кб, а в некоторых моделях и дополнительное ОЗУ.
Если объем ПЗУ превышает 32 Кб, то обязательной частью картриджа является микросхема контроллера банков памяти (МВС), определяющая, какая из страниц ПЗУ и ОЗУ подключена к центральному процессору.
Принципиальная схема картриджа, содержащего ПЗУ, ОЗУ с резервным питанием от литиевой батареи и микросхему контроллера банков памяти МВС1, приведена на рис. 2.18.
Картридж состоит из микросхемы ПЗУ объемом 512 Кб (IC1), микросхемы ОЗУ объемом 8 Кб (IC3) и контроллера страниц памяти (IC2).
Микросхема IC1 (LH534) представляет собой масочное ПЗУ с записанной программой. Младшие разряды адреса (А0 — А13) поступают с соответствующих контактов разъема картриджа, а старшие (А14 - А18) формируются микросхемой управления банками памяти IC2.
Сигнал CS, разрешающий работу ПЗУ IC1, является сигналом старшего разряда шины адреса А15. Таким образом, обмен данными с ПЗУ происходит в тот момент, когда процессор обращается к памяти, расположенной по адресам 0000 7FFFh.
Сигнал низкого логического уровня на входе 0Е (вывод I1/24) позволяет ПЗУ занимать шину данных для передачи байта процессору. На этот вывод микросхемы подается сигнал RD, низкий уровень которого указывает, что процессор считывает байт из памяти.
Микросхема IC3 (HY6264A) - это статическое ОЗУ. Шины адреса (А0 - А12) и данных (D0 - D7) подключены непосредственно к одноименным контактам разъема картриджа.
Выбор микросхемы ОЗУ происходит при наличии сигналов низкого логического уровня на входе CS1 (вывод IC3/20) и высокого логического уровня на входе CS2 (вывод IC3/26). Вход CS2 соединен с выходом 3 микросхемы IC4, а вход CS1 -с выходом RCS микросхемы контроллера страниц памяти.
Считывание информации из ОЗУ допустимо при низком логическом уровне сигнала RD на входе ОЕ (вывод IC3/22).
Для записи информации в микросхему ОЗУ сигнал низкого логического уровня подается на вход WR (вывод IC3/27), подключенный к одноименному контакту разъема.
Микросхема контроллера банков памяти (IC2) представляет собой программируемый дешифратор адреса, формирующий старшие разряды адреса для микросхемы ПЗУ IС1.
На входы микросхемы IC2 поступают сигналы шины адреса (А13 - А15) и данных (D0 - D4). С выходов микросхемы IC2 снимаются старшие разряды адреса (МА14 - МА18), которые затем передаются на входы микросхемы ПЗУ IC1, а также дополнительные разряды адреса (ЕА0 и ЕА1), необходимые при применении микросхем ОЗУ или ПЗУ большей информационной емкости.
В рассматриваемой схеме картриджа часть выходов микросхемы не подключена и функции контроллера банков памяти используются не полностью. С помощью дополнительных выходов ЕА1 и ЕА2 можно увеличить объем ПЗУ до 16 Мбит или ОЗУ до 32 Кб. В первом случае следует соединить выходы ЕА0и ЕА1 микросхемы IC2 с входами А19 и А20 шины адреса микросхемы ПЗУ; во втором -выходы ЕА0и ЕА1 микросхемы IC2 с входами А13 и А14 микросхемы ОЗУ.

2.3.3. Дополнительные устройства

Для игровой приставки GAME BOY разработано множество различных дополнительных устройств, в том числе принтер и фотокамера.
Рассмотреть все типы устройств не представляется возможным. Ниже рассказывается о способах

Рис. 2.18. Принципиальная схема картриджа игровой системы GAME BOY

объединения нескольких игровых систем GAME BOY, а также об использовании последовательного порта игровой системы для соединения с нестандартными устройствами.

Соединение нескольких систем GAME BOY
Основной задачей последовательного порта игровой системы GAME BOY является установка связи с другими игровыми приставками. Используя этот порт, можно насладиться игрой с одним или несколькими партнерами, что гораздо интереснее, чем игра с компьютером.
Проще всего объединить две игровые системы GAME BOY. Для этого потребуется только специальный кабель, входящий в комплект поставки.
Принципиальная схема такого кабеля приведена на рис. 2.19.
Несколько сложнее подключить большее число приставок: дело в том, что последовательный порт обладает минимальными возможностями контроля передаваемых данных. Следовательно, понадобится дополнительное оборудование, выполняющее функцию сервера и контролирующее пересылку данных от системы к приставке.
Фирма NINTENDO выпустила адаптер для одновременного подключения четырех игровых систем GAME BOY. Принципиальная схема такого адаптера представлена на рис. 2.20.

Рис. 2.19. Принципиальная схема кабеля для соединения двух игровых систем GAME BOY

Основу адаптера составляет специальный микроконтроллер типа МС68НС05Р7 производства фирмы MOTOROLA, управляющий обменом данными. Приставка главного игрока подключена с помощью встроенного кабеля с разъемом для основной системы. На кабеле имеются еще три разъема (гнезда) для подключения приставок других игроков посредством стандартных соединительных кабелей.
Микросхема IC2 (PST529G) формирует сигнал начальной установки для микроконтроллера IС1.
Рабочую частоту внутреннего тактового генератора микроконтроллера стабилизирует кварцевый резонатор X1 (3 МГц), подключенный к выводам IС1/26,27.

Использование последовательного порта
Дополнительные устройства для игровой системы GAME BOY можно подсоединить двумя способами: через разъем картриджа и через последовательный порт.

Рис. 2.20. Принципиальная схема адаптера для соединения четырех игровых систем GAME BOY

Если использование разъема картриджа является достаточно простым, а основные принципы становятся ясными после знакомства с принципиальной схемой картриджа, то подключение к последовательному порту требует понимания принципов его работы, а также определенных навыков программирования.
В этом разделе на конкретных примерах анализируется функционирование устройств, подсоединяемых к игровой системе GAME BOY через последовательный порт.
Исследование возможностей последовательного порта игровой системы мы начнем с рассмотрения временной диаграммы его работы, приведенной на рис. 2.21.
Для получения изображенного на диаграмме сигнала необходимо выполнить следующие команды:

LD А,$75
LD ($FF01),А ; Запись байта в регистр
; данных.
LD А,$81 ; Включение передачи
; данных (D7 = 1)
LD ($FF02),А ; с внутренней
; синхронизацией (D0 = 1) .

Затем на выходе синхронизации (контакт 5 разъема последовательного порта) формируется последовательность тактовых импульсов частотой 8192 Гц, после чего на выходе данных (контакт 3 разъема последовательного порта) будут появляться значения отдельных разрядов числа из регистра данных, начиная с младшего разряда.
Чтобы принять такой сигнал и преобразовать его в параллельные данные, наиболее удобен регистр сдвига, например микросхема 74НС595, подключенная по схеме, которая изображена на рис. 2.22.

Рис. 2.21 Временная диаграмма работы последовательного порта игровой системы GAME BOY

Рис. 2.22. Принципиальная схема модуля для обмена параллельными данными через последовательный порт игровой системы GAME BOY

Для ввода информации через последовательный порт также используется регистр сдвига. Данные с выхода сдвигового регистра поступают на вход данных последовательного порта игровой системы (контакт 3 разъема), в результате чего становятся доступными центральному процессору при чтении из регистра данных SB (FF01h).
При работе с последовательным портом игровой приставки удобнее всего составить программу так, чтобы передача и прием шли одновременно и генерация запроса на прерывания от контроллера последовательного порта была разрешена. Для этого необходимо установить бит D3 в регистре управления контроллером прерываний IE (FFFFh), например командой:

LD A, #09 ; Разрешение прерывания
; от видеопроцессора
LD (#FFFF) , А ; и от последовательного
; порта.

Каждый раз, когда контроллер последовательного порта выполнит передачу восьми бит данных, будет генерироваться запрос на прерывание с передачей управления по адресу 0058h, где обычно располагается команда перехода на процедуру обработки прерывания.
Рассмотрим еще один пример программы:

ORG #0058h
JP Serin

SerInit:
LD A, #09 ; Разрешение
; прерывания от LD (#FFFFh) ,A
; последовательного
; порта.
RET

SerOut:
LD A,(data) ; Запись
; передаваемого байта LD (#FF01),A ; в регистр
; контроллера LD A,#81
; и включение
; передачи LD (#FF02),A
; с внутренней
; синхронизацией.

RET Serin:
PUSH AF
LD A, (#FF01) ; Чтение принятого
; байта
LD (port),A ; и запись в память.
POP AF
RETI

В начале вызывается подпрограмма Serlnit, которая разрешает обрабатывать запросы на прерывание, поступающие от контроллера последоваТЕЛЬНОГО интерфейса.
Когда необходимо передать байт, следует записать его в ячейку памяти data и вызвать подпрограмму SerOut, которая запускает процесс обмена информацией через последовательный порт системы, после чего возвращает управление в основную программу. Когда передача будет окончена, одновременно завершится и процесс приема байта, а затем запустится процедура обработки прерывания. В результате принятый байт окажется в ячейке памяти port, откуда его может считать основная программа.
Если вы применяете внешнюю синхронизацию, не забудьте сбросить соответствующий разряд в регистре управления последовательным интерфейсом и подать тактовый сигнал от схемы на контакт 6 разъема последовательного интерфейса: в противном случае обмен данными производиться не будет.
Еще одно замечание касается написания программ, которые используют передачу данных между двумя приставками. Одна из приставок должна быть ведущей, работающей на основе внутренней синхронизации передачи и управляющей обменом данными. Другая приставка соответственно будет ведомой, применяющей внешнюю синхронизацию и передающей данные по запросу от ведущей системы.

2.3.4. Блок питания

Блок питания, используемый игровой системой GAME BOY, разделен на два функционально независимых модуля: внешний сетевой адаптер и внутренний преобразователь напряжения.
Сетевой адаптер выполнен по традиционной для игровых приставок схеме и содержит понижающий трансформатор, диодный мост для выпрямления переменного напряжения и конденсатор, сглаживающий пульсации выходного напряжения. Выходное напряжение адаптера составляет 6-8 В.
Принципиальная схема сетевого адаптера GAME BOY приведена на рис. 2.23.
Внутренний стабилизатор в системе GAME BOY представляет собой двухканальный импульсный преобразователь напряжения с разделительным трансформатором. Он преобразует напряжение +6,5 В, поступающее от сетевого адаптера или батареи, в два напряжения: +5 В (для микросхем системы) и -18 В (для матрицы ЖКИ).
Принципиальная схема внутреннего стабилизатора (преобразователя) игровой системы GAME BOY представлена на рис. 2.24.
Напряжение питания поступает на схему преобразователя от внешнего сетевого адаптера через защитный диод D1. Фильтр, выполненный на элементах С1, С2, L1, СЗ, предназначен для сглаживания пульсаций выходного напряжения сетевого адаптера и защиты от высокочастотных помех со стороны преобразователя.
Преобразователь выполнен по схеме блокинг-генератора. Ключевой транзистор Q1 периодически

Рис. 2.23. Принципиальная схема сетевого адаптера игровой системы GAME BOY

Рис. 2.24. Принципиальная схема внутреннего стабилизатора игровой системы GAME BOY

подсоединяет обмотку трансформатора Т1 к источнику питания. На обмотке обратной связи наводится напряжение, которое через цепь С4, R3 управляет частотой переключения Q1. Выходные напряжения на вторичных обмотках выпрямляются диодами D3, D2 и сглаживаются фильтрами С7 - С10.
Преобразователь имеет дополнительную обратную связь для стабилизации канала +5 В. Выходное выпрямленное напряжение после диода D3 через стабилитрон D4 поступает на базу транзистора Q2, который регулирует открывающее смещение транзистора Q1. Это приводит к изменению длительности импульса на обмотках трансформатора, в результате чего напряжение канала +5 В стабилизируется.

2.4. Характерные неисправности
В данном разделе приводится перечень неисправностей, наиболее часто встречающихся при эксплуатации игровой системы GAME BOY, а также алгоритмы их поиска и методы устранения.

Приставка не включается
Возможные причины: неисправность блока питания или внутреннего стабилизатора: короткое замыкание или обрыв цепей питания; неисправность картриджа; неисправность процессорного модуля. Алгоритм

поиска неисправности:
1. Проверить выходное напряжение сетевого адаптера. Если измеренное значение выходит за пределы 6-7 В, заменить сетевой адаптер. Практика показывает, что наиболее часто из-за перегрузки выходят из строя диоды выпрямительного моста, Если адаптер вышел из строя полностью, то подойдет любой источник питания с выходным напряжением 6-7 В при токе нагрузки 500 мА.
2. Проверить блоки игровой системы на отсутствие коротких замыканий. Если короткое замыкание обнаружено, проверить стабилизатор и установленные в нем транзисторы. При возникновении короткого замыкания часто происходит обрыв одного из печатных проводников в цепи питания, поэтому следует тщательно осмотреть платы и удостовериться в целостности проводников. 3. Если короткого замыкания нет, проверить внутренний стабилизатор игровой системы, отключив выход ной разъем. Напряжения на выходе стабилизатора должны быть в пределах +5+-0,1 В и -19+-0,25 В. Если напряжение выходит за указанные границы, в стабилизаторе следует проверить напряжения на обмотках трансформатора Т1, диоды D2, D3 и транзисторы Q1, Q2.
4. Проверить кварцевый генератор, а также функционирование микросхемы формирователя напряжений для матрицы ЖКИ. Исправность этих эля ментов свидетельствует о необходимости замен всего процессорного модуля.

Приставка работает нестабильно
Возможные причины: неисправность внешнего сетевого адаптера или внутреннего стабилизатора; загрязнение контактов разъема подключения картриджа; разряд или выход из строя аккумулятором Алгоритм поиска неисправности:

1. Проверить исправность аккумуляторных батарея измерив напряжение каждого элемента под нагрузкой. Часто нестабильная работа системы объясняется выходом из строя одного из аккумуляторов.
2. Проверить выходное напряжение сетевого адаптера. Сбой обычно возникает из-за малой нагрузочной способности адаптера, поставляемого в комплекте с игровой приставкой. Проблема решается при подключении более мощного источника питания.
3. Проверить надежность контактных соединений в разъемах системы. Разъем для подключения картриджа необходимо осмотреть особенно тщательно, при необходимости протереть его спиртом.
4. Проверить внутренний стабилизатор напряжения питания игровой системы. Особое внимание следует обратить на уровень пульсаций выходного напряжения импульсного преобразователя.
5. Возможным способом решения проблемы является установка дополнительных конденсаторов емкостью 100,0 мкФ х 16 В и 0,01 мкф в цепи питания на каждой из плат системы и в картридже.

На экране отсутствует изображение, звук есть
Возможные причины: выход из строя микросхемы IC9 или транзистора Q1 в модуле контроллера дисплея; неисправность матрицы ЖКИ; обрыв в цепи регулятора контрастности VR2; обрыв или плохой контакт в цепи питания матрицы ЖКИ. Алгоритм поиска неисправности:

1. Проверить напряжения питания -18 В на плате с ЖКИ. Если это напряжение отсутствует, проверить соответствующий выпрямитель в блоке питания, а также прохождение напряжения от блока питания до микросхем модуля контроллера матрицы ЖКИ.
2. Тестером проверить напряжение на выводе IC9/18. При отсутствии напряжения заменить регулятор контрастности VR2.
3. Проверить напряжения на выводах IC9/11-14. Отсутствие напряжения на любом из них свидетельствует о неисправности микросхемы iC9.
4. Проверить транзистор Q1 на плате контроллера матрицы ЖКИ и при необходимости заменить его.
5. Проверить прохождение сигналов от центрального процессора (выводы IC1/50-57) до контроллера матрицы ЖКИ (выводы IC8/3-9.16-18). Наличие сигналов позволяет сделать вывод о неисправности контроллера дисплея, а их отсутствие - о неисправности центрального процессора IC1.

Отсутствует звуковое сопровождение
Возможные причины: неисправность центрального процессора IC1 или усилителя IС4; обрыв в регуляторе громкости VR1; неисправность переключателя в разъеме подсоединения наушников. Алгоритм поиска неисправности:
1. Проверить наличие звукового сигнала на выходах S01 и S02 центрального процессора (выводы 1С1/ 59,60). Отсутствие сигнала свидетельствует о неисправности центрального процессора IC1.
2. Проверить прохождение сигнала от выходов процессора IC1 до входов усилителя IC4 (выводы IC4/7,8) через регулятор громкости VR1.
3. Проверить наличие выходного сигнала на выводах IC4/12.15. При отсутствии сигнала заменить микросхему IC4.
4. Проверить работу выключателя, расположенного в разъеме подключения наушников, и при необходимости заменить его.
5. Проверить динамик игровой системы.

Не работают все кнопки управления
Возможные причины: обрыв проводников или плохой контакт; загрязнение кнопок; неисправность микросхемы IC1. Алгоритм поиска неисправности:
1. Проверить целостность проводников, идущих от кнопок к выводам центрального процессора. Тканью, смоченной в спирте, тщательно очистить контактные площадки, расположенные под клавишами.
2. Проверить выходные сигналы Р14 и Р15 процессора (выводы IC1/62.63). Отсутствие сигналов свидетельствует о необходимости замены микросхемы центрального процессора IC1.
3. Проверить диодные сборки DA1 - DA4. Если сигналы с клавиатуры поступают на входы процессора (выводы IC1/64-69), неисправна микросхема центрального процессора IC1.

Не работают некоторые кнопки управления
Возможные причины: загрязнение контактных площадок или неисправность микросхемы IC1. Алгоритм поиска неисправности:
1. Протереть спиртом контактные площадки на плате и резиновую прокладку с токопроводящими площадками.
2. Если неисправны токопроводящие площадки, то восстановить их можно, наклеив кусочки фольги. Удобнее использовать фольгу от сигаретных пачек: она имеет бумажную основу, что обеспечивает лучшее приклеивание к резине.
З. Токопроводящее покрытие на плате воссоздается с помощью очищенного монтажного провода или токопррводящего клея.

<< Назад
Категория: Портативные консоли | Добавил: G-GURU (04.11.2010)
Просмотров: 3091 | Рейтинг: 5.0/1 |
Вы можете отправить сообщение на e-mail друга:
Интересное геймерам: Rainbow Saga

Смотрите другие материалы по темам: блок питания, Дополнительные устройства, картридж, GB, Gameboy, asm, Структурная схема.

Также вам предлагаются схожие с «Игровая система GameBoy (Часть 4)» материалы:

  • Игровая приставка Dendy (Часть 1)
  • Игровая система GameBoy (Часть 1)
  • Игровая система GameBoy (Часть 2)
  • Игровая система GameBoy (Часть 3)
  • Игровая приставка Sony PlayStation (Часть 1)
  • Система команд центрального процессора игровой приставки DENDY
  • Система команд центрального процессора игровой приставки GAME BOY
  • Программа Sokoban для DENDY
  • Программа работы со звуковыми процессорами для игровой приставки SEGA MEGA DRIVE
  • Программа для работы с графическим процессором приставки SONY PLAYSTATION
  • Sumo Slam
  • 400 игр для Dendy от CoolBoy

Если понравился материал «Игровая система GameBoy (Часть 4)» и вы можете обсудить его ниже.

Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск информации
Игровые братья
Создание игр, Конструкторы игр, Игровые движки Перевод консольных игр Скачать файлы: софт, видео Игорный бизнес в России Разработка игр в СНГ Пранк - телефонные шутки
10 новых описаний
Vagrant Story [PS1]
Tomb Raider: The Prophecy [GBA]
"Война Грибов: В Космос!" - Стратегия №1 возвращается!
Астролорды: Облако Оорта
Пушистый котёнок в вашем кармане
Skyrim Jumping
Shards of War
Dendy MegaBoy
Bliss Charger BC-46: возможность зарядки до 4 устройств одновременно!
400 игр для Dendy от CoolBoy
Все права сохранены за сайтом GFAQ.ru © 2014. Games Frequently Asked Questions
Полные прохождения, Секреты, Коды, Пароли, Советы, Уловки, Читы, Описания для Компьютерных и Консольных Игр. Хостинг от uCoz
Обращаем особое внимание на то, что при цитировании и размещении данных текстовых материалов, обязательно прикрепляйте ссылку на официальный сайт "GFAQ.ru".


The Cartridge Header - GbdevWiki

The Cartridge Header

From GbdevWiki

Jump to: navigation, search

This article is part of Pan Docs.

Please read more about Pan Docs, and what you can do to improve this article by clicking here.

To view this information in its original context, click here: http://gbdev.gg8.se/files/docs/mirrors/pandocs.html#thecartridgeheader

An internal information area is located at 0100-014F in each cartridge. It contains the following values:

Contents

[hide]
  • 1 0100-0103 - Entry Point
  • 2 0104-0133 - Nintendo Logo
  • 3 0134-0143 - Title
  • 4 013F-0142 - Manufacturer Code
  • 5 0143 - CGB Flag
  • 6 0144-0145 - New Licensee Code
  • 7 0146 - SGB Flag
  • 8 0147 - Cartridge Type
  • 9 0148 - ROM Size
  • 10 0149 - RAM Size
  • 11 014A - Destination Code
  • 12 014B - Old Licensee Code
  • 13 014C - Mask ROM Version number
  • 14 014D - Header Checksum
  • 15 014E-014F - Global Checksum

0100-0103 - Entry Point

After displaying the Nintendo Logo, the built-in boot procedure jumps to this address (100h), which should then jump to the actual main program in the cartridge. Usually this 4 byte area contains a NOP instruction, followed by a JP 0150h instruction. But not always.

0104-0133 - Nintendo Logo

These bytes define the bitmap of the Nintendo logo that is displayed when the gameboy gets turned on. The hexdump of this bitmap is:

 CE ED 66 66 CC 0D 00 0B 03 73 00 83 00 0C 00 0D
 00 08 11 1F 88 89 00 0E DC CC 6E E6 DD DD D9 99
 BB BB 67 63 6E 0E EC CC DD DC 99 9F BB B9 33 3E

The gameboys boot procedure verifies the content of this bitmap (after it has displayed it), and LOCKS ITSELF UP if these bytes are incorrect. A CGB verifies only the first 18h bytes of the bitmap, but others (for example a pocket gameboy) verify all 30h bytes.

0134-0143 - Title

Title of the game in UPPER CASE ASCII. If it is less than 16 characters then the remaining bytes are filled with 00's. When inventing the CGB, Nintendo has reduced the length of this area to 15 characters, and some months later they had the fantastic idea to reduce it to 11 characters only. The new meaning of the ex-title bytes is described below.

013F-0142 - Manufacturer Code

In older cartridges this area has been part of the Title (see above), in newer cartridges this area contains an 4 character uppercase manufacturer code. Purpose and Deeper Meaning unknown.

0143 - CGB Flag

In older cartridges this byte has been part of the Title (see above). In CGB cartridges the upper bit is used to enable CGB functions. This is required, otherwise the CGB switches itself into Non-CGB-Mode. Typical values are:

 80h - Game supports CGB functions, but works on old gameboys also.
 C0h - Game works on CGB only (physically the same as 80h).

Values with Bit 7 set, and either Bit 2 or 3 set, will switch the gameboy into a special non-CGB-mode with uninitialized palettes. Purpose unknown, eventually this has been supposed to be used to colorize monochrome games that include fixed palette data at a special location in ROM.

0144-0145 - New Licensee Code

Specifies a two character ASCII licensee code, indicating the company or publisher of the game. These two bytes are used in newer games only (games that have been released after the SGB has been invented). Older games are using the header entry at 014B instead.

0146 - SGB Flag

Specifies whether the game supports SGB functions, common values are:

 00h = No SGB functions (Normal Gameboy or CGB only game)
 03h = Game supports SGB functions

The SGB disables its SGB functions if this byte is set to another value than 03h.

0147 - Cartridge Type

Specifies which Memory Bank Controller (if any) is used in the cartridge, and if further external hardware exists in the cartridge.

 00h  ROM ONLY                 13h  MBC3+RAM+BATTERY
 01h  MBC1                     15h  MBC4
 02h  MBC1+RAM                 16h  MBC4+RAM
 03h  MBC1+RAM+BATTERY         17h  MBC4+RAM+BATTERY
 05h  MBC2                     19h  MBC5
 06h  MBC2+BATTERY             1Ah  MBC5+RAM
 08h  ROM+RAM                  1Bh  MBC5+RAM+BATTERY
 09h  ROM+RAM+BATTERY          1Ch  MBC5+RUMBLE
 0Bh  MMM01                    1Dh  MBC5+RUMBLE+RAM
 0Ch  MMM01+RAM                1Eh  MBC5+RUMBLE+RAM+BATTERY
 0Dh  MMM01+RAM+BATTERY        FCh  POCKET CAMERA
 0Fh  MBC3+TIMER+BATTERY       FDh  BANDAI TAMA5
 10h  MBC3+TIMER+RAM+BATTERY   FEh  HuC3
 11h  MBC3                     FFh  HuC1+RAM+BATTERY
 12h  MBC3+RAM

0148 - ROM Size

Specifies the ROM Size of the cartridge. Typically calculated as "32KB shl N".

 00h -  32KByte (no ROM banking)
 01h -  64KByte (4 banks)
 02h - 128KByte (8 banks)
 03h - 256KByte (16 banks)
 04h - 512KByte (32 banks)
 05h -   1MByte (64 banks)  - only 63 banks used by MBC1
 06h -   2MByte (128 banks) - only 125 banks used by MBC1
 07h -   4MByte (256 banks)
 52h - 1.1MByte (72 banks)
 53h - 1.2MByte (80 banks)
 54h - 1.5MByte (96 banks)

0149 - RAM Size

Specifies the size of the external RAM in the cartridge (if any).

 00h - None
 01h - 2 KBytes
 02h - 8 Kbytes
 03h - 32 KBytes (4 banks of 8KBytes each)

When using a MBC2 chip 00h must be specified in this entry, even though the MBC2 includes a built-in RAM of 512 x 4 bits.

014A - Destination Code

Specifies if this version of the game is supposed to be sold in japan, or anywhere else. Only two values are defined.

 00h - Japanese
 01h - Non-Japanese

014B - Old Licensee Code

Specifies the games company/publisher code in range 00-FFh. A value of 33h signalizes that the New License Code in header bytes 0144-0145 is used instead. (Super GameBoy functions won't work if <> $33.)

014C - Mask ROM Version number

Specifies the version number of the game. That is usually 00h.

014D - Header Checksum

Contains an 8 bit checksum across the cartridge header bytes 0134-014C. The checksum is calculated as follows:

 x=0:FOR i=0134h TO 014Ch:x=x-MEM[i]-1:NEXT

The lower 8 bits of the result must be the same than the value in this entry. The GAME WON'T WORK if this checksum is incorrect.

014E-014F - Global Checksum

Contains a 16 bit checksum (upper byte first) across the whole cartridge ROM. Produced by adding all bytes of the cartridge (except for the two checksum bytes). The Gameboy doesn't verify this checksum.

Retrieved from "http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header"
Category: Pandocs Article
Views
  • Page
  • Discussion
  • View source
  • History
Personal tools
  • Log in
Navigation
  • Main Page
  • Community portal
  • Current events
  • Recent changes
  • Random page
  • Help
Toolbox
  • What links here
  • Related changes
  • Special pages
  • Printable version
  • Permanent link
Powered by MediaWiki
  • This page was last modified on 28 June 2009, at 13:00.
  • This page has been accessed 10,897 times.
  • Privacy policy
  • About GbdevWiki
  • Disclaimers


GameBoy CPU InstructionSet Sheet (GCISheet)
QuickJump Navigator:
ADC A,n
ADD A,n
ADD HL,n
ADD SP,n
AND n
BIT b,r
CALL n
CALL cc,n 		CCF
CP n
CPL
DAA
DEC n
DEC nn
DI
EI 		INC n
INC nn
JP n
JP cc,n
JP [HL]
JR n
JR cc,n
HALT 		LD A,n
LD n,A
LD A,[C]
LD A,[HL+]
LD A,[HL-]
LD A,[HLI]
LD A,[HLD]
LD [C],A 		LD [HL+],A
LD [HL-],A
LD [HLI],A
LD [HLD],A
LD r1,r2
LD n,nn
LD HL,[SP+n]
LD SP,HL 		LD [n],SP
LDD A,[HL]
LDD [HL],A
LDH [n],A
LDH A,[n]
LDHL SP,n
LDI A,[HL]
LDI [HL],A 		NOP
OR n
POP nn
PUSH nn
RES b,r
RET
RET cc
RETI 		RL n
RLC n
RR n
RRC n
RST n
SBC A,n
SCF
SET b,r 		SLA n
SRA n
SRL n
STOP
SUB n
SWAP n
XOR n
ADC A,n       - Add n + Carry flag to A.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Set if carry from bit 3.

        C - Set if carry from bit 7.
Top
ADD A,n       - Add n to A.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Set if carry from bit 3.

        C - Set if carry from bit 7.
Top
ADD HL,n      - Add n to HL.

    n = BC,DE,HL

    Flags affected:

        Z - Not affected

        N - Reset.

        H - Set if carry from bit 11.

        C - Set if carry from bit 15.
Top
ADD SP,n      - Add n to Stack Pointer (SP).

    n = one byte signed immediate value

    Flags affected:

        Z - Reset.

        N - Reset.

        H - Set or reset according to operation.

        C - Set or reset according to operation.
Top
AND n         - Logically AND n with A, result in A.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Set.

        C - Reset.
Top
BIT b,r       - Test bit b in register r.

    b = 0-7, r = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if bit b of register r is 0.

        N - Reset.

        H - Set.

        C - Not affected.
Top
CALL n        - Push address of next instruction onto

        stack and then jump to address n.

    Flags affected:

        None
Top
CALL cc,n     - Call address n if following condition

        is true:

    cc = NZ, Call if Z flag is reset.

    cc = Z,  Call if Z flag is set.

    cc = NC, Call if C flag is reset.

    cc = C,  Call if C flag is set.

    Flags affected:

        None
Top
CCF           - Complement carry flag.

    If C flag is set then reset it.

    If C flag is reset then set it.

    Flags affected:

        Z - Not affected.

        N - Reset.

        H - Reset.

        C - Complemented.
Top
CP n          - Compare A with n.

    This is basically an A - n subtraction

    instruction but the results are thrown away.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero. (Set if A = n)

        N - Set.

        H - Set if no borrow from bit 4.

        C - Set for no borrow. (Set if A < n.)
Top
CPL           - Complement A register. (Flip all bits.)

    Flags affected:

        Z - Not affected.

        N - Set.

        H - Set.

        C - Not affected.
Top
DAA           - Decimal adjust register A.

    This instruction adjusts register A so that the

    correct representation of Binary Coded Decimal

    (BCD) is obtained.

    Flags affected:

        Z - Set if register A is zero.

        N - Not affected.

        H - Reset.

        C - Set of reset according to operation.
Top
DEC n         - Decrement register n.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Set.

        H - Set if no borrow from bit 4.

        C - Not affected.
Top
DEC nn        - Decrement register nn.

    nn = BC,DE,HL,SP

    Flags affected:

        None
Top
DI            - Disable interrupts.

    Flags affected:

        None
Top
EI            - Enable interrupts.

    This instruction enables the interrupts but not immediately.

    Interrupts are enabled after the instruction after EI is

    executed.

    Flags affected:

        None
Top
INC n         - Increment register n.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Set if carry from bit 3.

        C - Not affected.
Top
INC nn        - Increment register nn.

    n = BC,DE,HL,SP

    Flags affected:

        None
Top
JP n          - Jump to address n.

    n = two byte immediate value. (LSByte first)

    Flags affected:

        None
Top
JP cc,n       - Jump to address n if following condition

        is true:

    n = two byte immediate value. (LSByte first.)

    cc = NZ, Jump if Z flag is reset.

    cc = Z,  Jump if Z flag is set.

    cc = NC, Jump if C flag is reset.

    cc = C,  Jump if C flag is set.

    Flags affected:

        None
Top
JP [HL]       - Jump to address contained in HL.

    Flags affected:

        None
Top
JR n          - Add n to current address and jump to it.

    n = one byte signed immediate value.

    Flags affected:

        None
Top
JR cc,n       - If following condition is true then

        add n to current address and jump to it:

    n = one byte signed immediate value

    cc = NZ, Jump if Z flag is reset.

    cc = Z,  Jump if Z flag is set.

    cc = NC, Jump if C flag is reset.

    cc = C,  Jump if C flag is set.

    Flags affected:

        None
Top
HALT          - Power down CPU until an interrupt occurs.

    Flags affected:

        None
Top
LD A,n        - Put value n into A.

    n = A,B,C,D,E,H,L,(BC),(DE),(HL),(nnnn),#

    Flags affected:

        None
Top
LD n,A        - Put value A into n.

    n = A,B,C,D,E,H,L,(BC,(DE),(HL),(nnnn)

    Flags affected:

        None
Top
LD A,[C]      - Put value at address $FF00 + register C into A.

    Flags affected:

        None
Top
LD A,[HL+]    - Same as LD A,[HLI].
Top
LD A,[HL-]    - Same as LD A,[HLD].
Top
LD A,[HLI]    - Put value at address HL into A. Increment HL.

    Flags affected:

        None
Top
LD A,[HLD]    - Put value at address HL into A. Decrement HL.

    Flags affected:

        None
Top
LD [C],A      - Put A into address $FF00 + register C.

    Flags affected:

        None
Top
LD [HL+],A    - Same as LD [HLI],A.
Top
LD [HL-],A    - Same as LD [HLD],A.
Top
LD [HLI],A    - Put A into memory address HL. Increment HL.

    Flags affected:

        None
Top
LD [HLD],A    - Put A into memory address HL. Decrement HL.

    Flags affected:

        None
Top
LD r1,r2      - Put value r2 into r1.

    Flags affected:

        None
Top
LD n,nn       - Put value nn into n.

    n = BC,DE,HL,SP

    nn = 16 bit immediate value

    Flags affected:

        None
Top
LD HL,[SP+n]  - Put SP + n into HL.

    n = one byte signed immediate value

    Flags affected:

        Z - Reset.

        N - Reset.

        H - Set or reset according to operation.

        C - Set or reset according to operation.
Top
LD SP,HL      - Put HL into Stack Pointer (SP).

    Flags affected:

        None
Top
LD [n],SP     - Put Stack Pointer (SP) at address n.

    n = two byte immediate address

    Flags affected:

        None
Top
LDD A,[HL]    - Same as LD A,[HLD].
Top
LDD [HL],A    - Same as LD [HLD],A.
Top
LDH [n],A     - Put A into memory address $FF00 + n.

    n = one byte immediate value

    Flags affected:

        None
Top
LDH A,[n]     - Put memory address $FF00 + n into A.

    n = one byte immediate value

    Flags affected:

        None
Top
LDHL SP,n     - Same as LD HL,[SP+n]
Top
LDI A,[HL]    - Same as LD A,[HLI].
Top
LDI [HL],A    - Same as LD [HLI],A.
Top
NOP           - No operation.

    Flags affected:

        None
Top
OR n          - Logical OR n with register A, result in A.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Reset.
Top
POP nn        - Pop two bytes off stack into register pair nn.

        Increment Stack Pointer (SP) twice.

    nn = AF,BC,DE,HL

    Flags affected:

        None
Top
PUSH nn       - Push register pair nn onto stack.

        Decrement Stack Pointer (SP) twice.

    nn = AF,BC,DE,HL

    Flags affected:

        None
Top
RES b,r       - Reset bit b in register r.

    b = 0-7, r = A,B,C,D,E,H,L,(HL)

    Flags affected:

        None
Top
RET           - Pop two bytes from stack & jump to that address.

    Flags affected:

        None
Top
RET cc        - Return if following condition is true:

    cc = NZ, Return if Z flag is reset.

    cc = Z,  Return if Z flag is set.

    cc = NC, Return if C flag is reset.

    cc = C,  Return if C flag is set.

    Flags affected:

        None
Top
RETI          - Pop two bytes from stack & jump to that address

        then enable interrupts.

    Flags affected:

        None
Top
RL n          - Rotate n left through Carry flag.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Contains old bit 7 data.
Top
RLC n         - Rotate n left. Old bit 7 to Carry flag.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Contains old bit 7 data.
Top
RR n          - Rotate n right through Carry flag.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Contains old bit 0 data.
Top
RRC n         - Rotate n right. Old bit 0 to Carry flag.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Contains old bit 0 data.
Top
RST n         - Push present address onto stack.

        Jump to address $0000 + n.

    n = $00,$08,$10,$18,$20,$28,$30,$38

    Flags affected:

        None
Top
SBC A,n       - Subtract n + Carry flag from A.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero.

        N - Set.

        H - Set if no borrow from bit 4.

        C - Set if no borrow.
Top
SCF           - Set Carry flag.

    Flags affected:

        Z - Not affected.

        N - Reset.

        H - Reset.

        C - Set.
Top
SET b,r       - Set bit b in register r.

    b = 0-7, r = A,B,C,D,E,H,L,(HL)

    Flags affected:

        None
Top
SLA n         - Shift n left into Carry. LSBit of n set to 0.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Contains old bit 7 data.
Top
SRA n         - Shift n right into Carry. MSBit doesn't change.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Contains old bit 0 data.
Top
SRL n         - Shift n right into Carry. MSBit of n set to 0.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Contains old bit 0 data.
Top
STOP          - ???

    Flags affected:

        ?
Top
SUB n         - Subtract n from A.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero.

        N - Set.

        H - Set if no borrow from bit 4.

        C - Set if no borrow.
Top
SWAP n        - Swap upper & lower bits of n.

    n = A,B,C,D,E,H,L,(HL)

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Reset.
Top
XOR n         - Logical exclusive OR n with

        register A, result in A.

    n = A,B,C,D,E,H,L,(HL),#

    Flags affected:

        Z - Set if result is zero.

        N - Reset.

        H - Reset.

        C - Reset.
Top

All material on this page is Copyright (c) 1999 by col_deamon. All rights reserved.
Last updated: 11.25.99 09:46


ECE238Spr08/tutorials/GameBoyAudioSamples - CRATEL Wiki

  • Login
  • ECE238Spr08
  • tutorials
  • GameBoyAudioSamples
  • RecentChanges
  • FindPage
  • HelpContents
  • GameBoyAudioSamples
  • Immutable Page
  • Comments
  • Info
  • Attachments

Playing Audio Samples on the GameBoy

Ok, we are not talking audiophile sound here, but if you want to play short audio samples in 4-bit 8192Hz format, this is the tutorial for you...

General Concept

  • The GameBoy's sound channel 3 can play back audio samples

  • samples are stored in nibbles --- each byte stores 2 samples.

  • The GameBoy reads samples from wave pattern ram, located at $FF30-$FF3F

    • wave pattern ram is only 16 bytes so we can store only 32 samples at a time.
    • We cannot write to wave pattern ram when channel 3 is on so we must continually turn off channel 3 and refresh the wave pattern ram with new samples, then turn channel 3 back on.
  • this tutorial supports only very short samples of audio (~5 seconds max), because larger samples of audio would require more than 16K and therefore involves bank switching.

  • This tutorial includes the code to play the sample by continually updating the wave patterm ram. This code uses a simple delay loop to get the timing right, so using this code, the GameBoy cannot do anything else while playing the audio. However, it should be completely possible to alter the code to use the timer interrupt instead, which would allow for the GameBoy to perform other tasks seemingly concurrently

Steps involved

  • convert audio to nibbles for the GameBoy

  • import into code

Convert Audio into nibbles

Convert Audio from anything to 8 bit unsigned mono

There are probably many ways to do this. Here's how I did it:

  • load audio into Audacity (which is free and BTW awesome. I recommend the beta version)

  • convert to mono: from the tracks menu choose stereo to mono

  • resample to 8192Hz: from the tracks menu choose resample... and enter a sample rate of 8192

  • from the file menu choose export...

    • leave type as WAV, AIFF, and other uncompressed types

    • choose Options...

      • format: Other...
      • Header: Raw (header-less)
      • Encoding: Unsigned 8 bit PCM

    • choose a filename ending in .MOD. Ignore Audacity's warning not to use this extension

Convert Audio from 8 bit unsigned mono to GameBoy

  • download MegaMan_X's GameBoy Wave Converter

  • copy the .MOD file you created to the same directory where you downloaded the wave converter

  • go to the command line and change to the directory where both the .MOD file and the wave converter are located

  • type:

    snd2gbw <filename>.MOD <filename>.gbw

  • get the exact length in bytes of your .gbw. You'll need this when importing the code

    • find the .gbw file from Windows Explorer

    • right-click on the file and choose Properties...

    • you should see something like: Size:            x.xx KB (yy,yyy bytes)

    • yy,yyy is the number you want. For this example, make sure it is less than 16,383

Add audio and audio player to your source code

this is just an example, where I assume you are not using the upper 16K in a 32K GameBoy cartridge. You may need to tweak these directions for your use:

  • download snd-playback.asm and save it in the directory with your source code

  • copy the .gbw file to the directory where your source code is located.

  • In your source code, right before SECTION "start",HOME[$0100] (or the equivalent), add the following code:

    SECTION "raw-audio",CODE[$4000]
AUDIOBYTES      EQU     <add the exact length of bytes of your audio sample>
AUDIOLEN        EQU     AUDIOBYTES/16
RAWAUDIO:
                db      AUDIOLEN-((AUDIOLEN/256)*256)   ; LOW BYTE
                db      AUDIOLEN/256                    ; HIGH BYTE
                INCBIN  "<filename>.GBW"

  • somewhere after SECTION   "start",HOME[$0100] (immediately after would work), add:

    INCLUDE "snd-playback.asm" ; requires gbhw.inc

  • whereever you want the audio to play, add this to your code:

            ld      hl, RAWAUDIO
        call    snd_Sample1

And now...
Enjoy fine 4-bit 8192Hz sound

ECE238Spr08/tutorials/GameBoyAudioSamples (last edited 2010-08-04 16:50:46 by localhost)

  • Immutable Page
  • Comments
  • Info
  • Attachments
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01


Programmer-Net : The home of all programming information on the net

Wayback Machine
ОКТ | ИЮЛ | авг
Previous capture | 21 | Next capture
2008 | 2013 | 2014
40 captures
18 окт 00 - 21 июл 13
sparklines
Close Help

NEWS
+ Current News

GAMEBOY
+ Emulators
 + Tutorials
 + Cribsheet
 - Opcodes and Timings
 - Memory Map
 - Cartridge Header
 - Hardware Registers
 + Downloads
 + Links

PC
+ Source Code
- C++
- ASM
+ Links

CONTACT
+ Adrian@enliten.force9.co.uk
+ ICQ 8715274

You are visitor

Since 21st May 2000

Copyright (c) 1999, 2000 Adrian Brown
If you have any complaints about this website or its contents, please email me and I will remove or update the offending section.

Gameboy Hardware Registers

The memory between addresses FF00h and FF70 are the Input / Output ports of the Gameboy. Each byte in this memory range has a specific purpose. The follow table shows a list of these including information about them.

REGISTER | PURPOSE | COMMENT | ACCESS | BIT | ADDRESS
P1 | Read Joypad Info | P1F_5 P1F_4 P1F_3 P1F_2 P1F_1 P1F_0 | W W R R R R | 5 4 3 2 1 0 | FF00
SB | Serial Transfer Data |  | R/W |  | FF01
SC | Serial I/O Control |  | R/W |  | FF02
DIV | Timer Divider |  |  |  | FF04
TIMA | Timer Counter |  |  |  | FF05
TMA | Timer Modulo |  |  |  | FF06
TAC | Timer Control | Timer Start/Stop Timer Speed | R/W R/W | 2 0 - 1 | FF07
IF | Interrupt Flag |  | R/W |  | FF0F
NR10 | Audio Sweep | Sweep time Sweep increase/decrease Sweep shift | R/W R/W R/W | 4 - 6 3 0 - 2 | FF10
NR11 | Audio Channel #1 | Wave pattern duty Sound length data | R/W R/W | 6 - 7 0 - 5 | FF11
NR12 | Envelope Channel #1 | Initial value of envelope Envelope Up/Down Number of envelope sweep | R/W R/W R/W | 4 - 7 3 0 - 2 | FF12
NR13 | Sound Frequency #1 | Frequency LSB | W |  | FF13
NR14 | Sound Frequency #1 | Initialise Counter/Consecutive selection Frequency significant 3 bits | W W W | 7 6 0 - 2 | FF14
NR21 | Audio Channel #2 | Wave pattern duty Sound length data | R/W R/W | 6 - 7 0 - 5 | FF16
NR22 | Envelope Channel #2 | Initial value of envelope Envelope Up/Down Number of envelope sweep | R/W R/W R/W | 4 - 7 3 0 - 2 | FF17
NR23 | Sound Frequency #2 | Frequency LSB | W |  | FF18
NR24 | Sound Frequency #2 | Initialise Counter/Consecutive selection Frequency significant 3 bits | W W W | 7 6 0 - 2 | FF19
NR30 | Audio Channel #3 | Sound On/Off | R/W | 7 | FF1A
NR31 | Sound Length #3 | Sound Length | R/W |  | FF1B
NR32 | Volume #3 | Select output level | R/W | 5 - 6 | FF1C
NR33 | Sound Frequency #3 | Frequency LSB | W |  | FF1D
NR34 | Sound Frequency #3 | Initialise Counter/Consecutive selection Frequency significant 3 bits | W W W | 7 6 0 - 2 | FF1E
NR41 | Sound Length #4 | Sound Length | R/W | 0 - 5 | FF20
NR42 | Envelope Channel #4 | Initial value of envelope Envelope Up/Down Number of envelope sweep | R/W R/W R/W | 4 - 7 3 0 - 2 | FF21
NR43 | Audio Counter | Frequency of polynomial counter Polynomial counter's step Dividing ratio of frequency | R/W R/W R/W | 4 - 7 3 0 - 2 | FF22
NR44 | Audio Counter | Initialise audio Counter/Consecutive selection | R/W R/W | 7 6 | FF23
NR50 | Channel Control | Vin SO2 On/Off SO2 output volume Vin SO1 On/Off SO1 output volume | R/W R/W R/W R/W | 7 4 - 6 3 0 - 2 | FF24
NR51 | Sound Output | Output sound 4 to SO2 Output sound 3 to SO2 Output sound 2 to SO2 Output sound 1 to SO2 Output sound 4 to SO1 Output sound 3 to SO1 Output sound 2 to SO1 Output sound 1 to SO1 | R/W R/W R/W R/W R/W R/W R/W R/W | 7 6 5 4 3 2 1 0 | FF25
NR52 | Sound On/Off | All Channels On/Off Channel #4 On/Off Channel #3 On/Off Channel #2 On/Off Channel #1 On/Off | R/W R/W R/W R/W R/W | 7 3 2 1 0 | FF26
AUD3WAVERAM | Wave Data | Sound sample RAM (16 bytes) | R/W |  | FF30 - FF3F
LCDC | LCD Control | LCD On/Off Window Address Window On/Off Background Address Object Size Object On/Off Background On/Off | R/W R/W R/W R/W R/W R/W R/W | 7 6 5 3 - 4 2 1 0 | FF40
STAT | LCD Status | LYCEQULY Coincidence Mode 10 Mode 01 (V-Blank) Mode 00 (H-Blank) Coincidence Flag OAM/VRAM Lock | R/W R/W R/W R/W R/W R/W | 6 5 4 3 2 1 - 0 | FF41
SCY | Scroll Screen Y | Vertical Scroll | R/W |  | FF42
SCX | Scroll Screen X | Horizontal Scroll | R/W |  | FF43
LY | LCDC Y-Coord |  | R/W |  | FF44
LYC | LY Compare |  | R/W |  | FF45
DMA | DMA Transfer |  | R/W |  | FF46
BGP | BG Palette Data |  | R/W |  | FF47
OBP0 | Object Palette 0 Data |  | R/W |  | FF48
OBP1 | Object Palette 1 Data |  | R/W |  | FF49
WY | Window Y Position |  | R/W |  | FF4A
WX | Window X Position |  | R/W |  | FF4B
KEY1 | CPU Speed Select | GBC only | R/W |  | FF4D
VBK | VRAM Bank Select | GBC only | R/W |  | FF4F
HDMA1 | HBL General DMA | GBC only | R/W |  | FF51
HDMA2 | HBL General DMA | GBC only | R/W |  | FF52
HDMA3 | HBL General DMA | GBC only | R/W |  | FF53
HDMA4 | HBL General DMA | GBC only | R/W |  | FF54
HDMA5 | HBL General DMA | GBC only | R/W |  | FF55
RP | Infrared Comms | GBC only | R/W |  | FF56
BCPS | BG Colour Index | GBC only | R/W |  | FF68
BCPD | BG Colour Data | GBC only | R/W |  | FF69
OCPS | Object Colour Index | GBC only | R/W |  | FF6A
OCPD | Object Colour Data | GBC only | R/W |  | FF6B
SVBK | RAM Bank Select | GBC only | R/W |  | FF70
IE | Interrupt Enable | HILO Transition Serial I/O Transfer Done Timer Overflow LCDC VBL | R/W R/W R/W R/W R/W | 4 3 2 1 0 | FFFF


Specifications

Wayback Machine
НОЯ | ДЕК | ЯНВ
Previous capture | 21 | Next capture
2012 | 2013 | 2014
50 captures
8 фев 05 - 5 ноя 14
sparklines
Close Help
Pan Docs

Overview
 About the Pan Docs
Game Boy Technical Data
Memory Map

I/O Ports
 Video Display
Sound Controller
Joypad Input
Serial Data Transfer (Link Cable)
Timer and Divider Registers
Interrupts
CGB Registers
SGB Functions

CPU Specifications
 CPU Registers and Flags
CPU Instruction Set
CPU Comparision with Z80

Cartridges
 The Cartridge Header
Memory Bank Controllers
Gamegenie/Shark Cheats

Other
 Power Up Sequence
Reducing Power Consumption
Sprite RAM Bug
External Connectors

About the Pan Docs

 =================================================================
       Everything You Always Wanted To Know About GAMEBOY *
 =================================================================

                     * but were afraid to ask

        Pan of -ATX- Document Updated by contributions from:
     Marat Fayzullin, Pascal Felber, Paul Robson, Martin Korth
             CPU, SGB, CGB, AUX specs by Martin Korth

                  Last updated 10/2001 by nocash
               Previously updated 4-Mar-98 by kOOPa

Forward
The following was typed up for informational purposes regarding the inner workings on the hand-held game machine known as GameBoy, manufactured and designed by Nintendo Co., LTD. This info is presented to inform a user on how their Game Boy works and what makes it "tick". GameBoy is copyrighted by Nintendo Co., LTD. Any reference to copyrighted material is not presented for monetary gain, but for educational purposes and higher learning.

Available Document Formats
The present version of this document is available in Text and Html format:
  http://www.work.de/nocash/pandocs.txt
  http://www.work.de/nocash/pandocs.htm
Also, a copy of this document is included in the manual of newer versions of the no$gmb debugger, because of recent piracy attacks (many thanks and best wishes go to hell) I have currently no intention to publish any such or further no$gmb updates though.

Game Boy Technical Data

  CPU          - 8-bit (Similar to the Z80 processor)
  Clock Speed  - 4.194304MHz (4.295454MHz for SGB, max. 8.4MHz for CGB)
  Work RAM     - 8K Byte (32K Byte for CGB)
  Video RAM    - 8K Byte (16K Byte for CGB)
  Screen Size  - 2.6"
  Resolution   - 160x144 (20x18 tiles)
  Max sprites  - Max 40 per screen, 10 per line
  Sprite sizes - 8x8 or 8x16
  Palettes     - 1x4 BG, 2x3 OBJ (for CGB: 8x4 BG, 8x3 OBJ)
  Colors       - 4 grayshades (32768 colors for CGB)
  Horiz Sync   - 9198 KHz (9420 KHz for SGB)
  Vert Sync    - 59.73 Hz (61.17 Hz for SGB)
  Sound        - 4 channels with stereo sound
  Power        - DC6V 0.7W (DC3V 0.7W for GB Pocket, DC3V 0.6W for CGB)

Memory Map

The gameboy is having a 16bit address bus, that is used to address ROM, RAM, and I/O registers.

General Memory Map

  0000-3FFF   16KB ROM Bank 00     (in cartridge, fixed at bank 00)
  4000-7FFF   16KB ROM Bank 01..NN (in cartridge, switchable bank number)
  8000-9FFF   8KB Video RAM (VRAM) (switchable bank 0-1 in CGB Mode)
  A000-BFFF   8KB External RAM     (in cartridge, switchable bank, if any)
  C000-CFFF   4KB Work RAM Bank 0 (WRAM)
  D000-DFFF   4KB Work RAM Bank 1 (WRAM)  (switchable bank 1-7 in CGB Mode)
  E000-FDFF   Same as C000-DDFF (ECHO)    (typically not used)
  FE00-FE9F   Sprite Attribute Table (OAM)
  FEA0-FEFF   Not Usable
  FF00-FF7F   I/O Ports
  FF80-FFFE   High RAM (HRAM)
  FFFF        Interrupt Enable Register

Jump Vectors in First ROM Bank
The following addresses are supposed to be used as jump vectors:
  0000,0008,0010,0018,0020,0028,0030,0038   for RST commands
  0040,0048,0050,0058,0060                  for Interrupts
However, the memory may be used for any other purpose in case that your program doesn't use any (or only some) RST commands or Interrupts. RST commands are 1-byte opcodes that work similiar to CALL opcodes, except that the destination address is fixed.

Cartridge Header in First ROM Bank
The memory at 0100-014F contains the cartridge header. This area contains information about the program, its entry point, checksums, information about the used MBC chip, the ROM and RAM sizes, etc. Most of the bytes in this area are required to be specified correctly. For more information read the chapter about The Cartridge Header.

External Memory and Hardware
The areas from 0000-7FFF and A000-BFFF may be used to connect external hardware. The first area is typically used to address ROM (read only, of course), cartridges with Memory Bank Controllers (MBCs) are additionally using this area to output data (write only) to the MBC chip. The second area is often used to address external RAM, or to address other external hardware (Real Time Clock, etc). External memory is often battery buffered, and may hold saved game positions and high scrore tables (etc.) even when the gameboy is turned of, or when the cartridge is removed. For specific information read the chapter about Memory Bank Controllers.

Video Display

Video I/O Registers
 LCD Control Register
LCD Status Register
LCD Interrupts
LCD Position and Scrolling
LCD Monochrome Palettes
LCD Color Palettes (CGB only)
LCD VRAM Bank (CGB only)
LCD OAM DMA Transfers
LCD VRAM DMA Transfers (CGB only)

Video Memory
 VRAM Tile Data
VRAM Background Maps
VRAM Sprite Attribute Table (OAM)
Accessing VRAM and OAM

LCD Control Register

FF40 - LCDC - LCD Control (R/W)

  Bit 7 - LCD Display Enable             (0=Off, 1=On)
  Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
  Bit 5 - Window Display Enable          (0=Off, 1=On)
  Bit 4 - BG & Window Tile Data Select   (0=8800-97FF, 1=8000-8FFF)
  Bit 3 - BG Tile Map Display Select     (0=9800-9BFF, 1=9C00-9FFF)
  Bit 2 - OBJ (Sprite) Size              (0=8x8, 1=8x16)
  Bit 1 - OBJ (Sprite) Display Enable    (0=Off, 1=On)
  Bit 0 - BG Display (for CGB see below) (0=Off, 1=On)

LCDC.7 - LCD Display Enable
CAUTION: Stopping LCD operation (Bit 7 from 1 to 0) may be performed during V-Blank ONLY, disabeling the display outside of the V-Blank period may damage the hardware. This appears to be a serious issue, Nintendo is reported to reject any games that do not follow this rule.
V-blank can be confirmed when the value of LY is greater than or equal to 144. When the display is disabled the screen is blank (white), and VRAM and OAM can be accessed freely.

--- LCDC.0 has different Meanings depending on Gameboy Type ---

LCDC.0 - 1) Monochrome Gameboy and SGB: BG Display
When Bit 0 is cleared, the background becomes blank (white). Window and Sprites may still be displayed (if enabled in Bit 1 and/or Bit 5).

LCDC.0 - 2) CGB in CGB Mode: BG and Window Master Priority
When Bit 0 is cleared, the background and window lose their priority - the sprites will be always displayed on top of background and window, independently of the priority flags in OAM and BG Map attributes.

LCDC.0 - 3) CGB in Non CGB Mode: BG and Window Display
When Bit 0 is cleared, both background and window become blank (white), ie. the Window Display Bit (Bit 5) is ignored in that case. Only Sprites may still be displayed (if enabled in Bit 1).
This is a possible compatibility problem - any monochrome games (if any) that disable the background, but still want to display the window wouldn't work properly on CGBs.

LCD Status Register

FF41 - STAT - LCDC Status (R/W)

  Bit 6 - LYC=LY Coincidence Interrupt (1=Enable) (Read/Write)
  Bit 5 - Mode 2 OAM Interrupt         (1=Enable) (Read/Write)
  Bit 4 - Mode 1 V-Blank Interrupt     (1=Enable) (Read/Write)
  Bit 3 - Mode 0 H-Blank Interrupt     (1=Enable) (Read/Write)
  Bit 2 - Coincidence Flag  (0:LYC<>LY, 1:LYC=LY) (Read Only)
  Bit 1-0 - Mode Flag       (Mode 0-3, see below) (Read Only)
            0: During H-Blank
            1: During V-Blank
            2: During Searching OAM-RAM
            3: During Transfering Data to LCD Driver

The two lower STAT bits show the current status of the LCD controller.
  Mode 0: The LCD controller is in the H-Blank period and
          the CPU can access both the display RAM (8000h-9FFFh)
          and OAM (FE00h-FE9Fh)

  Mode 1: The LCD contoller is in the V-Blank period (or the
          display is disabled) and the CPU can access both the
          display RAM (8000h-9FFFh) and OAM (FE00h-FE9Fh)

  Mode 2: The LCD controller is reading from OAM memory.
          The CPU <cannot> access OAM memory (FE00h-FE9Fh)
          during this period.

  Mode 3: The LCD controller is reading from both OAM and VRAM,
          The CPU <cannot> access OAM and VRAM during this period.
          CGB Mode: Cannot access Palette Data (FF69,FF6B) either.

The following are typical when the display is enabled:
  Mode 2  2_____2_____2_____2_____2_____2___________________2____
  Mode 3  _33____33____33____33____33____33__________________3___
  Mode 0  ___000___000___000___000___000___000________________000
  Mode 1  ____________________________________11111111111111_____

The Mode Flag goes through the values 0, 2, and 3 at a cycle of about 109uS. 0 is present about 48.6uS, 2 about 19uS, and 3 about 41uS. This is interrupted every 16.6ms by the VBlank (1). The mode flag stays set at 1 for about 1.08 ms.

Mode 0 is present between 201-207 clks, 2 about 77-83 clks, and 3 about 169-175 clks. A complete cycle through these states takes 456 clks. VBlank lasts 4560 clks. A complete screen refresh occurs every 70224 clks.)

LCD Interrupts

INT 40 - V-Blank Interrupt
The V-Blank interrupt occurs ca. 59.7 times a second on a regular GB and ca. 61.1 times a second on a Super GB (SGB). This interrupt occurs at the beginning of the V-Blank period (LY=144).
During this period video hardware is not using video ram so it may be freely accessed. This period lasts approximately 1.1 milliseconds.

INT 48 - LCDC Status Interrupt
There are various reasons for this interrupt to occur as described by the STAT register ($FF40). One very popular reason is to indicate to the user when the video hardware is about to redraw a given LCD line. This can be useful for dynamically controlling the SCX/SCY registers ($FF43/$FF42) to perform special video effects.

LCD Position and Scrolling

FF42 - SCY - Scroll Y (R/W)
FF43 - SCX - Scroll X (R/W)
Specifies the position in the 256x256 pixels BG map (32x32 tiles) which is to be displayed at the upper/left LCD display position.
Values in range from 0-255 may be used for X/Y each, the video controller automatically wraps back to the upper (left) position in BG map when drawing exceeds the lower (right) border of the BG map area.

FF44 - LY - 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.

FF45 - LYC - LY Compare (R/W)
The gameboy permanently compares the value of the LYC and LY registers. When both values are identical, the coincident bit in the STAT register becomes set, and (if enabled) a STAT interrupt is requested.

FF4A - WY - Window Y Position (R/W)
FF4B - WX - Window X Position minus 7 (R/W)
Specifies the upper/left positions of the Window area. (The window is an alternate background area which can be displayed above of the normal background. OBJs (sprites) may be still displayed above or behinf the window, just as for normal BG.)
The window becomes visible (if enabled) when positions are set in range WX=0..166, WY=0..143. A postion of WX=7, WY=0 locates the window at upper left, it is then completly covering normal background.

LCD Monochrome Palettes

FF47 - BGP - BG Palette Data (R/W) - Non CGB Mode Only
This register assigns gray shades to the color numbers of the BG and Window tiles.
  Bit 7-6 - Shade for Color Number 3
  Bit 5-4 - Shade for Color Number 2
  Bit 3-2 - Shade for Color Number 1
  Bit 1-0 - Shade for Color Number 0
The four possible gray shades are:
  0  White
  1  Light gray
  2  Dark gray
  3  Black
In CGB Mode the Color Palettes are taken from CGB Palette Memory instead.

FF48 - OBP0 - Object Palette 0 Data (R/W) - Non CGB Mode Only
This register assigns gray shades for sprite palette 0. It works exactly as BGP (FF47), except that the lower two bits aren't used because sprite data 00 is transparent.

FF49 - OBP1 - Object Palette 1 Data (R/W) - Non CGB Mode Only
This register assigns gray shades for sprite palette 1. It works exactly as BGP (FF47), except that the lower two bits aren't used because sprite data 00 is transparent.

LCD Color Palettes (CGB only)

FF68 - BCPS/BGPI - CGB Mode Only - Background Palette Index
This register is used to address a byte in the CGBs Background Palette Memory. Each two byte in that memory define a color value. The first 8 bytes define Color 0-3 of Palette 0 (BGP0), and so on for BGP1-7.
  Bit 0-5   Index (00-3F)
  Bit 7     Auto Increment  (0=Disabled, 1=Increment after Writing)
Data can be read/written to/from the specified index address through Register FF69. When the Auto Increment Bit is set then the index is automatically incremented after each <write> to FF69. Auto Increment has no effect when <reading> from FF69, so the index must be manually incremented in that case.

FF69 - BCPD/BGPD - CGB Mode Only - Background Palette Data
This register allows to read/write data to the CGBs Background Palette Memory, addressed through Register FF68.
Each color is defined by two bytes (Bit 0-7 in first byte).
  Bit 0-4   Red Intensity   (00-1F)
  Bit 5-9   Green Intensity (00-1F)
  Bit 10-14 Blue Intensity  (00-1F)
Much like VRAM, Data in Palette Memory cannot be read/written during the time when the LCD Controller is reading from it. (That is when the STAT register indicates Mode 3).
Note: Initially all background colors are initialized as white.

FF6A - OCPS/OBPI - CGB Mode Only - Sprite Palette Index
FF6B - OCPD/OBPD - CGB Mode Only - Sprite Palette Data
These registers are used to initialize the Sprite Palettes OBP0-7, identically as described above for Background Palettes. Note that four colors may be defined for each OBP Palettes - but only Color 1-3 of each Sprite Palette can be displayed, Color 0 is always transparent, and can be initialized to a don't care value.
Note: Initially all sprite colors are uninitialized.

RGB Translation by CGBs
When developing graphics on PCs, note that the RGB values will have different appearance on CGB displays as on VGA monitors:
The highest intensity will produce Light Gray color rather than White. The intensities are not linear; the values 10h-1Fh will all appear very bright, while medium and darker colors are ranged at 00h-0Fh.
The CGB display will mix colors quite oddly, increasing intensity of only one R,G,B color will also influence the other two R,G,B colors.
For example, a color setting of 03EFh (Blue=0, Green=1Fh, Red=0Fh) will appear as Neon Green on VGA displays, but on the CGB it'll produce a decently washed out Yellow.

RGB Translation by GBAs
Even though GBA is described to be compatible to CGB games, most CGB games are completely unplayable on GBAs because most colors are invisible (black). Of course, colors such like Black and White will appear the same on both CGB and GBA, but medium intensities are arranged completely different.
Intensities in range 00h..0Fh are invisible/black (unless eventually under best sunlight circumstances, and when gazing at the screen under obscure viewing angles), unfortunately, these intensities are regulary used by most existing CGB games for medium and darker colors.
Newer CGB games may avoid this effect by changing palette data when detecting GBA hardware. A relative simple method would be using the formula GBA=CGB/2+10h for each R,G,B intensity, probably the result won't be perfect, and (once colors became visible) it may turn out that the color mixing is different also, anyways, it'd be still ways better than no conversion.
Asides, this translation method should have been VERY easy to implement in GBA hardware directly, even though Nintendo obviously failed to do so. How did they say, This seal is your assurance for excellence in workmanship and so on?

LCD VRAM Bank (CGB only)

FF4F - VBK - CGB Mode Only - VRAM Bank
This 1bit register selects the current Video Memory (VRAM) Bank.
  Bit 0 - VRAM Bank (0-1)
Bank 0 contains 192 Tiles, and two background maps, just as for monochrome games. Bank 1 contains another 192 Tiles, and color attribute maps for the background maps in bank 0.

LCD OAM DMA Transfers

FF46 - DMA - DMA Transfer and Start Address (W)
Writing to this register launches a DMA transfer from ROM or RAM to OAM memory (sprite attribute table). The written value specifies the transfer source address divided by 100h, ie. source & destination are:
  Source:      XX00-XX9F   ;XX in range from 00-F1h
  Destination: FE00-FE9F
It takes 160 microseconds until the transfer has completed (80 microseconds in CGB Double Speed Mode), during this time the CPU can access only HRAM (memory at FF80-FFFE). For this reason, the programmer must copy a short procedure into HRAM, and use this procedure to start the transfer from inside HRAM, and wait until the transfer has finished:
   ld  (0FF46h),a ;start DMA transfer, a=start address/100h
   ld  a,28h      ;delay...
  wait:           ;total 5x40 cycles, approx 200ms
   dec a          ;1 cycle
   jr  nz,wait    ;4 cycles
Most programs are executing this procedure from inside of their VBlank procedure, but it is possible to execute it during display redraw also, allowing to display more than 40 sprites on the screen (ie. for example 40 sprites in upper half, and other 40 sprites in lower half of the screen).

LCD VRAM DMA Transfers (CGB only)

FF51 - HDMA1 - CGB Mode Only - New DMA Source, High
FF52 - HDMA2 - CGB Mode Only - New DMA Source, Low
FF53 - HDMA3 - CGB Mode Only - New DMA Destination, High
FF54 - HDMA4 - CGB Mode Only - New DMA Destination, Low
FF55 - HDMA5 - CGB Mode Only - New DMA Length/Mode/Start
These registers are used to initiate a DMA transfer from ROM or RAM to VRAM. The Source Start Address may be located at 0000-7FF0 or A000-DFF0, the lower four bits of the address are ignored (treated as zero). The Destination Start Address may be located at 8000-9FF0, the lower four bits of the address are ignored (treated as zero), the upper 3 bits are ignored either (destination is always in VRAM).

Writing to FF55 starts the transfer, the lower 7 bits of FF55 specify the Transfer Length (divided by 10h, minus 1). Ie. lengths of 10h-800h bytes can be defined by the values 00h-7Fh. And the upper bit of FF55 indicates the Transfer Mode:

Bit7=0 - General Purpose DMA
When using this transfer method, all data is transferred at once. The execution of the program is halted until the transfer has completed. Note that the General Purpose DMA blindly attempts to copy the data, even if the LCD controller is currently accessing VRAM. So General Purpose DMA should be used only if the Display is disabled, or during V-Blank, or (for rather short blocks) during H-Blank.
The execution of the program continues when the transfer has been completed, and FF55 then contains a value if FFh.

Bit7=1 - H-Blank DMA
The H-Blank DMA transfers 10h bytes of data during each H-Blank, ie. at LY=0-143, no data is transferred during V-Blank (LY=144-153), but the transfer will then continue at LY=00. The execution of the program is halted during the separate transfers, but the program execution continues during the 'spaces' between each data block.
Note that the program may not change the Destination VRAM bank (FF4F), or the Source ROM/RAM bank (in case data is transferred from bankable memory) until the transfer has completed!
Reading from Register FF55 returns the remaining length (divided by 10h, minus 1), a value of 0FFh indicates that the transfer has completed. It is also possible to terminate an active H-Blank transfer by writing zero to Bit 7 of FF55. In that case reading from FF55 may return any value for the lower 7 bits, but Bit 7 will be read as "1".

Confirming if the DMA Transfer is Active
Reading Bit 7 of FF55 can be used to confirm if the DMA transfer is active (1=Not Active, 0=Active). This works under any circumstances - after completion of General Purpose, or H-Blank Transfer, and after manually terminating a H-Blank Transfer.

Transfer Timings
In both Normal Speed and Double Speed Mode it takes about 8us to transfer a block of 10h bytes. That are 8 cycles in Normal Speed Mode, and 16 'fast' cycles in Double Speed Mode.
Older MBC controllers (like MBC1-4) and slower ROMs are not guaranteed to support General Purpose or H-Blank DMA, that's because there are always 2 bytes transferred per microsecond (even if the itself program runs it Normal Speed Mode).

VRAM Tile Data

Tile Data is stored in VRAM at addresses 8000h-97FFh, this area defines the Bitmaps for 192 Tiles. In CGB Mode 384 Tiles can be defined, because memory at 0:8000h-97FFh and at 1:8000h-97FFh is used.

Each tile is sized 8x8 pixels and has a color depth of 4 colors/gray shades. Tiles can be displayed as part of the Background/Window map, and/or as OAM tiles (foreground sprites). Note that foreground sprites may have only 3 colors, because color 0 is transparent.

As it was said before, there are two Tile Pattern Tables at $8000-8FFF and at $8800-97FF. The first one can be used for sprites and the background. Its tiles are numbered from 0 to 255. The second table can be used for the background and the window display and its tiles are numbered from -128 to 127.

Each Tile occupies 16 bytes, where each 2 bytes represent a line:
  Byte 0-1  First Line (Upper 8 pixels)
  Byte 2-3  Next Line
  etc.
For each line, the first byte defines the least significant bits of the color numbers for each pixel, and the second byte defines the upper bits of the color numbers. In either case, Bit 7 is the leftmost pixel, and Bit 0 the rightmost.

So, each pixel is having a color number in range from 0-3. The color numbers are translated into real colors (or gray shades) depending on the current palettes. The palettes are defined through registers FF47-FF49 (Non CGB Mode), and FF68-FF6B (CGB Mode).

VRAM Background Maps

The gameboy contains two 32x32 tile background maps in VRAM at addresses 9800h-9BFFh and 9C00h-9FFFh. Each can be used either to display "normal" background, or "window" background.

BG Map Tile Numbers
An area of VRAM known as Background Tile Map contains the numbers of tiles to be displayed. It is organized as 32 rows of 32 bytes each. Each byte contains a number of a tile to be displayed. Tile patterns are taken from the Tile Data Table located either at $8000-8FFF or $8800-97FF. In the first case, patterns are numbered with unsigned numbers from 0 to 255 (i.e. pattern #0 lies at address $8000). In the second case, patterns have signed numbers from -128 to 127 (i.e. pattern #0 lies at address $9000). The Tile Data Table address for the background can be selected via LCDC register.

BG Map Attributes (CGB Mode only)
In CGB Mode, an additional map of 32x32 bytes is stored in VRAM Bank 1 (each byte defines attributes for the corresponding tile-number map entry in VRAM Bank 0):
  Bit 0-2  Background Palette number  (BGP0-7)
  Bit 3    Tile VRAM Bank number      (0=Bank 0, 1=Bank 1)
  Bit 4    Not used
  Bit 5    Horizontal Flip            (0=Normal, 1=Mirror horizontally)
  Bit 6    Vertical Flip              (0=Normal, 1=Mirror vertically)
  Bit 7    BG-to-OAM Priority         (0=Use OAM priority bit, 1=BG Priority)
When Bit 7 is set, the corresponding BG tile will have priority above all OBJs (regardless of the priority bits in OAM memory). There's also an Master Priority flag in LCDC register Bit 0 which overrides all other priority bits when cleared.

As one background tile has a size of 8x8 pixels, the BG maps may hold a picture of 256x256 pixels, an area of 160x144 pixels of this picture can be displayed on the LCD screen.

Normal Background (BG)
The SCY and SCX registers can be used to scroll the background, allowing to select the origin of the visible 160x144 pixel area within the total 256x256 pixel background map. Background wraps around the screen (i.e. when part of it goes off the screen, it appears on the opposite side.)

The Window
Besides background, there is also a "window" overlaying the background. The window is not scrollable i.e. it is always displayed starting from its left upper corner. The location of a window on the screen can be adjusted via WX and WY registers. Screen coordinates of the top left corner of a window are WX-7,WY. The tiles for the window are stored in the Tile Data Table. Both the Background and the window share the same Tile Data Table.

Both background and window can be disabled or enabled separately via bits in the LCDC register.

VRAM Sprite Attribute Table (OAM)

GameBoy video controller can display up to 40 sprites either in 8x8 or in 8x16 pixels. Because of a limitation of hardware, only ten sprites can be displayed per scan line. Sprite patterns have the same format as BG tiles, but they are taken from the Sprite Pattern Table located at $8000-8FFF and have unsigned numbering.

Sprite attributes reside in the Sprite Attribute Table (OAM - Object Attribute Memory) at $FE00-FE9F. Each of the 40 entries consists of four bytes with the following meanings:

Byte0 - Y Position
Specifies the sprites vertical position on the screen (minus 16).
An offscreen value (for example, Y=0 or Y>=160) hides the sprite.

Byte1 - X Position
Specifies the sprites horizontal position on the screen (minus 8).
An offscreen value (X=0 or X>=168) hides the sprite, but the sprite
still affects the priority ordering - a better way to hide a sprite is to set its Y-coordinate offscreen.

Byte2 - Tile/Pattern Number
Specifies the sprites Tile Number (00-FF). This (unsigned) value selects a tile from memory at 8000h-8FFFh. In CGB Mode this could be either in VRAM Bank 0 or 1, depending on Bit 3 of the following byte.
In 8x16 mode, the lower bit of the tile number is ignored. Ie. the upper 8x8 tile is "NN AND FEh", and the lower 8x8 tile is "NN OR 01h".

Byte3 - Attributes/Flags:

  Bit7   OBJ-to-BG Priority (0=OBJ Above BG, 1=OBJ Behind BG color 1-3)
         (Used for both BG and Window. BG color 0 is always behind OBJ)
  Bit6   Y flip          (0=Normal, 1=Vertically mirrored)
  Bit5   X flip          (0=Normal, 1=Horizontally mirrored)
  Bit4   Palette number  **Non CGB Mode Only** (0=OBP0, 1=OBP1)
  Bit3   Tile VRAM-Bank  **CGB Mode Only**     (0=Bank 0, 1=Bank 1)
  Bit2-0 Palette number  **CGB Mode Only**     (OBP0-7)

Sprite Priorities and Conflicts
When sprites with different x coordinate values overlap, the one with the smaller x coordinate (closer to the left) will have priority and appear above any others. This applies in Non CGB Mode only.
When sprites with the same x coordinate values overlap, they have priority according to table ordering. (i.e. $FE00 - highest, $FE04 - next highest, etc.) In CGB Mode priorities are always assigned like this.

Only 10 sprites can be displayed on any one line. When this limit is exceeded, the lower priority sprites (priorities listed above) won't be displayed. To keep unused sprites from affecting onscreen sprites set their Y coordinate to Y=0 or Y=>144+16. Just setting the X coordinate to X=0 or X=>160+8 on a sprite will hide it but it will still affect other sprites sharing the same lines.

Writing Data to OAM Memory
The recommened method is to write the data to normal RAM first, and to copy that RAM to OAM by using the DMA transfer function, initiated through DMA register (FF46).
Beside for that, it is also possible to write data directly to the OAM area by using normal LD commands, this works only during the H-Blank and V-Blank periods. The current state of the LCD controller can be read out from the STAT register (FF41).

Accessing VRAM and OAM

CAUTION
When the LCD Controller is drawing the screen it is directly reading from Video Memory (VRAM) and from the Sprite Attribute Table (OAM). During these periods the Gameboy CPU may not access the VRAM and OAM. That means, any attempts to write to VRAM/OAM are ignored (the data remains unchanged). And any attempts to read from VRAM/OAM will return undefined data (typically a value of FFh).

For this reason the program should verify if VRAM/OAM is accessable before actually reading or writing to it. This is usually done by reading the Mode Bits from the STAT Register (FF41). When doing this (as described in the examples below) you should take care that no interrupts occur between the wait loops and the following memory access - the memory is guaranted to be accessable only for a few cycles directly after the wait loops have completed.

VRAM (memory at 8000h-9FFFh) is accessable during Mode 0-2

  Mode 0 - H-Blank Period,
  Mode 1 - V-Blank Period, and
  Mode 2 - Searching OAM Period
A typical procedure that waits for accessibility of VRAM would be:
  ld   hl,0FF41h    ;-STAT Register
 @@wait:            ;\
  bit  1,(hl)       ; Wait until Mode is 0 or 1
  jr   nz,@@wait    ;/
Even if the procedure gets executed at the <end> of Mode 0 or 1, it is still proof to assume that VRAM can be accessed for a few more cycles because in either case the following period is Mode 2 which allows access to VRAM either.
In CGB Mode an alternate method to write data to VRAM is to use the HDMA Function (FF51-FF55).

OAM (memory at FE00h-FE9Fh) is accessable during Mode 0-1

  Mode 0 - H-Blank Period, and
  Mode 1 - V-Blank Period
Beside for that, OAM can be accessed at any time by using the DMA Function (FF46). When directly reading or writing to OAM, a typical procedure that waits for accessibilty or OAM Memory would be:
  ld   hl,0FF41h    ;-STAT Register
 @@wait1:           ;\
  bit  1,(hl)       ; Wait until Mode is -NOT- 0 or 1
  jr   z,@@wait1    ;/
 @@wait2:           ;\
  bit  1,(hl)       ; Wait until Mode 0 or 1 -BEGINS-
  jr   nz,@@wait2   ;/
The two wait loops ensure that Mode 0 or 1 will last for a few clock cycles after completion of the procedure. In V-Blank period it might be recommended to skip the whole procedure - and in most cases using the above mentioned DMA function would be more recommended anyways.

Note
When the display is disabled, both VRAM and OAM are accessable at any time. The downside is that the screen is blank (white) during this period, so that disabling the display would be recommended only during initialization.

Sound Controller

Sound Overview
Sound Channel 1 - Tone & Sweep
Sound Channel 2 - Tone
Sound Channel 3 - Wave Output
Sound Channel 4 - Noise
Sound Control Registers

Sound Overview

There are two sound channels connected to the output terminals SO1 and SO2. There is also a input terminal Vin connected to the cartridge. It can be routed to either of both output terminals. GameBoy circuitry allows producing sound in four different ways:

   Quadrangular wave patterns with sweep and envelope functions.
   Quadrangular wave patterns with envelope functions.
   Voluntary wave patterns from wave RAM.
   White noise with an envelope function.

These four sounds can be controlled independantly and then mixed separately for each of the output terminals.

Sound registers may be set at all times while producing sound.

(Sounds will have a 2.4% higher frequency on Super GB.)

Sound Channel 1 - Tone & Sweep

FF10 - NR10 - Channel 1 Sweep register (R/W)

  Bit 6-4 - Sweep Time
  Bit 3   - Sweep Increase/Decrease
             0: Addition    (frequency increases)
             1: Subtraction (frequency decreases)
  Bit 2-0 - Number of sweep shift (n: 0-7)
Sweep Time:
  000: sweep off - no freq change
  001: 7.8 ms  (1/128Hz)
  010: 15.6 ms (2/128Hz)
  011: 23.4 ms (3/128Hz)
  100: 31.3 ms (4/128Hz)
  101: 39.1 ms (5/128Hz)
  110: 46.9 ms (6/128Hz)
  111: 54.7 ms (7/128Hz)

The change of frequency (NR13,NR14) at each shift is calculated by the following formula where X(0) is initial freq & X(t-1) is last freq:
  X(t) = X(t-1) +/- X(t-1)/2^n

FF11 - NR11 - Channel 1 Sound length/Wave pattern duty (R/W)

  Bit 7-6 - Wave Pattern Duty (Read/Write)
  Bit 5-0 - Sound length data (Write Only) (t1: 0-63)
Wave Duty:
  00: 12.5% ( _-------_-------_------- )
  01: 25%   ( __------__------__------ )
  10: 50%   ( ____----____----____---- ) (normal)
  11: 75%   ( ______--______--______-- )
Sound Length = (64-t1)*(1/256) seconds
The Length value is used only if Bit 6 in NR14 is set.

FF12 - NR12 - Channel 1 Volume Envelope (R/W)

  Bit 7-4 - Initial Volume of envelope (0-0Fh) (0=No Sound)
  Bit 3   - Envelope Direction (0=Decrease, 1=Increase)
  Bit 2-0 - Number of envelope sweep (n: 0-7)
            (If zero, stop envelope operation.)
Length of 1 step = n*(1/64) seconds

FF13 - NR13 - Channel 1 Frequency lo (Write Only)

Lower 8 bits of 11 bit frequency (x).
Next 3 bit are in NR14 ($FF14)

FF14 - NR14 - Channel 1 Frequency hi (R/W)

  Bit 7   - Initial (1=Restart Sound)     (Write Only)
  Bit 6   - Counter/consecutive selection (Read/Write)
            (1=Stop output when length in NR11 expires)
  Bit 2-0 - Frequency's higher 3 bits (x) (Write Only)
Frequency = 131072/(2048-x) Hz

Sound Channel 2 - Tone

This sound channel works exactly as channel 1, except that it doesn't have a Tone Envelope/Sweep Register.

FF16 - NR21 - Channel 2 Sound Length/Wave Pattern Duty (R/W)

  Bit 7-6 - Wave Pattern Duty (Read/Write)
  Bit 5-0 - Sound length data (Write Only) (t1: 0-63)
Wave Duty:
  00: 12.5% ( _-------_-------_------- )
  01: 25%   ( __------__------__------ )
  10: 50%   ( ____----____----____---- ) (normal)
  11: 75%   ( ______--______--______-- )
Sound Length = (64-t1)*(1/256) seconds
The Length value is used only if Bit 6 in NR24 is set.

FF17 - NR22 - Channel 2 Volume Envelope (R/W)

  Bit 7-4 - Initial Volume of envelope (0-0Fh) (0=No Sound)
  Bit 3   - Envelope Direction (0=Decrease, 1=Increase)
  Bit 2-0 - Number of envelope sweep (n: 0-7)
            (If zero, stop envelope operation.)
Length of 1 step = n*(1/64) seconds

FF18 - NR23 - Channel 2 Frequency lo data (W)
Frequency's lower 8 bits of 11 bit data (x).
Next 3 bits are in NR24 ($FF19).

FF19 - NR24 - Channel 2 Frequency hi data (R/W)

  Bit 7   - Initial (1=Restart Sound)     (Write Only)
  Bit 6   - Counter/consecutive selection (Read/Write)
            (1=Stop output when length in NR21 expires)
  Bit 2-0 - Frequency's higher 3 bits (x) (Write Only)
Frequency = 131072/(2048-x) Hz

Sound Channel 3 - Wave Output

This channel can be used to output digital sound, the length of the sample buffer (Wave RAM) is limited to 32 digits. This sound channel can be also used to output normal tones when initializing the Wave RAM by a square wave. This channel doesn't have a volume envelope register.

FF1A - NR30 - Channel 3 Sound on/off (R/W)

  Bit 7 - Sound Channel 3 Off  (0=Stop, 1=Playback)  (Read/Write)

FF1B - NR31 - Channel 3 Sound Length

  Bit 7-0 - Sound length (t1: 0 - 255)
Sound Length = (256-t1)*(1/256) seconds
This value is used only if Bit 6 in NR34 is set.

FF1C - NR32 - Channel 3 Select output level (R/W)

  Bit 6-5 - Select output level (Read/Write)
Possible Output levels are:
  0: Mute (No sound)
  1: 100% Volume (Produce Wave Pattern RAM Data as it is)
  2:  50% Volume (Produce Wave Pattern RAM data shifted once to the right)
  3:  25% Volume (Produce Wave Pattern RAM data shifted twice to the right)

FF1D - NR33 - Channel 3 Frequency's lower data (W)
Lower 8 bits of an 11 bit frequency (x).

FF1E - NR34 - Channel 3 Frequency's higher data (R/W)

  Bit 7   - Initial (1=Restart Sound)     (Write Only)
  Bit 6   - Counter/consecutive selection (Read/Write)
            (1=Stop output when length in NR31 expires)
  Bit 2-0 - Frequency's higher 3 bits (x) (Write Only)
Frequency = 4194304/(64*(2048-x)) Hz = 65536/(2048-x) Hz

FF30-FF3F - Wave Pattern RAM
Contents - Waveform storage for arbitrary sound data

This storage area holds 32 4-bit samples that are played back upper 4 bits first.

Sound Channel 4 - Noise

This channel is used to output white noise. This is done by randomly switching the amplitude between high and low at a given frequency. Depending on the frequency the noise will appear 'harder' or 'softer'.

It is also possible to influence the function of the random generator, so the that the output becomes more regular, resulting in a limited ability to output Tone instead of Noise.

FF20 - NR41 - Channel 4 Sound Length (R/W)

  Bit 5-0 - Sound length data (t1: 0-63)
Sound Length = (64-t1)*(1/256) seconds
The Length value is used only if Bit 6 in NR44 is set.

FF21 - NR42 - Channel 4 Volume Envelope (R/W)

  Bit 7-4 - Initial Volume of envelope (0-0Fh) (0=No Sound)
  Bit 3   - Envelope Direction (0=Decrease, 1=Increase)
  Bit 2-0 - Number of envelope sweep (n: 0-7)
            (If zero, stop envelope operation.)
Length of 1 step = n*(1/64) seconds

FF22 - NR43 - Channel 4 Polynomial Counter (R/W)
The amplitude is randomly switched between high and low at the given frequency. A higher frequency will make the noise to appear 'softer'.
When Bit 3 is set, the output will become more regular, and some frequencies will sound more like Tone than Noise.
  Bit 7-4 - Shift Clock Frequency (s)
  Bit 3   - Counter Step/Width (0=15 bits, 1=7 bits)
  Bit 2-0 - Dividing Ratio of Frequencies (r)
Frequency = 524288 Hz / r / 2^(s+1) ;For r=0 assume r=0.5 instead

FF23 - NR44 - Channel 4 Counter/consecutive; Inital (R/W)

  Bit 7   - Initial (1=Restart Sound)     (Write Only)
  Bit 6   - Counter/consecutive selection (Read/Write)
            (1=Stop output when length in NR41 expires)

Sound Control Registers

FF24 - NR50 - Channel control / ON-OFF / Volume (R/W)
The volume bits specify the "Master Volume" for Left/Right sound output.
  Bit 7   - Output Vin to SO2 terminal (1=Enable)
  Bit 6-4 - SO2 output level (volume)  (0-7)
  Bit 3   - Output Vin to SO1 terminal (1=Enable)
  Bit 2-0 - SO1 output level (volume)  (0-7)
The Vin signal is received from the game cartridge bus, allowing external hardware in the cartridge to supply a fifth sound channel, additionally to the gameboys internal four channels. As far as I know this feature isn't used by any existing games.

FF25 - NR51 - 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

FF26 - NR52 - Sound on/off
If your GB programs don't use sound then write 00h to this register to save 16% or more on GB power consumption. Disabeling the sound controller by clearing Bit 7 destroys the contents of all sound registers. Also, it is not possible to access any sound registers (execpt FF26) while the sound controller is disabled.
  Bit 7 - All sound on/off  (0: stop all sound circuits) (Read/Write)
  Bit 3 - Sound 4 ON flag (Read Only)
  Bit 2 - Sound 3 ON flag (Read Only)
  Bit 1 - Sound 2 ON flag (Read Only)
  Bit 0 - Sound 1 ON flag (Read Only)
Bits 0-3 of this register are read only status bits, writing to these bits does NOT enable/disable sound. The flags get set when sound output is restarted by setting the Initial flag (Bit 7 in NR14-NR44), the flag remains set until the sound length has expired (if enabled). A volume envelopes which has decreased to zero volume will NOT cause the sound flag to go off.

Joypad Input

FF00 - P1/JOYP - Joypad (R/W)
The eight gameboy buttons/direction keys are arranged in form of a 2x4 matrix. Select either button or direction keys by writing to this register, then read-out bit 0-3.
  Bit 7 - Not used
  Bit 6 - Not used
  Bit 5 - P15 Select Button Keys      (0=Select)
  Bit 4 - P14 Select Direction Keys   (0=Select)
  Bit 3 - P13 Input Down  or Start    (0=Pressed) (Read Only)
  Bit 2 - P12 Input Up    or Select   (0=Pressed) (Read Only)
  Bit 1 - P11 Input Left  or Button B (0=Pressed) (Read Only)
  Bit 0 - P10 Input Right or Button A (0=Pressed) (Read Only)
Note: Most programs are repeatedly reading from this port several times (the first reads used as short delay, allowing the inputs to stabilize, and only the value from the last read actually used).

Usage in SGB software
Beside for normal joypad input, SGB games mis-use the joypad register to output SGB command packets to the SNES, also, SGB programs may read out gamepad states from up to four different joypads which can be connected to the SNES.
See SGB description for details.

INT 60 - Joypad Interrupt
Joypad interrupt is requested when any of the above Input lines changes from High to Low. Generally this should happen when a key becomes pressed (provided that the button/direction key is enabled by above Bit4/5), however, because of switch bounce, one or more High to Low transitions are usually produced both when pressing or releasing a key.

Using the Joypad Interrupt
It's more or less useless for programmers, even when selecting both buttons and direction keys simultaneously it still cannot recognize all keystrokes, because in that case a bit might be already held low by a button key, and pressing the corresponding direction key would thus cause no difference. The only meaningful purpose of the keystroke interrupt would be to terminate STOP (low power) standby state.
Also, the joypad interrupt does not appear to work with CGB and GBA hardware (the STOP function can be still terminated by joypad keystrokes though).

Serial Data Transfer (Link Cable)

FF01 - SB - Serial transfer data (R/W)
8 Bits of data to be read/written

FF02 - SC - Serial Transfer Control (R/W)

  Bit 7 - Transfer Start Flag (0=No Transfer, 1=Start)
  Bit 1 - Clock Speed (0=Normal, 1=Fast) ** CGB Mode Only **
  Bit 0 - Shift Clock (0=External Clock, 1=Internal Clock)
The clock signal specifies the rate at which the eight data bits in SB (FF01) are transferred. When the gameboy is communicating with another gameboy (or other computer) then either one must supply internal clock, and the other one must use external clock.

Internal Clock
In Non-CGB Mode the gameboy supplies an internal clock of 8192Hz only (allowing to transfer about 1 KByte per second). In CGB Mode four internal clock rates are available, depending on Bit 1 of the SC register, and on whether the CGB Double Speed Mode is used:
    8192Hz -  1KB/s - Bit 1 cleared, Normal
   16384Hz -  2KB/s - Bit 1 cleared, Double Speed Mode
  262144Hz - 32KB/s - Bit 1 set,     Normal
  524288Hz - 64KB/s - Bit 1 set,     Double Speed Mode

External Clock
The external clock is typically supplied by another gameboy, but might be supplied by another computer (for example if connected to a PCs parallel port), in that case the external clock may have any speed. Even the old/monochrome gameboy is reported to recognizes external clocks of up to 500KHz. And there is no limitiation into the other direction - even when suppling an external clock speed of "1 bit per month", then the gameboy will still eagerly wait for the next bit(s) to be transferred. It isn't required that the clock pulses are sent at an regular interval either.

Timeouts
When using external clock then the transfer will not complete until the last bit is received. In case that the second gameboy isn't supplying a clock signal, if it gets turned off, or if there is no second gameboy connected at all) then transfer will never complete. For this reason the transfer procedure should use a timeout counter, and abort the communication if no response has been received during the timeout interval.

Delays and Synchronization
The gameboy that is using internal clock should always execute a small delay between each transfer, in order to ensure that the opponent gameboy has enough time to prepare itself for the next transfer, ie. the gameboy with external clock must have set its transfer start bit before the gameboy with internal clock starts the transfer. Alternately, the two gameboys could switch between internal and external clock for each transferred byte to ensure synchronization.

Transfer is initiated by setting the Transfer Start Flag. This bit is automatically set to 0 at the end of Transfer. Reading this bit can be used to determine if the transfer is still active.

INT 58 - Serial Interrupt
When the transfer has completed (ie. after sending/receiving 8 bits, if any) then an interrupt is requested by setting Bit 3 of the IF Register (FF0F). When that interrupt is enabled, then the Serial Interrupt vector at 0058 is called.

XXXXXX...

Transmitting and receiving serial data is done simultaneously. The received data is automatically stored in SB.

The serial I/O port on the Gameboy is a very simple setup and is crude compared to standard RS-232 (IBM-PC) or RS-485 (Macintosh) serial ports. There are no start or stop bits.

During a transfer, a byte is shifted in at the same time that a byte is shifted out. The rate of the shift is determined by whether the clock source is internal or external.
The most significant bit is shifted in and out first.

When the internal clock is selected, it drives the clock pin on the game link port and it stays high when not used. During a transfer it will go low eight times to clock in/out each bit.

The state of the last bit shifted out determines the state of the output line until another transfer takes place.

If a serial transfer with internal clock is performed and no external GameBoy is present, a value of $FF will be received in the transfer.

The following code causes $75 to be shifted out the serial port and a byte to be shifted into $FF01:

    ld   a,$75
    ld  ($FF01),a
    ld   a,$81
    ld  ($FF02),a

Timer and Divider Registers

FF04 - DIV - Divider Register (R/W)
This register is incremented at rate of 16384Hz (~16779Hz on SGB). In CGB Double Speed Mode it is incremented twice as fast, ie. at 32768Hz. Writing any value to this register resets it to 00h.

FF05 - TIMA - Timer counter (R/W)
This timer is incremented by a clock frequency specified by the TAC register ($FF07). When the value overflows (gets bigger than FFh) then it will be reset to the value specified in TMA (FF06), and an interrupt will be requested, as described below.

FF06 - TMA - Timer Modulo (R/W)
When the TIMA overflows, this data will be loaded.

FF07 - TAC - Timer Control (R/W)

  Bit 2    - Timer Stop  (0=Stop, 1=Start)
  Bits 1-0 - Input Clock Select
             00:   4096 Hz    (~4194 Hz SGB)
             01: 262144 Hz  (~268400 Hz SGB)
             10:  65536 Hz   (~67110 Hz SGB)
             11:  16384 Hz   (~16780 Hz SGB)

INT 50 - Timer Interrupt
Each time when the timer overflows (ie. when TIMA gets bigger than FFh), then an interrupt is requested by setting Bit 2 in the IF Register (FF0F). When that interrupt is enabled, then the CPU will execute it by calling the timer interrupt vector at 0050h.

Note
The above described Timer is the built-in timer in the gameboy. It has nothing to do with the MBC3s battery buffered Real Time Clock - that's a completely different thing, described in the chapter about Memory Banking Controllers.

Interrupts

IME - Interrupt Master Enable Flag (Write Only)

  0 - Disable all Interrupts
  1 - Enable all Interrupts that are enabled in IE Register (FFFF)
The IME flag is used to disable all interrupts, overriding any enabled bits in the IE Register. It isn't possible to access the IME flag by using a I/O address, instead IME is accessed directly from the CPU, by the following opcodes/operations:
  EI     ;Enable Interrupts  (ie. IME=1)
  DI     ;Disable Interrupts (ie. IME=0)
  RETI   ;Enable Ints & Return (same as the opcode combination EI, RET)
  <INT>  ;Disable Ints & Call to Interrupt Vector
Whereas <INT> means the operation which is automatically executed by the CPU when it executes an interrupt.

FFFF - IE - Interrupt Enable (R/W)

  Bit 0: V-Blank  Interrupt Enable  (INT 40h)  (1=Enable)
  Bit 1: LCD STAT Interrupt Enable  (INT 48h)  (1=Enable)
  Bit 2: Timer    Interrupt Enable  (INT 50h)  (1=Enable)
  Bit 3: Serial   Interrupt Enable  (INT 58h)  (1=Enable)
  Bit 4: Joypad   Interrupt Enable  (INT 60h)  (1=Enable)

FF0F - IF - Interrupt Flag (R/W)

  Bit 0: V-Blank  Interrupt Request (INT 40h)  (1=Request)
  Bit 1: LCD STAT Interrupt Request (INT 48h)  (1=Request)
  Bit 2: Timer    Interrupt Request (INT 50h)  (1=Request)
  Bit 3: Serial   Interrupt Request (INT 58h)  (1=Request)
  Bit 4: Joypad   Interrupt Request (INT 60h)  (1=Request)
When an interrupt signal changes from low to high, then the corresponding bit in the IF register becomes set. For example, Bit 0 becomes set when the LCD controller enters into the V-Blank period.

Interrupt Requests
Any set bits in the IF register are only <requesting> an interrupt to be executed. The actual <execution> happens only if both the IME flag, and the corresponding bit in the IE register are set, otherwise the interrupt 'waits' until both IME and IE allow its execution.

Interrupt Execution
When an interrupt gets executed, the corresponding bit in the IF register becomes automatically reset by the CPU, and the IME flag becomes cleared (disabeling any further interrupts until the program re-enables the interrupts, typically by using the RETI instruction), and the corresponding Interrupt Vector (that are the addresses in range 0040h-0060h, as shown in IE and IF register decriptions above) becomes called.

Manually Requesting/Discarding Interrupts
As the CPU automatically sets and cleares the bits in the IF register it is usually not required to write to the IF register. However, the user may still do that in order to manually request (or discard) interrupts. As for real interrupts, a manually requested interrupt isn't executed unless/until IME and IE allow its execution.

Interrupt Priorities
In the following three situations it might happen that more than 1 bit in the IF register are set, requesting more than one interrupt at once:
  1) More than one interrupt signal changed from Low
     to High at the same time.
  2) Several interrupts have been requested during a
     time in which IME/IE didn't allow these interrupts
     to be executed directly.
  3) The user has written a value with several "1" bits
     (for example 1Fh) to the IF register.
Provided that IME and IE allow the execution of more than one of the requested interrupts, then the interrupt with the highest priority becomes executed first. The priorities are ordered as the bits in the IE and IF registers, Bit 0 (V-Blank) having the highest priority, and Bit 4 (Joypad) having the lowest priority.

Nested Interrupts
The CPU automatically disables all other interrupts by setting IME=0 when it executes an interrupt. Usually IME remains zero until the interrupt procedure returns (and sets IME=1 by the RETI instruction). However, if you want any other interrupts of lower or higher (or same) priority to be allowed to be executed from inside of the interrupt procedure, then you can place an EI instruction into the interrupt procedure.

CGB Registers

Forward
This chapter describes only CGB (Color Gameboy) registers that didn't fit into normal categories - most CGB registers are described in the chapter about Video Display (Color Palettes, VRAM Bank, VRAM DMA Transfers, and changed meaning of Bit 0 of LCDC Control register). Also, a changed bit is noted in the chapter about the Serial/Link port.

Unlocking CGB functions
When using any CGB registers (including those in the Video/Link chapters), you must first unlock CGB features by changing byte 0143h in the cartridge header. Typically use a value of 80h for games which support both CGB and monochrome gameboys, and C0h for games which work on CGBs only. Otherwise, the CGB will operate in monochrome "Non CGB" compatibility mode.

Detecting CGB (and GBA) functions
CGB hardware can be detected by examing the CPU accumulator (A-register) directly after startup. A value of 11h indicates CGB (or GBA) hardware, if so, CGB functions can be used (if unlocked, see above).
When A=11h, you may also examine Bit 0 of the CPUs B-Register to separate between CGB (bit cleared) and GBA (bit set), by that detection it is possible to use 'repaired' color palette data matching for GBA displays.

FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch

  Bit 7: Current Speed     (0=Normal, 1=Double) (Read Only)
  Bit 0: Prepare Speed Switch (0=No, 1=Prepare) (Read/Write)
This register is used to prepare the gameboy to switch between CGB Double Speed Mode and Normal Speed Mode. The actual speed switch is performed by executing a STOP command after Bit 0 has been set. After that Bit 0 will be cleared automatically, and the gameboy will operate at the 'other' speed. The recommended speed switching procedure in pseudo code would be:
  IF KEY1_BIT7 <> DESIRED_SPEED THEN
    IE=00H       ;(FFFF)=00h
    JOYP=30H     ;(FF00)=30h
    KEY1=01H     ;(FF4D)=01h
    STOP         ;STOP
  ENDIF
The CGB is operating in Normal Speed Mode when it is turned on. Note that using the Double Speed Mode increases the power consumption, it would be recommended to use Single Speed whenever possible. However, the display will flicker (white) for a moment during speed switches, so this cannot be done permanentely.
In Double Speed Mode the following will operate twice as fast as normal:
  The CPU (2.10 MHz, 1 Cycle = approx. 0.5us)
  Timer and Divider Registers
  Serial Port (Link Cable)
  DMA Transfer to OAM
And the following will keep operating as usual:
  LCD Video Controller
  HDMA Transfer to VRAM
  All Sound Timings and Frequencies

FF56 - RP - CGB Mode Only - Infrared Communications Port
This register allows to input and output data through the CGBs built-in Infrared Port. When reading data, bit 6 and 7 must be set (and obviously Bit 0 must be cleared - if you don't want to receive your own gameboys IR signal). After sending or receiving data you should reset the register to 00h to reduce battery power consumption again.
  Bit 0:   Write Data   (0=LED Off, 1=LED On)             (Read/Write)
  Bit 1:   Read Data    (0=Receiving IR Signal, 1=Normal) (Read Only)
  Bit 6-7: Data Read Enable (0=Disable, 3=Enable)         (Read/Write)
Note that the receiver will adapt itself to the normal level of IR pollution in the air, so if you would send a LED ON signal for a longer period, then the receiver would treat that as normal (=OFF) after a while. For example, a Philips TV Remote Control sends a series of 32 LED ON/OFF pulses (length 10us ON, 17.5us OFF each) instead of a permanent 880us LED ON signal.
Even though being generally CGB compatible, the GBA does not include an infra-red port.

FF70 - SVBK - CGB Mode Only - WRAM Bank
In CGB Mode 32 KBytes internal RAM are available. This memory is divided into 8 banks of 4 KBytes each. Bank 0 is always available in memory at C000-CFFF, Bank 1-7 can be selected into the address space at D000-DFFF.
  Bit 0-2  Select WRAM Bank (Read/Write)
Writing a value of 01h-07h will select Bank 1-7, writing a value of 00h will select Bank 1 either.

FF6C - Undocumented (FEh) - Bit 0 (Read/Write) - CGB Mode Only
FF72 - Undocumented (00h) - Bit 0-7 (Read/Write)
FF73 - Undocumented (00h) - Bit 0-7 (Read/Write)
FF74 - Undocumented (00h) - Bit 0-7 (Read/Write) - CGB Mode Only
FF75 - Undocumented (8Fh) - Bit 4-6 (Read/Write)
FF76 - Undocumented (00h) - Always 00h (Read Only)
FF77 - Undocumented (00h) - Always 00h (Read Only)
These are undocumented CGB Registers. The numbers in brackets () indicate the initial values. Purpose of these registers is unknown (if any). Registers FF6C and FF74 are always FFh if the CGB is in Non CGB Mode.

SGB Functions

General Information
 SGB Description
SGB Unlocking and Detecting SGB Functions
SGB Command Packet Transfers
SGB VRAM Transfers
SGB Command Summary
SGB Color Palettes Overview

SGB Commands
 SGB Palette Commands
SGB Color Attribute Commands
SGB Sound Functions
SGB System Control Commands
SGB Multiplayer Command
SGB Border and OBJ Commands

SGB Description

General Description
Basically, the SGB (Super Gameboy) is an adapter cartridge that allows to play gameboy games on a SNES (Super Nintendo Entertainment System) gaming console. In detail, you plug the gameboy cartridge into the SGB cartridge, then plug the SGB cartridge into the SNES, and then connect the SNES to your TV Set. In result, games can be played and viewed on the TV Set, and are controlled by using the SNES joypad(s).

More Technical Description
The SGB cartridge just contains a normal gameboy CPU and normal gameboy video controller. Normally the video signal from this controller would be sent to the LCD screen, however, in this special case the SNES read out the video signal and displays it on the TV set by using a special SNES BIOS ROM which is located in the SGB cartridge. Also, normal gameboy sound output is forwared to the SNES and output to the TV Set, vice versa, joypad input is forwared from the SNES controller(s) to the gameboy joypad inputs.

Normal Monochrome Games
Any gameboy games which have been designed for normal monochrome handheld gameboys will work with the SGB hardware as well. The SGB will apply a four color palette to these games by replacing the normal four grayshades. The 160x144 pixel gamescreen is displayed in the middle of the 256x224 pixel SNES screen (the unused area is filled by a screen border bitmap). The user may access built-in menues, allowing to change color palette data, to select between several pre-defined borders, etc.

Games that have been designed to support SGB functions may also access the following additional features:

Colorized Game Screen
There's limited ability to colorize the gamescreen by assigning custom color palettes to each 20x18 display characters, however, this works mainly for static display data such like title screens or status bars, the 20x18 color attribute map is non-scrollable, and it is not possible to assign separate colors to moveable foreground sprites (OBJs), so that animated screen regions will be typically restricted to using a single palette of four colors only.

SNES Foreground Sprites
Up to 24 foreground sprites (OBJs) of 8x8 or 16x16 pixels, 16 colors can be displayed. When replacing (or just overlaying) the normal gameboy OBJs by SNES OBJs it'd be thus possible to display OBJs with other colors than normal background area. This method doesn't appear to be very popular, even though it appears to be quite easy to implement, however, the bottommost character line of the gamescreen will be masked out because this area is used to transfer OAM data to the SNES.

The SGB Border
The possibly most popular and most impressive feature is to replace the default SGB screen border by a custom bitmap which is stored in the game cartridge.

Multiple Joypads
Up to four joypads can be conected to the SNES, and SGB software may read-out each of these joypads separately, allowing up to four players to play the same game simultaneously. Unlike for multiplayer handheld games, this requires only one game cartridge and only one SGB/SNES, and no link cables are required, the downside is that all players must share the same display screen.

Sound Functions
Beside for normal gameboy sound, a number of digital sound effects is pre-defined in the SNES BIOS, these effects may be accessed quite easily. Programmers whom are familiar with SNES sounds may also access the SNES sound chip, or use the SNES MIDI engine directly in order to produce other sound effects or music.

Taking Control of the SNES CPU
Finally, it is possible to write program code or data into SNES memory, and to execute such program code by using the SNES CPU.

SGB System Clock
Because the SGB is synchronized to the SNES CPU, the gameboy system clock is directly chained to the SNES system clock. In result, the gameboy CPU, video controller, timers, and sound frequencies will be all operated approx 2.4% faster as by normal gameboys.
Basically, this should be no problem, and the game will just run a little bit faster. However sensitive musicians may notice that sound frequencies are a bit too high, programs that support SGB functions may avoid this effect by reducing frequencies of gameboy sounds when having detected SGB hardware.
Also, I think that I've heard that SNES models which use a 50Hz display refresh rate (rather than 60Hz) are resulting in respectively slower SGB/gameboy timings ???

SGB Unlocking and Detecting SGB Functions

Cartridge Header
SGB games are required to have a cartridge header with Nintendo and proper checksum just as normal gameboy games. Also, two special entries must be set in order to unlock SGB functions:
  146h - SGB Flag - Must be set to 03h for SGB games
  14Bh - Old Licensee Code - Must be set 33h for SGB games
When these entries aren't set, the game will still work just like all 'monochrome' gameboy games, but it cannot access any of the special SGB functions.

Detecting SGB hardware
The recommended detection method is to send a MLT_REQ command which enables two (or four) joypads. A normal handheld gameboy will ignore this command, a SGB will now return incrementing joypad IDs each time when deselecting keyboard lines (see MLT_REQ description for details).
Now read-out joypad state/IDs several times, and if the ID-numbers are changing, then it is a SGB (a normal gameboy would typically always return 0Fh as ID). Finally, when not intending to use more than one joypad, send another MLT_REQ command in order to re-disable the multi-controller mode.
Detection works regardless of whether and how many joypads are physically connected to the SNES. However, detection works only when having unlocked SGB functions in the cartridge header, as described above.

Separating between SGB and SGB2
It is also possible to separate between SGB and SGB2 models by examining the inital value of the accumulator (A-register) directly after startup.
  01h  SGB or Normal Gameboy (DMG)
  FFh  SGB2 or Pocket Gameboy
  11h  CGB or GBA
Because values 01h and FFh are shared for both handhelds and SGBs, it is still required to use the above MLT_REQ detection procedure. As far as I know the SGB2 doesn't have any extra features which'd require separate SGB2 detection except for curiosity purposes, for example, the game "Tetris DX" chooses to display an alternate SGB border on SGB2s.

Reportedly, some SGB models include link ports (just like handheld gameboy) (my own SGB does not have such an port), possibly this feature is available in SGB2-type models only ???

SGB Command Packet Transfers

Command packets (aka Register Files) are transferred from the gameboy to the SNES by using P14 and P15 output lines of the JOYPAD register (FF00h), these lines are normally used to select the two rows in the gameboy keyboard matrix (which still works).

Transferring Bits
A command packet transfer must be initiated by setting both P14 and P15 to LOW, this will reset and start the SNES packet receiving program. Data is then transferred (LSB first), setting P14=LOW will indicate a "0" bit, and setting P15=LOW will indicate a "1" bit. For example:
       RESET 0   0   1   1   0   1   0
  P14  --_---_---_-----------_-------_--...
  P15  --_-----------_---_-------_------...
Data and reset pulses must be kept LOW for at least 5us. P14 and P15 must be kept both HIGH for at least 15us between any pulses.
Obviously, it'd be no good idea to access the JOYPAD register during the transfer, for example, in case that your VBlank interrupt procedure reads-out joypad states each frame, be sure to disable that interrupt during the transfer (or disable only the joypad procedure by using a software flag).

Transferring Packets
Each packet is invoked by a RESET pulse, then 128 bits of data are transferred (16 bytes, LSB of first byte first), and finally, a "0"-bit must be transferred as stop bit. The structure of normal packets is:
   1 PULSE Reset
   1 BYTE  Command Code*8+Length
  15 BYTES Parameter Data
   1 BIT   Stop Bit (0)
The above 'Length' indicates the total number of packets (1-7, including the first packet) which will be sent, ie. if more than 15 parameter bytes are used, then further packet(s) will follow, as such:
   1 PULSE Reset
  16 BYTES Parameter Data
   1 BIT   Stop Bit (0)
By using all 7 packets, up to 111 data bytes (15+16*6) may be sent.
Unused bytes at the end of the last packet must be set to zero.
A 60ms (4 frames) delay should be invoked between each packet transfer.

SGB VRAM Transfers

Overview
Beside for the packet transfer method, larger data blocks of 4KBytes can be transferred by using the video signal. These transfers are invoked by first sending one of the commands with the ending _TRN (by using normal packet transfer), the 4K data block is then read-out by the SNES from gameboy display memory during the next frame.

Transfer Data
Normally, transfer data should be stored at 8000h-8FFFh in gameboy VRAM,
even though the SNES receives the data in from display scanlines, it will automatically re-produce the same ordering of bits and bytes, as being originally stored at 8000h-8FFFh in gameboy memory.

Preparing the Display
The above method works only when recursing the following things: BG Map must display unsigned characters 00h-FFh on the screen; 00h..13h in first line, 14h..27h in next line, etc. The gameboy display must be enabled, the display may not be scrolled, OBJ sprites should not overlap the background tiles, the BGP palette register must be set to E4h.

Transfer Time
Note that the transfer data should be prepared in VRAM <before> sending the transfer command packet. The actual transfer starts at the beginning of the next frame after the command has been sent, and the transfer ends at the end of the 5th frame after the command has been sent (not counting the frame in which the command has been sent).

Avoiding Screen Garbage
The display will contain 'garbage' during the transfer, this dirt-effect can be avoided by freezing the screen (in the state which has been displayed before the transfer) by using the MASK_EN command.
Of course, this works only when actually executing the game on a SGB (and not on normal handheld gameboys), it'd be thus required to detect the presence of SGB hardware before blindly sending VRAM data.

SGB Command Summary

SGB System Command Table

  Code Name      Expl.
  00   PAL01     Set SGB Palette 0,1 Data
  01   PAL23     Set SGB Palette 2,3 Data
  02   PAL03     Set SGB Palette 0,3 Data
  03   PAL12     Set SGB Palette 1,2 Data
  04   ATTR_BLK  "Block" Area Designation Mode
  05   ATTR_LIN  "Line" Area Designation Mode
  06   ATTR_DIV  "Divide" Area Designation Mode
  07   ATTR_CHR  "1CHR" Area Designation Mode
  08   SOUND     Sound On/Off
  09   SOU_TRN   Transfer Sound PRG/DATA
  0A   PAL_SET   Set SGB Palette Indirect
  0B   PAL_TRN   Set System Color Palette Data
  0C   ATRC_EN   Enable/disable Attraction Mode
  0D   TEST_EN   Speed Function
  0E   ICON_EN   SGB Function
  0F   DATA_SND  SUPER NES WRAM Transfer 1
  10   DATA_TRN  SUPER NES WRAM Transfer 2
  11   MLT_REG   Controller 2 Request
  12   JUMP      Set SNES Program Counter
  13   CHR_TRN   Transfer Character Font Data
  14   PCT_TRN   Set Screen Data Color Data
  15   ATTR_TRN  Set Attribute from ATF
  16   ATTR_SET  Set Data to ATF
  17   MASK_EN   Game Boy Window Mask
  18   OBJ_TRN   Super NES OBJ Mode

SGB Color Palettes Overview

Available SNES Palettes
The SGB/SNES provides 8 palettes of 16 colors each, each color may be defined out of a selection of 34768 colors (15 bit). Palettes 0-3 are used to colorize the gamescreen, only the first four colors of each of these palettes are used. Palettes 4-7 are used for the SGB Border, all 16 colors of each of these palettes may be used.

Color 0 Restriction
Color 0 of each of the eight palettes is transparent, causing the backdrop color to be displayed instead. The backdrop color is typically defined by the most recently color being assigned to Color 0 (regardless of the palette number being used for that operation).
Effectively, gamescreen palettes can have only three custom colors each, and SGB border palettes only 15 colors each, additionally, color 0 can be used for for all palettes, which will then all share the same color though.

Translation of Grayshades into Colors
Because the SGB/SNES reads out the gameboy video controllers display signal, it translates the different grayshades from the signal into SNES colors as such:
  White       -->  Color 0
  Light Gray  -->  Color 1
  Dark Gray   -->  Color 2
  Black       -->  Color 3
Note that gameboy colors 0-3 are assigned to user-selectable grayshades by the gameboys BGP, OBP1, and OBP2 registers. There is thus no fixed relationship between gameboy colors 0-3 and SNES colors 0-3.

Using Gameboy BGP/OBP Registers
A direct translation of color 0-3 into color 0-3 may be produced by setting BGP/OBP registers to a value of 0E4h each. However, in case that your program uses black background for example, then you may internally assign background as "White" at the gameboy side by BGP/OBP registers (which is then interpreted as SNES color 0, which is shared for all SNES palettes). The advantage is that you may define Color 0 as Black at the SNES side, and may assign custom colors for Colors 1-3 of each SNES palette.

System Color Palette Memory
Beside for the actually visible palettes, up to 512 palettes of 4 colors each may be defined in SNES RAM. Basically, this is completely irrelevant because the palettes are just stored in RAM whithout any relationship to the displayed picture, anyways, these pre-defined colors may be transferred to actually visible palettes slightly faster as when transferring palette data by separate command packets.

SGB Palette Commands

SGB Command 00h - PAL01
Transmit color data for SGB palette 0, color 0-3, and for SGB palette 1, color 1-3 (without separate color 0).
  Byte  Content
  0     Command*8+Length (fixed length=01h)
  1-E   Color Data for 7 colors of 2 bytes (16bit) each:
          Bit 0-4   - Red Intensity   (0-31)
          Bit 5-9   - Green Intensity (0-31)
          Bit 10-14 - Blue Intensity  (0-31)
          Bit 15    - Not used (zero)
  F     Not used (00h)
The value transferred as color 0 will be applied for all eight palettes.

SGB Command 01h - PAL23
Same as above PAL01, but for Palettes 2 and 3 respectively.

SGB Command 02h - PAL03
Same as above PAL01, but for Palettes 0 and 3 respectively.

SGB Command 03h - PAL12
Same as above PAL01, but for Palettes 1 and 2 respectively.

SGB Command 0Ah - PAL_SET
Used to copy pre-defined palette data from SGB system color palette to actual SGB palette.
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1-2   System Palette number for SGB Color Palette 0 (0-511)
  3-4   System Palette number for SGB Color Palette 1 (0-511)
  5-6   System Palette number for SGB Color Palette 2 (0-511)
  7-8   System Palette number for SGB Color Palette 3 (0-511)
  9     Attribute File
          Bit 0-5 - Attribute File Number (00h-2Ch) (Used only if Bit7=1)
          Bit 6   - Cancel Mask           (0=No change, 1=Yes)
          Bit 7   - Use Attribute File    (0=No, 1=Apply above ATF Number)
  A-F   Not used (zero)
Before using this function, System Palette data should be initialized by PAL_TRN command, and (when used) Attribute File data should be initialized by ATTR_TRN.

SGB Command 0Bh - PAL_TRN
Used to initialize SGB system color palettes in SNES RAM.
System color palette memory contains 512 pre-defined palettes, these palettes do not directly affect the display, however, the PAL_SET command may be later used to transfer four of these 'logical' palettes to actual visible 'physical' SGB palettes. Also, the OBJ_TRN function will use groups of 4 System Color Palettes (4*4 colors) for SNES OBJ palettes (16 colors).
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1-F   Not used (zero)
The palette data is sent by VRAM-Transfer (4 KBytes).
  000-FFF  Data for System Color Palette 0-511
Each Palette consists of four 16bit-color definitions (8 bytes).
Note: The data is stored at 3000h-3FFFh in SNES memory.

SGB Color Attribute Commands

SGB Command 04h - ATTR_BLK
Used to specify color attributes for the inside or outside of one or more rectangular screen regions.
  Byte  Content
  0     Command*8+Length (length=1..7)
  1     Number of Data Sets (01h..12h)
  2-7   Data Set #1
          Byte 0 - Control Code (0-7)
            Bit 0 - Change Colors inside of surrounded area     (1=Yes)
            Bit 1 - Change Colors of surrounding character line (1=Yes)
            Bit 2 - Change Colors outside of surrounded area    (1=Yes)
            Bit 3-7 - Not used (zero)
            Exception: When changing only the Inside or Outside, then the
            Surrounding line becomes automatically changed to same color.
          Byte 1 - Color Palette Designation
            Bit 0-1 - Palette Number for inside of surrounded area
            Bit 2-3 - Palette Number for surrounding character line
            Bit 4-5 - Palette Number for outside of surrounded area
            Bit 6-7 - Not used (zero)
          Data Set Byte 2 - Coordinate X1 (left)
          Data Set Byte 3 - Coordinate Y1 (upper)
          Data Set Byte 4 - Coordinate X2 (right)
          Data Set Byte 5 - Coordinate Y2 (lower)
            Specifies the coordinates of the surrounding rectangle.
  8-D   Data Set #2 (if any)
  E-F   Data Set #3 (continued at 0-3 in next packet) (if any)
When sending three or more data sets, data is continued in further packet(s). Unused bytes at the end of the last packet should be set to zero. The format of the separate Data Sets is described below.

SGB Command 05h - ATTR_LIN
Used to specify color attributes of one or more horizontal or vertical character lines.
  Byte  Content
  0     Command*8+Length (length=1..7)
  1     Number of Data Sets (01h..6Eh) (one byte each)
  2     Data Set #1
          Bit 0-4 - Line Number    (X- or Y-coordinate, depending on bit 7)
          Bit 5-6 - Palette Number (0-3)
          Bit 7   - H/V Mode Bit   (0=Vertical line, 1=Horizontal Line)
  3     Data Set #2 (if any)
  4     Data Set #3 (if any)
  etc.
When sending 15 or more data sets, data is continued in further packet(s). Unused bytes at the end of the last packet should be set to zero. The format of the separate Data Sets (one byte each) is described below.
The length of each line reaches from one end of the screen to the other end. In case that some lines overlap each other, then lines from lastmost data sets will overwrite lines from previous data sets.

SGB Command 06h - ATTR_DIV
Used to split the screen into two halfes, and to assign separate color attributes to each half, and to the division line between them.
  Byte  Content
  0     Command*8+Length   (fixed length=1)
  1     Color Palette Numbers and H/V Mode Bit
          Bit 0-1  Palette Number below/right of division line
          Bit 2-3  Palette Number above/left of division line
          Bit 4-5  Palette Number for division line
          Bit 6    H/V Mode Bit  (0=split left/right, 1=split above/below)
  2     X- or Y-Coordinate (depending on H/V bit)
  3-F   Not used (zero)

SGB Command 07h - ATTR_CHR
Used to specify color attributes for separate characters.
  Byte  Content
  0     Command*8+Length (length=1..6)
  1     Beginning X-Coordinate
  2     Beginning Y-Coordinate
  3-4   Number of Data Sets (1-360)
  5     Writing Style   (0=Left to Right, 1=Top to Bottom)
  6     Data Sets 1-4   (Set 1 in MSBs, Set 4 in LSBs)
  7     Data Sets 5-8   (if any)
  8     Data Sets 9-12  (if any)
  etc.
When sending 41 or more data sets, data is continued in further packet(s). Unused bytes at the end of the last packet should be set to zero. Each data set consists of two bits, indicating the palette number for one character.
Depending on the writing style, data sets are written from left to right, or from top to bottom. In either case the function wraps to the next row/column when reaching the end of the screen.

SGB Command 15h - ATTR_TRN
Used to initialize Attribute Files (ATFs) in SNES RAM. Each ATF consists of 20x18 color attributes for the gameboy screen. This function does not directly affect display attributes. Instead, one of the defined ATFs may be copied to actual display memory at a later time by using ATTR_SET or PAL_SET functions.
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1-F   Not used (zero)
The ATF data is sent by VRAM-Transfer (4 KBytes).
  000-FD1  Data for ATF0 through ATF44 (4050 bytes)
  FD2-FFF  Not used
Each ATF consists of 90 bytes, that are 5 bytes (20x2bits) for each of the 18 character lines of the gameboy window. The two most significant bits of the first byte define the color attribute (0-3) for the first character of the first line, the next two bits the next character, and so on.

SGB Command 16h - ATTR_SET
Used to transfer attributes from Attribute File (ATF) to gameboy window.
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1     Attribute File Number (00-2Ch), Bit 6=Cancel Mask
  2-F   Not used (zero)
When above Bit 6 is set, the gameboy screen becomes re-enabled after the transfer (in case it has been disabled/frozen by MASK_EN command).
Note: The same functions may be (optionally) also included in PAL_SET commands, as described in the chapter about Color Palette Commands.

SGB Sound Functions

SGB Command 08h - SOUND
Used to start/stop internal sound effect, start/stop sound using internal tone data.
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1     Sound Effect A (Port 1) Decrescendo 8bit Sound Code
  2     Sound Effect B (Port 2) Sustain     8bit Sound Code
  3     Sound Effect Attributes
          Bit 0-1 - Sound Effect A Pitch  (0..3=Low..High)
          Bit 2-3 - Sound Effect A Volume (0..2=High..Low, 3=Mute on)
          Bit 4-5 - Sound Effect B Pitch  (0..3=Low..High)
          Bit 6-7 - Sound Effect B Volume (0..2=High..Low, 3=Not used)
  4     Music Score Code (must be zero if not used)
  5-F   Not used (zero)
See Sound Effect Tables below for a list of available pre-defined effects.
"Notes"
1) Mute is only active when both bits D2 and D3 are 1.
2) When the volume is set for either Sound Effect A or Sound Effect B, mute is turned off.
3) When Mute on/off has been executed, the sound fades out/fades in.
4) Mute on/off operates on the (BGM) which is reproduced by Sound Effect A, Sound Effect B, and the Super NES APU. A "mute off" flag does not exist by itself. When mute flag is set, volume and pitch of Sound Effect A (port 1) and Sound Effect B (port 2) must be set.

SGB Command 09h - SOU_TRN
Used to transfer sound code or data to SNES Audio Processing Unit memory (APU-RAM).
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1-F   Not used (zero)
The sound code/data is sent by VRAM-Transfer (4 KBytes).
  000      One (or two ???) 16bit expression(s ???) indicating the
           transfer destination address and transfer length.
  ...-...  Transfer Data
  ...-FFF  Remaining bytes not used
Possible destinations in APU-RAM are:
  0400h-2AFFh  APU-RAM Program Area (9.75KBytes)
  2B00h-4AFFh  APU-RAM Sound Score Area (8Kbytes)
  4DB0h-EEFFh  APU-RAM Sampling Data Area (40.25 Kbytes)
This function may be used to take control of the SNES sound chip, and/or to access the SNES MIDI engine. In either case it requires deeper knowledge of SNES sound programming.

SGB Sound Effect A/B Tables
Below lists the digital sound effects that are pre-defined in the SGB/SNES BIOS, and which can be used with the SGB "SOUND" Command.
Effect A and B may be simultaneously reproduced.
The P-column indicates the recommended Pitch value, the V-column indicates the numbers of Voices used. Sound Effect A uses voices 6,7. Sound Effect B uses voices 0,1,4,5. Effects that use less voices will use only the upper voices (eg. 4,5 for Effect B with only two voices).

Sound Effect A Flag Table

  Code Description             P V     Code Description             P V
  00  Dummy flag, re-trigger   - 2     18  Fast Jump                3 1
  80  Effect A, stop/silent    - 2     19  Jet (rocket) takeoff     0 1
  01  Nintendo                 3 1     1A  Jet (rocket) landing     0 1
  02  Game Over                3 2     1B  Cup breaking             2 2
  03  Drop                     3 1     1C  Glass breaking           1 2
  04  OK ... A                 3 2     1D  Level UP                 2 2
  05  OK ... B                 3 2     1E  Insert air               1 1
  06  Select...A               3 2     1F  Sword swing              1 1
  07  Select...B               3 1     20  Water falling            2 1
  08  Select...C               2 2     21  Fire                     1 1
  09  Mistake...Buzzer         2 1     22  Wall collapsing          1 2
  0A  Catch Item               2 2     23  Cancel                   1 2
  0B  Gate squeaks 1 time      2 2     24  Walking                  1 2
  0C  Explosion...small        1 2     25  Blocking strike          1 2
  0D  Explosion...medium       1 2     26  Picture floats on & off  3 2
  0E  Explosion...large        1 2     27  Fade in                  0 2
  0F  Attacked...A             3 1     28  Fade out                 0 2
  10  Attacked...B             3 2     29  Window being opened      1 2
  11  Hit (punch)...A          0 2     2A  Window being closed      0 2
  12  Hit (punch)...B          0 2     2B  Big Laser                3 2
  13  Breath in air            3 2     2C  Stone gate closes/opens  0 2
  14  Rocket Projectile...A    3 2     2D  Teleportation            3 1
  15  Rocket Projectile...B    3 2     2E  Lightning                0 2
  16  Escaping Bubble          2 1     2F  Earthquake               0 2
  17  Jump                     3 1     30  Small Laser              2 2
Sound effect A is used for formanto sounds (percussion sounds).

Sound Effect B Flag Table

  Code Description             P V     Code Description             P V
  00  Dummy flag, re-trigger   - 4     0D  Waterfall                2 2
  80  Effect B, stop/silent    - 4     0E  Small character running  3 1
  01  Applause...small group   2 1     0F  Horse running            3 1
  02  Applause...medium group  2 2     10  Warning sound            1 1
  03  Applause...large group   2 4     11  Approaching car          0 1
  04  Wind                     1 2     12  Jet flying               1 1
  05  Rain                     1 1     13  UFO flying               2 1
  06  Storm                    1 3     14  Electromagnetic waves    0 1
  07  Storm with wind/thunder  2 4     15  Score UP                 3 1
  08  Lightning                0 2     16  Fire                     2 1
  09  Earthquake               0 2     17  Camera shutter, formanto 3 4
  0A  Avalanche                0 2     18  Write, formanto          0 1
  0B  Wave                     0 1     19  Show up title, formanto  0 1
  0C  River                    3 2
Sound effect B is mainly used for looping sounds (sustained sounds).

SGB System Control Commands

SGB Command 17h - MASK_EN
Used to mask the gameboy window, among others this can be used to freeze the gameboy screen before transferring data through VRAM (the SNES then keeps displaying the gameboy screen, even though VRAM doesn't contain meaningful display information during the transfer).
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1     Gameboy Screen Mask (0-3)
          0  Cancel Mask   (Display activated)
          1  Freeze Screen (Keep displaying current picture)
          2  Blank Screen  (Black)
          3  Blank Screen  (Color 0)
  2-F   Not used (zero)
Freezing works only if the SNES has stored a picture, ie. if necessary wait one or two frames before freezing (rather than freezing directly after having displayed the picture).
The Cancel Mask function may be also invoked (optionally) by completion of PAL_SET and ATTR_SET commands.

SGB Command 0Ch - ATRC_EN
Used to enable/disable Attraction mode. It is totally unclear what an attraction mode is ???, but it is enabled by default.
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     Attraction Disable  (0=Enable, 1=Disable)
  2-F   Not used (zero)

SGB Command 0Dh - TEST_EN
Used to enable/disable test mode for "SGB-CPU variable clock speed function". This function is disabled by default.
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     Test Mode Enable    (0=Disable, 1=Enable)
  2-F   Not used (zero)
Maybe intended to determine whether SNES operates at 50Hz or 60Hz display refresh rate ??? Possibly result can be read-out from joypad register ???

SGB Command 0Eh - ICON_EN
Used to enable/disable ICON function. Possibly meant to enable/disable SGB/SNES popup menues which might otherwise activated during gameboy game play. By default all functions are enabled (0).
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     Disable Bits
          Bit 0 - Use of SGB-Built-in Color Palettes    (1=Disable)
          Bit 1 - Controller Set-up Screen    (0=Enable, 1=Disable)
          Bit 2 - SGB Register File Transfer (0=Receive, 1=Disable)
          Bit 3-6 - Not used (zero)
  2-F   Not used (zero)
Above Bit 2 will suppress all further packets/commands when set, this might be useful when starting a monochrome game from inside of the SGB-menu of a multi-gamepak which contains a collection of different games.

SGB Command 0Fh - DATA_SND
Used to write one or more bytes directly into SNES Work RAM.
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     SNES Destination Address, low
  2     SNES Destination Address, high
  3     SNES Destination Address, bank number
  4     Number of bytes to write (01h-0Bh)
  5     Data Byte #1
  6     Data Byte #2 (if any)
  7     Data Byte #3 (if any)
  etc.
Unused bytes at the end of the packet should be set to zero, this function is restricted to a single packet, so that not more than 11 bytes can be defined at once.
Free Addresses in SNES memory are Bank 0 1800h-1FFFh, Bank 7Fh 0000h-FFFFh.

SGB Command 10h - DATA_TRN
Used to transfer binary code or data directly into SNES RAM.
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     SNES Destination Address, low
  2     SNES Destination Address, high
  3     SNES Destination Address, bank number
  4-F   Not used (zero)
The data is sent by VRAM-Transfer (4 KBytes).
  000-FFF  Data
Free Addresses in SNES memory are Bank 0 1800h-1FFFh, Bank 7Fh 0000h-FFFFh. The transfer length is fixed at 4KBytes ???, so that directly writing to the free 2KBytes at 0:1800h would be a not so good idea ???

SGB Command 12h - JUMP
Used to set the SNES program counter to a specified address. Optionally, it may be used to set a new address for the SNES NMI handler, the NMI handler remains unchanged if all bytes 4-6 are zero.
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     SNES Program Counter, low
  2     SNES Program Counter, high
  3     SNES Program Counter, bank number
  4     SNES NMI Handler, low
  5     SNES NMI Handler, high
  6     SNES NMI Handler, bank number
  7-F   Not used, zero
Note: The game "Space Invaders 94" uses this function when selecting "Arcade mode" to execute SNES program code which has been previously transferred from the SGB to the SNES. The type of the CPU which is used in the SNES is unknown ???

SGB Multiplayer Command

SGB Command 11h - MLT_REQ
Used to request multiplayer mode (ie. input from more than one joypad).
Because this function provides feedback from the SGB/SNES to the gameboy program, it is also used to detect SGB hardware.
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     Multiplayer Control (0-3) (Bit0=Enable, Bit1=Two/Four Players)
          0 = One player
          1 = Two players
          3 = Four players
  2-F   Not used (zero)
In one player mode, the second joypad (if any) is used for the SGB system program. In two player mode, both joypads are used for the game. Because SNES have only two joypad sockets, four player mode requires an external "Multiplayer 5" adapter.

Reading Multiple Controllers (Joypads)
When having enabled multiple controllers by MLT_REQ, data for each joypad can be read out through JOYPAD register (FF00) as follows: First set P14 and P15 both HIGH (deselect both Buttons and Cursor keys), you can now read the lower 4bits of FF00 which indicate the joypad ID for the following joypad input:
  0Fh  Joypad 1
  0Eh  Joypad 2
  0Dh  Joypad 3
  0Ch  Joypad 4
Next, set P14 and P15 low (one after each other) to select Buttons and Cursor lines, and read-out joypad state as normally. When completed, set P14 and P15 back HIGH, this automatically increments the joypad number (or restarts counting once reached the lastmost joypad). Repeat the procedure until you have read-out states for all two (or four) joypads.

SGB Border and OBJ Commands

SGB Command 13h - CHR_TRN
Used to transfer tile data (characters) to SNES Tile memory in VRAM. This normally used to define BG tiles for the SGB Border (see PCT_TRN), but might be also used to define moveable SNES foreground sprites (see OBJ_TRN).
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1     Tile Transfer Destination
          Bit 0   - Tile Numbers   (0=Tiles 00h-7Fh, 1=Tiles 80h-FFh)
          Bit 1   - Tile Type      (0=BG Tiles, 1=OBJ Tiles)
          Bit 2-7 - Not used (zero)
  2-F   Not used (zero)
The tile data is sent by VRAM-Transfer (4 KBytes).
  000-FFF  Bitmap data for 128 Tiles
Each tile occupies 16bytes (8x8 pixels, 16 colors each).
When intending to transfer more than 128 tiles, call this function twice (once for tiles 00h-7Fh, and once for tiles 80h-FFh). Note: The BG/OBJ Bit seems to have no effect and writes to the same VRAM addresses for both BG and OBJ ???

SGB Command 14h - PCT_TRN
Used to transfer tile map data and palette data to SNES BG Map memory in VRAM to be used for the SGB border. The actual tiles must be separately transferred by using the CHR_TRN function.
  Byte  Content
  0     Command*8+Length    (fixed length=1)
  1-F   Not used (zero)
The map data is sent by VRAM-Transfer (4 KBytes).
  000-7FF  BG Map 32x32 Entries of 16bit each (2048 bytes)
  800-87F  BG Palette Data (Palettes 4-7, each 16 colors of 16bits each)
  880-FFF  Not used, don't care
Each BG Map Entry consists of a 16bit value as such:
  Bit 0-9   - Character Number (use only 00h-FFh, upper 2 bits zero)
  Bit 10-12 - Palette Number   (use only 4-7, officially use only 4-6)
  Bit 13    - BG Priority      (use only 0)
  Bit 14    - X-Flip           (0=Normal, 1=Mirror horizontally)
  Bit 15    - Y-Flip           (0=Normal, 1=Mirror vertically)
Even though 32x32 map entries are transferred, only upper 32x28 are actually used (256x224 pixels, SNES screen size). The 20x18 entries in the center of the 32x28 area should be set to 0000h as transparent space for the gameboy window to be displayed inside. Reportedly, non-transparent border data will cover the gameboy window.

SGB Command 18h - OBJ_TRN
Used to transfer OBJ attributes to SNES OAM memory. Unlike all other functions with the ending _TRN, this function does not use the usual one-shot 4KBytes VRAM transfer method.
Instead, when enabled (below execute bit set), data is permanently (each frame) read out from the lower character line of the gameboy screen. To suppress garbage on the display, the lower line is masked, and only the upper 20x17 characters of the gameboy window are used - the masking method is unknwon - frozen, black, or recommended to be covered by the SGB border, or else ??? Also, when the function is enabled, "system attract mode is not performed" - whatever that means ???
  Byte  Content
  0     Command*8+Length (fixed length=1)
  1     Control Bits
          Bit 0   - SNES OBJ Mode enable (0=Cancel, 1=Enable)
          Bit 1   - Change OBJ Color     (0=No, 1=Use definitions below)
          Bit 2-7 - Not used (zero)
  2-3   System Color Palette Number for OBJ Palette 4 (0-511)
  4-5   System Color Palette Number for OBJ Palette 5 (0-511)
  6-7   System Color Palette Number for OBJ Palette 6 (0-511)
  8-9   System Color Palette Number for OBJ Palette 7 (0-511)
          These color entries are ignored if above Control Bit 1 is zero.
          Because each OBJ palette consists of 16 colors, four system
          palette entries (of 4 colors each) are transferred into each
          OBJ palette. The system palette numbers are not required to be
          aligned to a multiple of four, and will wrap to palette number
          0 when exceeding 511. For example, a value of 511 would copy
          system palettes 511, 0, 1, 2 to the SNES OBJ palette.
  A-F   Not used (zero)
The recommended method is to "display" gameboy BG tiles F9h..FFh from left to right as first 7 characters of the bottom-most character line of the gameboy screen. As for normal 4KByte VRAM transfers, this area should not be scrolled, should not be overlapped by gameboy OBJs, and the gameboy BGP palette register should be set up properly. By following that method, SNES OAM data can be defined in the 70h bytes of the gameboy BG tile memory at following addresses:
  8F90-8FEF  SNES OAM, 24 Entries of 4 bytes each (96 bytes)
  8FF0-8FF5  SNES OAM MSBs, 24 Entries of 2 bits each (6 bytes)
  8FF6-8FFF  Not used, don't care (10 bytes)
The format of SNES OAM Entries is:
  Byte 0  OBJ X-Position (0-511, MSB is separately stored, see below)
  Byte 1  OBJ Y-Position (0-255)
  Byte 2-3  Attributes (16bit)
    Bit 0-8    Tile Number     (use only 00h-FFh, upper bit zero)
    Bit 9-11   Palette Number  (use only 4-7)
    Bit 12-13  OBJ Priority    (use only 3)
    Bit 14     X-Flip          (0=Normal, 1=Mirror horizontally)
    Bit 15     Y-Flip          (0=Normal, 1=Mirror vertically)
The format of SNES OAM MSB Entries is:
  Actually, the format is unknown ??? However, 2 bits are used per entry:
  One bit is the most significant bit of the OBJ X-Position.
  The other bit specifies the OBJ size (8x8 or 16x16 pixels).

CPU Registers and Flags

Registers

  16bit Hi   Lo   Name/Function
  AF    A    -    Accumulator & Flags
  BC    B    C    BC
  DE    D    E    DE
  HL    H    L    HL
  SP    -    -    Stack Pointer
  PC    -    -    Program Counter/Pointer
As shown above, most registers can be accessed either as one 16bit register, or as two separate 8bit registers.

The Flag Register (lower 8bit of AF register)

  Bit  Name  Set Clr  Expl.
  7    zf    Z   NZ   Zero Flag
  6    n     -   -    Add/Sub-Flag (BCD)
  5    h     -   -    Half Carry Flag (BCD)
  4    cy    C   NC   Carry Flag
  3-0  -     -   -    Not used (always zero)
Conatins the result from the recent instruction which has affected flags.

The Zero Flag (Z)
This bit becomes set (1) if the result of an operation has been zero (0). Used for conditional jumps.

The Carry Flag (C, or Cy)
Becomes set when the result of an addition became bigger than FFh (8bit) or FFFFh (16bit). Or when the result of a subtraction or comparision became less than zero (much as for Z80 and 80x86 CPUs, but unlike as for 65XX and ARM CPUs). Also the flag becomes set when a rotate/shift operation has shifted-out a "1"-bit.
Used for conditional jumps, and for instructions such like ADC, SBC, RL, RLA, etc.

The BCD Flags (N, H)
These flags are (rarely) used for the DAA instruction only, N Indicates whether the previous instruction has been an addition or subtraction, and H indicates carry for lower 4bits of the result, also for DAA, the C flag must indicate carry for upper 8bits.
After adding/subtracting two BCD numbers, DAA is intended to convert the result into BCD format; BCD numbers are ranged from 00h to 99h rather than 00h to FFh.
Because C and H flags must contain carry-outs for each digit, DAA cannot be used for 16bit operations (which have 4 digits), or for INC/DEC operations (which do not affect C-flag).

CPU Instruction Set

Tables below specify the mnemonic, opcode bytes, clock cycles, affected flags (ordered as znhc), and explanatation.
The timings assume a CPU clock frequency of 4.194304 MHz (or 8.4
MHz for CGB in double speed mode), as all gameboy timings are divideable
by 4, many people specify timings and clock frequency divided by 4.

GMB 8bit-Loadcommands

  ld   r,r         xx         4 ---- r=r
  ld   r,n         xx nn      8 ---- r=n
  ld   r,(HL)      xx         8 ---- r=(HL)
  ld   (HL),r      7x         8 ---- (HL)=r
  ld   (HL),n      36 nn     12 ----
  ld   A,(BC)      0A         8 ----
  ld   A,(DE)      1A         8 ----
  ld   A,(nn)      FA        16 ----
  ld   (BC),A      02         8 ----
  ld   (DE),A      12         8 ----
  ld   (nn),A      EA        16 ----
  ld   A,(FF00+n)  F0 nn     12 ---- read from io-port n (memory FF00+n)
  ld   (FF00+n),A  E0 nn     12 ---- write to io-port n (memory FF00+n)
  ld   A,(FF00+C)  F2         8 ---- read from io-port C (memory FF00+C)
  ld   (FF00+C),A  E2         8 ---- write to io-port C (memory FF00+C)
  ldi  (HL),A      22         8 ---- (HL)=A, HL=HL+1
  ldi  A,(HL)      2A         8 ---- A=(HL), HL=HL+1
  ldd  (HL),A      32         8 ---- (HL)=A, HL=HL-1
  ldd  A,(HL)      3A         8 ---- A=(HL), HL=HL-1

GMB 16bit-Loadcommands

  ld   rr,nn       x1 nn nn  12 ---- rr=nn (rr may be BC,DE,HL or SP)
  ld   SP,HL       F9         8 ---- SP=HL
  push rr          x5        16 ---- SP=SP-2  (SP)=rr   (rr may be BC,DE,HL,AF)
  pop  rr          x1        12 (AF) rr=(SP)  SP=SP+2   (rr may be BC,DE,HL,AF)

GMB 8bit-Arithmetic/logical Commands

  add  A,r         8x         4 z0hc A=A+r
  add  A,n         C6 nn      8 z0hc A=A+n
  add  A,(HL)      86         8 z0hc A=A+(HL)
  adc  A,r         8x         4 z0hc A=A+r+cy
  adc  A,n         CE nn      8 z0hc A=A+n+cy
  adc  A,(HL)      8E         8 z0hc A=A+(HL)+cy
  sub  r           9x         4 z1hc A=A-r
  sub  n           D6 nn      8 z1hc A=A-n
  sub  (HL)        96         8 z1hc A=A-(HL)
  sbc  A,r         9x         4 z1hc A=A-r-cy
  sbc  A,n         DE nn      8 z1hc A=A-n-cy
  sbc  A,(HL)      9E         8 z1hc A=A-(HL)-cy
  and  r           Ax         4 z010 A=A & r
  and  n           E6 nn      8 z010 A=A & n
  and  (HL)        A6         8 z010 A=A & (HL)
  xor  r           Ax         4 z000
  xor  n           EE nn      8 z000
  xor  (HL)        AE         8 z000
  or   r           Bx         4 z000 A=A | r
  or   n           F6 nn      8 z000 A=A | n
  or   (HL)        B6         8 z000 A=A | (HL)
  cp   r           Bx         4 z1hc compare A-r
  cp   n           FE nn      8 z1hc compare A-n
  cp   (HL)        BE         8 z1hc compare A-(HL)
  inc  r           xx         4 z0h- r=r+1
  inc  (HL)        34        12 z0h- (HL)=(HL)+1
  dec  r           xx         4 z1h- r=r-1
  dec  (HL)        35        12 z1h- (HL)=(HL)-1
  daa              27         4 z-0x decimal adjust akku
  cpl              2F         4 -11- A = A xor FF

GMB 16bit-Arithmetic/logical Commands

  add  HL,rr     x9           8 -0hc HL = HL+rr     ;rr may be BC,DE,HL,SP
  inc  rr        x3           8 ---- rr = rr+1      ;rr may be BC,DE,HL,SP
  dec  rr        xB           8 ---- rr = rr-1      ;rr may be BC,DE,HL,SP
  add  SP,dd     E8          16 00hc SP = SP +/- dd ;dd is 8bit signed number
  ld   HL,SP+dd  F8          12 00hc HL = SP +/- dd ;dd is 8bit signed number

GMB Rotate- und Shift-Commands

  rlca           07           4 000c rotate akku left
  rla            17           4 000c rotate akku left through carry
  rrca           0F           4 000c rotate akku right
  rra            1F           4 000c rotate akku right through carry
  rlc  r         CB 0x        8 z00c rotate left
  rlc  (HL)      CB 06       16 z00c rotate left
  rl   r         CB 1x        8 z00c rotate left through carry
  rl   (HL)      CB 16       16 z00c rotate left through carry
  rrc  r         CB 0x        8 z00c rotate right
  rrc  (HL)      CB 0E       16 z00c rotate right
  rr   r         CB 1x        8 z00c rotate right through carry
  rr   (HL)      CB 1E       16 z00c rotate right through carry
  sla  r         CB 2x        8 z00c shift left arithmetic (b0=0)
  sla  (HL)      CB 26       16 z00c shift left arithmetic (b0=0)
  swap r         CB 3x        8 z000 exchange low/hi-nibble
  swap (HL)      CB 36       16 z000 exchange low/hi-nibble
  sra  r         CB 2x        8 z00c shift right arithmetic (b7=b7)
  sra  (HL)      CB 2E       16 z00c shift right arithmetic (b7=b7)
  srl  r         CB 3x        8 z00c shift right logical (b7=0)
  srl  (HL)      CB 3E       16 z00c shift right logical (b7=0)

GMB Singlebit Operation Commands

  bit  n,r       CB xx        8 z01- test bit n
  bit  n,(HL)    CB xx       12 z01- test bit n
  set  n,r       CB xx        8 ---- set bit n
  set  n,(HL)    CB xx       16 ---- set bit n
  res  n,r       CB xx        8 ---- reset bit n
  res  n,(HL)    CB xx       16 ---- reset bit n

GMB CPU-Controlcommands

  ccf            3F           4 -00c cy=cy xor 1
  scf            37           4 -001 cy=1
  nop            00           4 ---- no operation
  halt           76         N*4 ---- halt until interrupt occurs (low power)
  stop           10 00        ? ---- low power standby mode (VERY low power)
  di             F3           4 ---- disable interrupts, IME=0
  ei             FB           4 ---- enable interrupts, IME=1

GMB Jumpcommands

  jp   nn        C3 nn nn    16 ---- jump to nn, PC=nn
  jp   HL        E9           4 ---- jump to HL, PC=HL
  jp   f,nn      xx nn nn 16;12 ---- conditional jump if nz,z,nc,c
  jr   PC+dd     18 dd       12 ---- relative jump to nn (PC=PC+/-7bit)
  jr   f,PC+dd   xx dd     12;8 ---- conditional relative jump if nz,z,nc,c
  call nn        CD nn nn    24 ---- call to nn, SP=SP-2, (SP)=PC, PC=nn
  call f,nn      xx nn nn 24;12 ---- conditional call if nz,z,nc,c
  ret            C9          16 ---- return, PC=(SP), SP=SP+2
  ret  f         xx        20;8 ---- conditional return if nz,z,nc,c
  reti           D9          16 ---- return and enable interrupts (IME=1)
  rst  n         xx          16 ---- call to 00,08,10,18,20,28,30,38

CPU Comparision with Z80

Comparision with 8080
Basically, the gameboy CPU works more like an older 8080 CPU rather than like a more powerful Z80 CPU. It is, however, supporting CB-prefixed instructions. Also, all known gameboy assemblers using the more obvious Z80-style syntax, rather than the chaotic 8080-style syntax.

Comparision with Z80
Any DD-, ED-, and FD-prefixed instructions are missing, that means no IX-, IY-registers, no block commands, and some other missing commands.
All exchange instructions have been removed (including total absence of second register set), 16bit memory accesses are mostly missing, and 16bit arithmetic functions are heavily cut-down.
The gameboy has no IN/OUT instructions, instead I/O ports are accessed directly by normal LD instructions, or by special LD (FF00+n) opcodes.
The sign and parity/overflow flags have been removed.
The gameboy operates approximately as fast as a 4MHz Z80 (8MHz in CGB double speed mode), execution time of all instructions has been rounded up to a multiple of 4 cycles though.

Moved, Removed, and Added Opcodes

  Opcode  Z80             GMB
  ---------------------------------------
  08      EX   AF,AF      LD   (nn),SP
  10      DJNZ PC+dd      STOP
  22      LD   (nn),HL    LDI  (HL),A
  2A      LD   HL,(nn)    LDI  A,(HL)
  32      LD   (nn),A     LDD  (HL),A
  3A      LD   A,(nn)     LDD  A,(HL)
  D3      OUT  (n),A      -
  D9      EXX             RETI
  DB      IN   A,(n)      -
  DD      <IX>            -
  E0      RET  PO         LD   (FF00+n),A
  E2      JP   PO,nn      LD   (FF00+C),A
  E3      EX   (SP),HL    -
  E4      CALL P0,nn      -
  E8      RET  PE         ADD  SP,dd
  EA      JP   PE,nn      LD   (nn),A
  EB      EX   DE,HL      -
  EC      CALL PE,nn      -
  ED      <pref>          -
  F0      RET  P          LD   A,(FF00+n)
  F2      JP   P,nn       LD   A,(FF00+C)
  F4      CALL P,nn       -
  F8      RET  M          LD   HL,SP+dd
  FA      JP   M,nn       LD   A,(nn)
  FC      CALL M,nn       -
  FD      <IY>            -
  CB3X    SLL  r/(HL)     SWAP r/(HL)
Note: The unused (-) opcodes will lock-up the gameboy CPU when used.

The Cartridge Header

An internal information area is located at 0100-014F in
each cartridge. It contains the following values:

0100-0103 - Entry Point
After displaying the Nintendo Logo, the built-in boot procedure jumps to this address (100h), which should then jump to the actual main program in the cartridge. Usually this 4 byte area contains a NOP instruction, followed by a JP 0150h instruction. But not always.

0104-0133 - Nintendo Logo
These bytes define the bitmap of the Nintendo logo that is displayed when the gameboy gets turned on. The hexdump of this bitmap is:
  CE ED 66 66 CC 0D 00 0B 03 73 00 83 00 0C 00 0D
  00 08 11 1F 88 89 00 0E DC CC 6E E6 DD DD D9 99
  BB BB 67 63 6E 0E EC CC DD DC 99 9F BB B9 33 3E
The gameboys boot procedure verifies the content of this bitmap (after it has displayed it), and LOCKS ITSELF UP if these bytes are incorrect. A CGB verifies only the first 18h bytes of the bitmap, but others (for example a pocket gameboy) verify all 30h bytes.

0134-0143 - Title
Title of the game in UPPER CASE ASCII. If it is less than 16 characters then the remaining bytes are filled with 00's. When inventing the CGB, Nintendo has reduced the length of this area to 15 characters, and some months later they had the fantastic idea to reduce it to 11 characters only. The new meaning of the ex-title bytes is described below.

013F-0142 - Manufacturer Code
In older cartridges this area has been part of the Title (see above), in newer cartridges this area contains an 4 character uppercase manufacturer code. Purpose and Deeper Meaning unknown.

0143 - CGB Flag
In older cartridges this byte has been part of the Title (see above). In CGB cartridges the upper bit is used to enable CGB functions. This is required, otherwise the CGB switches itself into Non-CGB-Mode. Typical values are:
  80h - Game supports CGB functions, but works on old gameboys also.
  C0h - Game works on CGB only (physically the same as 80h).
Values with Bit 7 set, and either Bit 2 or 3 set, will switch the gameboy into a special non-CGB-mode with uninitialized palettes. Purpose unknown, eventually this has been supposed to be used to colorize monochrome games that include fixed palette data at a special location in ROM.

0144-0145 - New Licensee Code
Specifies a two character ASCII licensee code, indicating the company or publisher of the game. These two bytes are used in newer games only (games that have been released after the SGB has been invented). Older games are using the header entry at 014B instead.

0146 - SGB Flag
Specifies whether the game supports SGB functions, common values are:
  00h = No SGB functions (Normal Gameboy or CGB only game)
  03h = Game supports SGB functions
The SGB disables its SGB functions if this byte is set to another value than 03h.

0147 - Cartridge Type
Specifies which Memory Bank Controller (if any) is used in the cartridge, and if further external hardware exists in the cartridge.
  00h  ROM ONLY                 13h  MBC3+RAM+BATTERY
  01h  MBC1                     15h  MBC4
  02h  MBC1+RAM                 16h  MBC4+RAM
  03h  MBC1+RAM+BATTERY         17h  MBC4+RAM+BATTERY
  05h  MBC2                     19h  MBC5
  06h  MBC2+BATTERY             1Ah  MBC5+RAM
  08h  ROM+RAM                  1Bh  MBC5+RAM+BATTERY
  09h  ROM+RAM+BATTERY          1Ch  MBC5+RUMBLE
  0Bh  MMM01                    1Dh  MBC5+RUMBLE+RAM
  0Ch  MMM01+RAM                1Eh  MBC5+RUMBLE+RAM+BATTERY
  0Dh  MMM01+RAM+BATTERY        FCh  POCKET CAMERA
  0Fh  MBC3+TIMER+BATTERY       FDh  BANDAI TAMA5
  10h  MBC3+TIMER+RAM+BATTERY   FEh  HuC3
  11h  MBC3                     FFh  HuC1+RAM+BATTERY
  12h  MBC3+RAM

0148 - ROM Size
Specifies the ROM Size of the cartridge. Typically calculated as "32KB shl N".
  00h -  32KByte (no ROM banking)
  01h -  64KByte (4 banks)
  02h - 128KByte (8 banks)
  03h - 256KByte (16 banks)
  04h - 512KByte (32 banks)
  05h -   1MByte (64 banks)  - only 63 banks used by MBC1
  06h -   2MByte (128 banks) - only 125 banks used by MBC1
  07h -   4MByte (256 banks)
  52h - 1.1MByte (72 banks)
  53h - 1.2MByte (80 banks)
  54h - 1.5MByte (96 banks)

0149 - RAM Size
Specifies the size of the external RAM in the cartridge (if any).
  00h - None
  01h - 2 KBytes
  02h - 8 Kbytes
  03h - 32 KBytes (4 banks of 8KBytes each)
When using a MBC2 chip 00h must be specified in this entry, even though the MBC2 includes a built-in RAM of 512 x 4 bits.

014A - Destination Code
Specifies if this version of the game is supposed to be sold in japan, or anywhere else. Only two values are defined.
  00h - Japanese
  01h - Non-Japanese

014B - Old Licensee Code
Specifies the games company/publisher code in range 00-FFh. A value of 33h signalizes that the New License Code in header bytes 0144-0145 is used instead.
(Super GameBoy functions won't work if <> $33.)

014C - Mask ROM Version number
Specifies the version number of the game. That is usually 00h.

014D - Header Checksum
Contains an 8 bit checksum across the cartridge header bytes 0134-014C. The checksum is calculated as follows:
  x=0:FOR i=0134h TO 014Ch:x=x-MEM[i]-1:NEXT
The lower 8 bits of the result must be the same than the value in this entry. The GAME WON'T WORK if this checksum is incorrect.

014E-014F - Global Checksum
Contains a 16 bit checksum (upper byte first) across the whole cartridge ROM. Produced by adding all bytes of the cartridge (except for the two checksum bytes). The Gameboy doesn't verify this checksum.

Memory Bank Controllers

As the gameboys 16 bit address bus offers only limited space for ROM and RAM addressing, many games are using Memory Bank Controllers (MBCs) to expand the available address space by bank switching. These MBC chips are located in the game cartridge (ie. not in the gameboy itself), several different MBC types are available:

None (32KByte ROM only)
MBC1 (max 2MByte ROM and/or 32KByte RAM)
MBC2 (max 256KByte ROM and 512x4 bits RAM)
MBC3 (max 2MByte ROM and/or 32KByte RAM and Timer)
HuC1 (MBC with Infrared Controller)

MBC Timing Issues

In each cartridge, the required (or preferred) MBC type should be specified in byte at 0147h of the ROM. (As described in the chapter about The Cartridge Header.)

None (32KByte ROM only)

Small games of not more than 32KBytes ROM do not require a MBC chip for ROM banking. The ROM is directly mapped to memory at 0000-7FFFh. Optionally up to 8KByte of RAM could be connected at A000-BFFF, even though that could require a tiny MBC-like circuit, but no real MBC chip.

MBC1 (max 2MByte ROM and/or 32KByte RAM)

This is the first MBC chip for the gameboy. Any newer MBC chips are working similiar, so that is relative easy to upgrade a program from one MBC chip to another - or even to make it compatible to several different types of MBCs.

Note that the memory in range 0000-7FFF is used for both reading from ROM, and for writing to the MBCs Control Registers.

0000-3FFF - ROM Bank 00 (Read Only)
This area always contains the first 16KBytes of the cartridge ROM.

4000-7FFF - ROM Bank 01-7F (Read Only)
This area may contain any of the further 16KByte banks of the ROM, allowing to address up to 125 ROM Banks (almost 2MByte). As described below, bank numbers 20h, 40h, and 60h cannot be used, resulting in the odd amount of 125 banks.

A000-BFFF - RAM Bank 00-03, if any (Read/Write)
This area is used to address external RAM in the cartridge (if any). External RAM is often battery buffered, allowing to store game positions or high score tables, even if the gameboy is turned off, or if the cartridge is removed from the gameboy. Available RAM sizes are: 2KByte (at A000-A7FF), 8KByte (at A000-BFFF), and 32KByte (in form of four 8K banks at A000-BFFF).

0000-1FFF - RAM Enable (Write Only)
Before external RAM can be read or written, it must be enabled by writing to this address space. It is recommended to disable external RAM after accessing it, in order to protect its contents from damage during power down of the gameboy. Usually the following values are used:
  00h  Disable RAM (default)
  0Ah  Enable RAM
Practically any value with 0Ah in the lower 4 bits enables RAM, and any other value disables RAM.

2000-3FFF - ROM Bank Number (Write Only)
Writing to this address space selects the lower 5 bits of the ROM Bank Number (in range 01-1Fh). When 00h is written, the MBC translates that to bank 01h also. That doesn't harm so far, because ROM Bank 00h can be always directly accessed by reading from 0000-3FFF.
But (when using the register below to specify the upper ROM Bank bits), the same happens for Bank 20h, 40h, and 60h. Any attempt to address these ROM Banks will select Bank 21h, 41h, and 61h instead.

4000-5FFF - RAM Bank Number - or - Upper Bits of ROM Bank Number (Write Only) This 2bit register can be used to select a RAM Bank in range from 00-03h, or to specify the upper two bits (Bit 5-6) of the ROM Bank number, depending on the current ROM/RAM Mode. (See below.)

6000-7FFF - ROM/RAM Mode Select (Write Only)
This 1bit Register selects whether the two bits of the above register should be used as upper two bits of the ROM Bank, or as RAM Bank Number.
  00h = ROM Banking Mode (up to 8KByte RAM, 2MByte ROM) (default)
  01h = RAM Banking Mode (up to 32KByte RAM, 512KByte ROM)
The program may freely switch between both modes, the only limitiation is that only RAM Bank 00h can be used during Mode 0, and only ROM Banks 00-1Fh can be used during Mode 1.

MBC2 (max 256KByte ROM and 512x4 bits RAM)

0000-3FFF - ROM Bank 00 (Read Only)
Same as for MBC1.

4000-7FFF - ROM Bank 01-0F (Read Only)
Same as for MBC1, but only a total of 16 ROM banks is supported.

A000-A1FF - 512x4bits RAM, built-in into the MBC2 chip (Read/Write)
The MBC2 doesn't support external RAM, instead it includes 512x4 bits of built-in RAM (in the MBC2 chip itself). It still requires an external battery to save data during power-off though.
As the data consists of 4bit values, only the lower 4 bits of the "bytes" in this memory area are used.

0000-1FFF - RAM Enable (Write Only)
The least significant bit of the upper address byte must be zero to enable/disable cart RAM. For example the following addresses can be used to enable/disable cart RAM: 0000-00FF, 0200-02FF, 0400-04FF, ..., 1E00-1EFF.
The suggested address range to use for MBC2 ram enable/disable is 0000-00FF.

2000-3FFF - ROM Bank Number (Write Only)
Writing a value (XXXXBBBB - X = Don't cares, B = bank select bits) into 2000-3FFF area will select an appropriate ROM bank at 4000-7FFF.

The least significant bit of the upper address byte must be one to select a ROM bank. For example the following addresses can be used to select a ROM bank: 2100-21FF, 2300-23FF, 2500-25FF, ..., 3F00-3FFF.
The suggested address range to use for MBC2 rom bank selection is 2100-21FF.

MBC3 (max 2MByte ROM and/or 32KByte RAM and Timer)

Beside for the ability to access up to 2MB ROM (128 banks), and 32KB RAM (4 banks), the MBC3 also includes a built-in Real Time Clock (RTC). The RTC requires an external 32.768 kHz Quartz Oscillator, and an external battery (if it should continue to tick when the gameboy is turned off).

0000-3FFF - ROM Bank 00 (Read Only)
Same as for MBC1.

4000-7FFF - ROM Bank 01-7F (Read Only)
Same as for MBC1, except that accessing banks 20h, 40h, and 60h is supported now.

A000-BFFF - RAM Bank 00-03, if any (Read/Write)
A000-BFFF - RTC Register 08-0C (Read/Write)
Depending on the current Bank Number/RTC Register selection (see below), this memory space is used to access an 8KByte external RAM Bank, or a single RTC Register.

0000-1FFF - RAM and Timer Enable (Write Only)
Mostly the same as for MBC1, a value of 0Ah will enable reading and writing to external RAM - and to the RTC Registers! A value of 00h will disable either.

2000-3FFF - ROM Bank Number (Write Only)
Same as for MBC1, except that the whole 7 bits of the RAM Bank Number are written directly to this address. As for the MBC1, writing a value of 00h, will select Bank 01h instead. All other values 01-7Fh select the corresponding ROM Banks.

4000-5FFF - RAM Bank Number - or - RTC Register Select (Write Only)
As for the MBC1s RAM Banking Mode, writing a value in range for 00h-03h maps the corresponding external RAM Bank (if any) into memory at A000-BFFF.
When writing a value of 08h-0Ch, this will map the corresponding RTC register into memory at A000-BFFF. That register could then be read/written by accessing any address in that area, typically that is done by using address A000.

6000-7FFF - Latch Clock Data (Write Only)
When writing 00h, and then 01h to this register, the current time becomes latched into the RTC registers. The latched data will not change until it becomes latched again, by repeating the write 00h->01h procedure.
This is supposed for <reading> from the RTC registers. It is proof to read the latched (frozen) time from the RTC registers, while the clock itself continues to tick in background.

The Clock Counter Registers

  08h  RTC S   Seconds   0-59 (0-3Bh)
  09h  RTC M   Minutes   0-59 (0-3Bh)
  0Ah  RTC H   Hours     0-23 (0-17h)
  0Bh  RTC DL  Lower 8 bits of Day Counter (0-FFh)
  0Ch  RTC DH  Upper 1 bit of Day Counter, Carry Bit, Halt Flag
        Bit 0  Most significant bit of Day Counter (Bit 8)
        Bit 6  Halt (0=Active, 1=Stop Timer)
        Bit 7  Day Counter Carry Bit (1=Counter Overflow)
The Halt Flag is supposed to be set before <writing> to the RTC Registers.

The Day Counter
The total 9 bits of the Day Counter allow to count days in range from 0-511 (0-1FFh). The Day Counter Carry Bit becomes set when this value overflows. In that case the Carry Bit remains set until the program does reset it.
Note that you can store an offset to the Day Counter in battery RAM. For example, every time you read a non-zero Day Counter, add this Counter to the offset in RAM, and reset the Counter to zero. This method allows to count any number of days, making your program Year-10000-Proof, provided that the cartridge gets used at least every 511 days.

Delays
When accessing the RTC Registers it is recommended to execute a 4ms delay (4 Cycles in Normal Speed Mode) between the separate accesses.

HuC1 (MBC with Infrared Controller)

This controller (made by Hudson Soft) appears to be very similar to an MBC1 with the main difference being that it supports infrared LED input / output. (Similiar to the infrared port that has been later invented in CGBs.)

The Japanese cart "Fighting Phoenix" (internal cart name: SUPER B DAMAN) is known to contain this chip.

MBC Timing Issues

Using MBCs with CGB Double Speed Mode
The MBC5 has been designed to support CGB Double Speed Mode.
There have been rumours that older MBCs (like MBC1-3) wouldn't be fast enough in that mode. If so, it might be nethertheless possible to use Double Speed during periods which use only code and data which is located in internal RAM.
However, despite of the above, my own good old selfmade MBC1-EPROM card appears to work stable and fine even in Double Speed Mode though.

Gamegenie/Shark Cheats

Game Shark and Gamegenie are external cartridge adapters that can be plugged between the gameboy and the actual game cartridge. Hexadecimal codes can be then entered for specific games, typically providing things like Infinite Sex, 255 Cigarettes, or Starting directly in Wonderland Level PRO, etc.

Gamegenie (ROM patches)
Gamegenie codes consist of nine-digit hex numbers, formatted as ABC-DEF-GHI, the meaning of the separate digits is:
  AB    New data
  FCDE  Memory address, XORed by 0F000h
  GI    Old data, XORed by 0BAh and rotated left by two
  H     Don't know, maybe checksum and/or else
The address should be located in ROM area 0000h-7FFFh, the adapter permanently compares address/old data with address/data being read by the game, and replaces that data by new data if necessary. That method (more or less) prohibits unwanted patching of wrong memory banks. Eventually it is also possible to patch external RAM ?
Newer devices reportedly allow to specify only the first six digits (optionally). As far as I rememeber, around three or four codes can be used simultaneously.

Game Shark (RAM patches)
Game Shark codes consist of eight-digit hex numbers, formatted as ABCDEFGH, the meaning of the separate digits is:
  AB    External RAM bank number
  CD    New Data
  GHEF  Memory Address (internal or external RAM, A000-DFFF)
As far as I understand, patching is implement by hooking the original VBlank interrupt handler, and re-writing RAM values each frame. The downside is that this method steals some CPU time, also, it cannot be used to patch program code in ROM.
As far as I rememeber, somewhat 10-25 codes can be used simultaneously.

Power Up Sequence

When the GameBoy is powered up, a 256 byte program starting at memory location 0 is executed. This program is located in a ROM inside the GameBoy. The first thing the program does is read the cartridge locations from $104 to $133 and place this graphic of a Nintendo logo on the screen at the top. This image is then scrolled until it is in the middle of the screen. Two musical notes are then played on the internal speaker. Again, the cartridge locations $104 to $133 are read but this time they are compared with a table in the internal rom. If any byte fails to compare, then the GameBoy stops comparing bytes and simply halts all operations. If all locations compare the same, then the GameBoy starts adding all of the bytes in the cartridge from $134 to $14d. A value of 25 decimal is added to this total. If the least significant byte of the result is a not a zero, then the GameBoy will stop doing anything. If it is a zero, then the internal ROM is disabled and cartridge program execution begins at location $100 with the following register values:

   AF=$01B0
   BC=$0013
   DE=$00D8
   HL=$014D
   Stack Pointer=$FFFE
   [$FF05] = $00   ; TIMA
   [$FF06] = $00   ; TMA
   [$FF07] = $00   ; TAC
   [$FF10] = $80   ; NR10
   [$FF11] = $BF   ; NR11
   [$FF12] = $F3   ; NR12
   [$FF14] = $BF   ; NR14
   [$FF16] = $3F   ; NR21
   [$FF17] = $00   ; NR22
   [$FF19] = $BF   ; NR24
   [$FF1A] = $7F   ; NR30
   [$FF1B] = $FF   ; NR31
   [$FF1C] = $9F   ; NR32
   [$FF1E] = $BF   ; NR33
   [$FF20] = $FF   ; NR41
   [$FF21] = $00   ; NR42
   [$FF22] = $00   ; NR43
   [$FF23] = $BF   ; NR30
   [$FF24] = $77   ; NR50
   [$FF25] = $F3   ; NR51
   [$FF26] = $F1-GB, $F0-SGB ; NR52
   [$FF40] = $91   ; LCDC
   [$FF42] = $00   ; SCY
   [$FF43] = $00   ; SCX
   [$FF45] = $00   ; LYC
   [$FF47] = $FC   ; BGP
   [$FF48] = $FF   ; OBP0
   [$FF49] = $FF   ; OBP1
   [$FF4A] = $00   ; WY
   [$FF4B] = $00   ; WX
   [$FFFF] = $00   ; IE

It is not a good idea to assume the above values will always exist. A later version GameBoy could contain different values than these at reset. Always set these registers on reset rather than assume they are as above.

Please note that GameBoy internal RAM on power up contains random data. All of the GameBoy emulators tend to set all RAM to value $00 on entry.

Cart RAM the first time it is accessed on a real GameBoy contains random data. It will only contain known data if the GameBoy code initializes it to some value.

Reducing Power Consumption

The following can be used to recude the power consumption of the gameboy, and to extend the life of the batteries.

PWR Using the HALT Instruction
PWR Using the STOP Instruction
PWR Disabeling the Sound Controller
PWR Not using CGB Double Speed Mode
PWR Using the Skills

PWR Using the HALT Instruction

It is recommended that the HALT instruction be used whenever possible to reduce power consumption & extend the life of the batteries. This command stops the system clock reducing the power consumption of both the CPU and ROM.

The CPU will remain suspended until an interrupt occurs at which point the interrupt is serviced and then the instruction immediately following the HALT is executed.

Depending on how much CPU time is required by a game, the HALT instruction can extend battery life anywhere from 5 to 50% or possibly more.

When waiting for a vblank event, this would be a BAD example:
  @@wait:
   ld   a,(0FF44h)      ;LY
   cp   a,144
   jr   nz,@@wait

A better example would be a procedure as shown below. In this case the vblank interrupt must be enabled, and your vblank interrupt procedure must set vblank_flag to a non-zero value.
   ld   hl,vblank_flag  ;hl=pointer to vblank_flag
   xor  a               ;a=0
  @@wait:               ;wait...
   halt                 ;suspend CPU - wait for ANY interrupt
   cp   a,(hl)          ;vblank flag still zero?
   jr   z,@@wait        ;wait more if zero
   ld   (hl),a          ;set vblank_flag back to zero
The vblank_flag is used to determine whether the HALT period has been terminated by a vblank interrupt, or by another interrupt. In case that your program has all other interrupts disabled, then it would be proof to replace the above procedure by a single HALT instruction.

PWR Using the STOP Instruction

The STOP instruction is intended to switch the gameboy into VERY low power standby mode. For example, a program may use this feature when it hasn't sensed keyboard input for a longer period (assuming that somebody forgot to turn off the gameboy).

Before invoking STOP, it might be required to disable Sound and Video manually (as well as IR-link port in CGB). Much like HALT, the STOP state is terminated by interrupt events - in this case this would be commonly a joypad interrupt. The joypad register might be required to be prepared for STOP either.

PWR Disabeling the Sound Controller

If your programs doesn't use sound at all (or during some periods) then write 00h to register FF26 to save 16% or more on GB power consumption.
Sound can be turned back on by writing 80h to the same register, all sound registers must be then re-initialized.
When the gameboy becomes turned on, sound is enabled by default, and must be turned off manually when not used.

PWR Not using CGB Double Speed Mode

Because CGB Double Speed mode consumes more power, it'd be recommended to use normal speed when possible.
There's limited ability to switch between both speeds, for example, a game might use normal speed in the title screen, and double speed in the game, or vice versa.
However, during speed switch the display collapses for a short moment, so that it'd be no good idea to alter speeds within active game or title screen periods.

PWR Using the Skills

Most of the above power saving methods will produce best results when using efficient and tight assembler code which requires as less CPU power as possible. Thus, experienced old-school programmers will (hopefully) produce lower power consumption, as than HLL-programming teenagers, for example.

Sprite RAM Bug

There is a flaw in the GameBoy hardware that causes trash to be written to OAM RAM if the following commands are used while their 16-bit content is in the range of $FE00 to $FEFF:
  inc rr        dec rr          ;rr = bc,de, or hl
  ldi a,(hl)    ldd a,(hl)
  ldi (hl),a    ldd (hl),a
Only sprites 1 & 2 ($FE00 & $FE04) are not affected by these instructions.

External Connectors

Cartridge Slot

  Pin   Name    Expl.
  1     VDD     Power Supply +5V DC
  2     PHI     System Clock
  3     /WR     Write
  4     /RD     Read
  5     /CS     Chip Select
  6-21  A0-A15  Address Lines
  22-29 D0-D7   Data Lines
  30    /RES    Reset signal
  31    VIN     External Sound Input
  32    GND     Ground

Link Port
Pin numbers are arranged as 2,4,6 in upper row, 1,3,5 un lower row; outside view of gameboy socket; flat side of socket upside.
Colors as used in most or all standard link cables, because SIN and SOUT are crossed, colors Red and Orange are exchanged at one cable end.
  Pin Name Color  Expl.
  1   VCC  -      +5V DC
  2   SOUT red    Data Out
  3   SIN  orange Data In
  4   P14  -      Not used
  5   SCK  green  Shift Clock
  6   GND  blue   Ground
Note: The original gameboy used larger plugs (unlike pocket gameboys and newer), linking between older/newer gameboys is possible by using cables with one large and one small plug though.

Stereo Sound Connector (3.5mm, female)

  Pin     Expl.
  Tip     Sound Left
  Middle  Sound Right
  Base    Ground

External Power Supply
...

END


GB DEV FAQs

Note#1: Any reference below to GameBoy (GB) is also a generic reference to GameBoy Colo(u)r (GBC), Super GameBoy (SGB), GameBoy Pocket (GBP), and GameBoy Classic unless otherwise specified. Any references to registers in example code use the official GB register names in uppercase with a small 'r' in front of them.

Short Cuts

Take me to Important Web Links!

GB DEV FAQs by GeeBee - Last update 00-Jan-05

General FAQs

How do I join or leave the GB development mailing list ?
What are the commonly used acronyms in GB development ?
What software do I need to get started in GB development ?
What hardware do I need to get started in GB development ?
Where can I find programming specification documents ?
Which is better C or Assembly Language (ASM) for development ?
Are there any tutorials for writing software for GB ?
What is a flash cart & do I need one ?
What software tools do official developers use ?
What hardware tools do official developers use ?
What are .GB and .GBC files ?
What are .ISX files ?
How do I convert .ISX files to .GB/.GBC files ?
Are some of the files on Intelligent Systems web page dongle protected ?
What capabilities are specific to the GBC ?
What is an MBC, when is one needed, and what are the differences between them ?
How do I become a licensed GB developer ?
Is there any way to connect my GB to the internet so that I can play Pokemon with a friend ?
Why is it that most older GB games when run on a GBC have blue & green backgrounds with red sprites but a few games have different default colors ?
Is there an IRC channel for GB development ?
What is the ROM header info in ROMs used for ?
What can you do if some of the lines of the LCD display quit working ?
Do you have to be licensed by Nintendo to make / sell carts ?
Is there really going to be a GBC2 next generation handheld ?
What games written for GB will not work correctly on GBC & why ?
How do I go about trying to sell my game/demo to a company ?
Where can a three-sided tool to open the GB be obtained ?

Bung FAQs

Where all can I order Bung products ?
How long does it take for them to ship ?
What do I need to order from Bung or a distributor ?
What type of flash carts do Bung sell and what are the specifications for these carts ?
What kind of battery life can I expect using their flash carts ?
How can I tell if I have newer or older Bung flash carts ?
Is it possible to write to the flash chip using code on the GB ?
Is it possible to use the GB Xchanger to download GB camera pictures ?
Do the Bung tools work on Windows NT ?
How do I improve write-to-cart times during program development ?
The GB Xchanger doesn't work with my parallel port. What's the problem ?
Camera FAQs

What are the specifications of the GB Camera ?
How do I transfer pictures from my GB Camera to a PC ?
Has anyone modified a GB camera to allow real-time transfer of photos to a PC using the GB link port ?
Cart FAQs

How do I enable cart RAM using software ?
What is the maximum amount of ROM & RAM that you can put in a cart ?
Which carts containing RAM also contain a battery to preserve the contents of this RAM during power off ?
Is there a list of commercial GB & GBC carts somewhere and what they contain as far as ROM, RAM, & MBC type ?
What commercial GB & GBC games have unusual or unique features ?
What commercial carts have been written using the GBDK C compiler ?
Will a GB cart work on a GBA ?
What voltage is present on the cart interface ?
What are the specs for the clock signal (pin 2) on the cart slot ?
Is it possible to design a flash cart that would allow you to program it using GB code ?
Is it possible to design a cart that displays something other than the Nintendo logo on power up ?
What are the advantages of using a cart reader with ReadPlus software over using the Bung xchanger with their flash carts ?
Where can a cart connector and the tool to open GB carts be obtained ?
Emulator FAQs

What are GB, EPROM, and IS emulators ?
Why does my code that runs on a GB emulator not run on a real GB ?
Why do colo(u)rs look washed out on some GB emulators (i.e. no$gmb, etc) but not on others ?
Graphics FAQs

I have read that the GB screen is tile based. What exactly does this mean ?
I have heard that the GBC can do more than 4 colo(u)rs per tile, has a secret high resolution mode, and can do 3D graphics in hardware. Is this true ?
How do I turn individual pixels on & off (i.e. APA graphics) instead of drawing with tiles ?
How do some games put more than 56 colo(u)rs on the screen ?
Can GBC palettes be set at any time ?
The GBC & SGB both support colo(u)r. Is graphics programming for them similar ?
What is the difference between the GB window and the GB background ?
Can any part of the window be made transparent ?
What precautions should I use when reading/writing video RAM ?
What precautions should I use when doing HDMA on a GBC ?
How do I know when an HDMA on the GBC has completed ?
What precautions should I use when doing GDMA on a GBC ?
How long does it take for a GDMA to execute on a GBC ?
I read somewhere that you must set GBC tile attributes before setting the GBC tile map. Is this true ?
I'm using the LYC interrupt as a trigger to modify the SCX and/or SCY scroll registers. I get some graphic "trash" on the screen line where the LYC occurs. Why? And how do I fix it ?
What are the video timing specs for the GB & GBC ?
Why do pictures in the background or window often appear distorted if you scroll them up or down 1 pixel each V-blank ?
Can the screen be turned off at any time & what assembly code should be used ?
Why does the GB have H-blank & V-blank periods in the video timing ?
Interrupt FAQs

What is an interrupt ?
What precautions should I use when writing interrupt code ?
Can an interrupt interrupt another interrupt ?
What is the difference between assembly commands RET & RETI ?
I can't get the HI/LO (joypad) interrupt to work. Ideas ?
Should a HI/LO interrupt occur when a button is released ?
Can interrupts occur if I enable interrupts & then quickly disable them ?
Is there anything I should know when using the LCDC interrupt ?
Link Port FAQs

What does pin 4 of the link port do ?
Can the serial link port be used to connect to other serial devices ?
I'm trying to write code to communicate with another GB using the link port. Are there any suggested algorithms for doing this ?
I get an unreliable game link connection when connecting a GB Pocket or Classic to a GBC. What might cause this ?
Is the GB link connector available anywhere ?
With the introduction of the GB Pocket the link port connector is smaller than it was on the GB Classic. Are the changes only physical ?
Is there any direct way to read or write any of the pins on the link port ?
What kind of serial input data should I expect if no GB is connected on the other end or the other unit is turned off ?
Misc Programming FAQs

Are there any disadvantages to running the GBC in double-speed mode ?
I read somewhere that there is a boot rom in the GB & GBC. Has anyone been able to read this rom ?
Are the GB & GBC 1 & 2MHz systems or 4 & 8MHz systems ?
What are all of the DMA modes & what are they used for ?
Is the GB a big or little endian system ?
Is it possible to use the Infrared capability of the GBC for a IR remote control ?
I have written a program for the GBC. Normal GB functions work but I can't get any of the GBC additional registers to work. What am I doing wrong ?
With the introduction of the GBC, the internal cart name has been made smaller. Is this correct ?
So why did Nintendo change rom address $143 from $80 to $c0 for a GBC dedicated game ?
What is the assembly language command HALT used for ?
What is the assembly language command STOP and how/when should it be used ?
Are there any undocumented bugs in the GB ?
Are there any undocumented features of the GBC ?
Does the GBC register LCDC ($ff40) bit 0 do anything ?
Does the GBC start in single or double speed mode ?
What assembly code is needed to toggle CPU speed on GBC ?
Has anyone tried to port Unix to a GB ?
What do the undocumented assembly opcodes do ?
What is high RAM ($ff80-$fffe) useful for ? If used for the GB stack, does it provide better performance ?
What does the term Metatile mean ?
Is it possible to drain the batteries quicker through poor code ?
Is it important to pad a ROM with a specific value ?
Off Topic FAQs

What are the specs of the Neo Geo Pocket Color ?
Sound FAQs

Are there any differences in the sound hardware on the GB, GBC, & SGB ?
Does it matter in which order I set the sound registers ?
What all techniques are used to play WAV files on a GB ?
I get very low volume when playing WAVs. What can I do ?
Should I disable sound if I am not using it ?
Can I dynamically modulate the volume envelope of a sound channel while it is playing ?
Can Wave Pattern RAM ($ff30-ff3f) be modified at any time ?
Is there anything I need to know about the "sweep" register ?
Are there any bugs in the GB sound hardware ?
Sprite FAQs

What size sprites does the GB support ?
Can 8*8 sprites & 8*16 sprites be used at the same time ?
What happens if you exceed the GBs limit of 10 sprites per line ?
Can you have a sprite appear above the background but below the window ?
What happens if you have one sprite appear above the background and another sprite with higher priority appear below the background ?
Software-wise, how do most games implement sprites ?
What precautions should I use when writing assembly code & using sprites ?
What precautions should I use when doing Sprite DMA ?
I have read somewhere someone mentioning "background" sprites. What are these and how do I use them ?
Is it possible to display more than 40 sprites at one time ?
How do sprite priority settings work on the GBC ?
Tool FAQs

What assemblers are available ?
Where can I get bmp/pcx/gif/jpg -> GB picture converters ?
What is a tile editor and why do I need one ?
What is a map editor and why do I need one ?
What map or tile editors are available ?
Where can I get a .WAV -> GB converter ?
How do I join or leave the GB development mailing list ?

Follow the instructions on the GB Mailing List Page.

This mailing list is only designed for GB/GBC development related discussions. Discussions of other platforms or other hand-helds should be kept to a minimum when possible.

What are the commonly used acronyms in GB development ?

ADSR - Attack, Decay, Sustain, Release (Used to define audio waveform envelopes.)
AdvGBIDE - GB IDE assembler by MegaMan_X.
AI - Artificial Intelligence
API - Application Program Interface
C3 - Carbon Copy Card
CPU - Central Processing Unit
GB - GameBoy
GBA - GameBoy Advanced (Code name for the next generation GameBoy in development.)
GBC - GameBoy Color
GBDK - C compiler for GB by Pascal Felber.
GBP - Game Boy Pocket
GBTD - Tile editor for GB by Harry Mulder.
GG - Game Genie
DMA - Direct Memory Access
EPROM - A chip that works like a ROM but is reprogrammable and requires an ultaviolet light source in order to be erased.
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
FPS - Frames Per Second
GBX - GB Xchanger made by Bung Enterprises Limited.
GDMA - General Purpose DMA
GUI - Graphical User Interface
H-blank - Horizontal video blank
HDMA - Horizontal Blanking DMA
Hicolor - A generic term referring to dynamically updating colo(u)r palettes to render a picture with up to 2048 colo(u)rs.
HuC - Hudson Controller (selects memory banks + other features)
HUD - Heads Up Display (Often refers to on screen game information at the top, bottom, or side of screen.)
IDE - Integrated Development Environment
IME - Interrupt Master Enable
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
OOP - Object Oriented Programming
RAM - Random Access Memory (read / write memory)
RGBDS - Powerful GB assembler by Carsten Sorensen.
ROM - Read Only Memory (new carts contain this)
SGB - Super Game Boy
SMC - Self Modifying Code
SSC - Super Smart Card (programmable cart, no longer made)
TASM - Table assembler for GB (not related to Turbo Assembler)
V-blank - Vertical video blank
VBK - Video Bank select register in the GBC.
VRAM - Video RAM

What software do I need to get started in GB development ?

First, you need to get an Assembly Language (ASM) compiler (often also referred to as an assembler) or GBDK compiler if you wish to program in C language instead of ASM.

Next, if you are programming in ASM you need to get a conversion program that will convert the output of the assembler into a format that is useable by the GB. (RGBFIX is a popular DOS program that many use to do this conversion.) A conversion program does such things as fix the ROM checksums & pad the ROM file to a correct size.

To test your program you will either need a gb emulator or a flash cart.

What hardware do I need to get started in GB development ?

Theoretically you can use a gb emulator for development and not require any hardware. The reality though is that no emulator is 100% like the real GB hardware and emulators will often run code that real hardware will not.

To guarantee that your code will run on real GB hardware you need to put your code into a GB cart and try it on the real GB handheld unit. To do this you can wire an EPROM into a GB cart (yourself) or buy or build a flash cart. EPROM's require an ultraviolet light source to erase them so flash carts are often a simpler solution.

Another method that some use is to connect an EPROM emulator to a GB cart.

Where can I find programming specification documents?

Check the Important Web Links section.

The best document (arguably) for GBC development information is the abc409 document. Any information that you can not locate on the GBC is usually always exactly the same as information for the older GB series. As a result, use the document described next as a GBC reference as well.

The best document for GB development is the updated Pan/kOOPa document (otherwise known as the GB Manual.)

Which is better C or Assembly Language (ASM) for development ?

Assembly language is what the majority of commercial GB games use. One reason for this is that no serious C compiler has been available for the GB until GBDK was released. Another reason is that C is terribly inefficient. A C compiler can often generate 8 to 10 times more code compared to optimized assembly code. As GB ROM cart sizes get larger this makes less of a difference unless you are talking about ROM bank 0. ROM bank 0 is a major bottle neck for many commercial games. Some code must reside in ROM bank 0. Often the amount of code that must reside in ROM bank 0 is larger than a person expects until he gets deep into a large project. Commercial GB programmers often have a hard time fitting all the code required in ROM bank 0 when using highly optimised assembly code so using C provides even more of a challenge.
Assembly code that uses the HALT command can save up to 5% battery power compared to a similar program written in GBDK even if the GBDK program uses HALT as well. The reason for more power being required is due to the larger code generated by GBDK thus causing the CPU to stay awake longer each game loop.

C for use in commercial applications is becoming more popular. It often requires less of a learning curve for development for those that already know C fairly well. With many key subroutines written in assembly language and called from C, many of the issues of code size & execution speed start to disappear. It's also an excellent means for porting C code from another platform to the GB.

To be able to do many special effects you will probably need to learn assembly enough to at least code time-critical interrupts & other code.

Are there any tutorials for writing software for GB ?

Check every page on the GB Dev web ring first. Many good sources of info can be found on the ring.

If you are programming in C using GBDK check out the GBDoK tutorial. Also check out the GBDK page & 420 Studios while you are at it.

If you are programming in GB assembly language then locate Gameboy Assembly Language Primer (GALP) v1.0 on a web page on the GB dev web ring.

What is a flash cart & do I need one ?

Flash carts allow you to test your custom GB code on a GB. You can also use them to play commercial GB ROMs that you might obtain from the internet but this is illegal. Especially if the commercial ROM is still available for sale.

Flash carts are more ideal for testing custom code than gb emulators because emulators often allow you to do things that real GB hardware does not allow due to hardware limitations.

Where can I get a commercial flash cart ?

There is currently only one place that publically sells flash carts and that is Bung Enterprises Limited. Nintendo sells a 32mbit flash cart (one with & one without rumble support) but this is only available for sale to licensed developers. The Super Smart Card made by China Coach Limited is no longer available and the company itself is out of business. The C3 cart made by Jeff Frohwein is no longer available.

Here are some links to Bung product distributors.

What software tools do official developers use ?

ISAS is the official gameboy assembler that Nintendo recommends. ISAS is short for Intelligent Systems ASsembler.

However, this assembler isn't required for official development. Some official developers use RGBDS, TASM, and probably GBDK as well.

What hardware tools do official developers use ?

The Intelligent Systems Debugger (US$4000) or Emulator (US$3000) are the most common hardware development tools. They connect to the SCSI port of an IBM-PC and allow downloading 8mbit files in just a few seconds to a GB or GBC. They both support development for projects as large as 64mbits. A special cart with a cable connecting the cart to the ISAS hardware is used to allow running the downloaded program on a GB. Both of these units also allow programming Nintendo flash carts. (These flash carts are only available to licensed developers as well.)

Other official developers use a gb emulator program that runs on an IBM-PC to do most testing and occasionally test their programs on the real GB hardware by using a flash cart.

What are .GB and .GBC files ?

These files are raw ROM files. There is no header or trailer information in these files. This is the exact data that is contained in cart ROMs. Also, this is the file format required by Bung flash carts.

The .GB indicates that the rom contains GB but no GBC code. The .GBC indicates that the ROM supports GB & GBC or GBC only.

What are .ISX files ?

These are compact ROM files that are generated by the ISAS assembler. They must be converted to .GB/.GBC format before they can be programmed to most flash carts and all GB emulators.

How do I convert .ISX files to .GB/.GBC files ?

To convert a .ISX file to a .GB file go to the Intelligent Systems ISAS/ISLK web page and download CVTISX and then do the following steps in DOS (Check here for RGBFIX.) :

cvtisx -b0,32k -mdmg filename.isx filename.gb
rgbfix -v -p filename.gb

Are some of the files on Intelligent Systems web page dongle protected ?

No. You have to have the SCSI interface Emulator or Debugger that Intelligent Systems sells in order to use some of the files.

What capabilities are specific to the GBC ?

The GBC is basically a GB with these extra features:

* Colo(u)r capability including 8 sprite palettes & 8 background palettes

* Background tile attributes which include X/Y flipping

* Twice as much video RAM (16K) & four times more main RAM (32K)

* InfraRed (IR) send / receive interface

* Two new DMA modes for more powerful graphics capabilities

* Single-speed (same as GB) or double-speed CPU clock mode

* Link port pin 4 is an unrestricted digital input

What is an MBC, when is one needed, and what are the differences between them ?

An MBC is a Memory Bank Controller. It is required any time time you have more than 32k bytes (256kbits) of cart ROM or more than 8k bytes (64kbits) of cart RAM because the GB & GBC can only access up to this amount of cart ROM & RAM at any one time.

Here are brief descriptions of each MBC. Listed values are the maximum supported ROM & RAM. Many/most carts do not use the maximum ROM & RAM that the MBC type is capable of supporting. For the complete & most accurate differences check here:

MBC1 - 2M bytes ROM/8k bytes RAM -or- 512k bytes ROM/32k bytes RAM
MBC2 - 256k bytes ROM/512 nybbles RAM (nybble = 4 bits)
MBC3 - 32M bytes ROM/32k bytes RAM, Real Time Clock support (not available in all MBC3 carts)
MBC5 - 64M bytes ROM/128K bytes RAM (First MBC guaranteed to handle GBC double speed mode. Other MBCs handle double speed even though there is no guarantee.)
HuC1 - An MBC similar to MBC1 but with InfraRed (IR) transmit & receive capability.
TAMA5 - This is a special MBC that Bandai put out for for Tamagotchi 3 (Japanese). This is the only cart capable of making simple sounds while the GB itself is turned off.

How do I become a licensed GB developer ?

This usually requires that you submit samples of your work to Nintendo. Often this even means submitting a game that is nearly complete in order that Nintendo might better realize your potential for code, graphics, & sound. Without them having something to see there is no real incentive for them to seriously listen to your objectives. But that's just one opinion....

Check this page for info on how to get licensed: WarioWorld

Is there any way to connect my GB to the internet so that I can play Pokemon with a friend ?

No one has yet proven whether this is possible.

Why is it that most older GB games when run on a GBC have blue & green backgrounds with red sprites but a few games have different default colo(u)rs ?

The GBC has a built-in list of older GB games for which it has default colo(u)rs. If the internal cart name matches up with any of the names in the GBC built-in list then these custom colo(u)rs are used instead of the default colo(u)rs.

Is there an IRC channel for GB development ?

Yes. Channels #gbdev, #gameboy, and #pocketdev on efnet. Many have been abandoning #gbdev, though, due to the number of bots in that channel.

What is the ROM header info in ROMs used for ?

The Nintendo graphic area & the complement byte are the only things that the GB Classic & GB Pocket care about and check. These bytes must be set correctly or a game will lockup after scrolling the Nintendo power up logo.

On the Super GB, rom addresses $146 & $14b must be set correctly for SGB features to work.

On the GBC, rom address $143 must be set correctly for GBC features to work.

Nintendo has a special piece of equipment that they use to test returned carts. It reads the ROM size, RAM size, MBC type, Checksum, and other information in order to know what hardware is present for testing. This is the reason for the other data fields in the ROM header area. This information is not used by games themselves.

What can you do if some of the lines of the LCD display quit working ?

Many early GameBoy Classic units had LCD display driver chips that would go bad over time. The solution would be to replace the bad chip or get a new GB. Replacing the chip is beyond the scope of these FAQs.

Do you have to be licensed by Nintendo to make / sell carts ?

It is not necessary to be an official Nintendo publisher to sell Gameboy format games. Wisdom Tree is not an official Gameboy publisher and has been sued by Nintendo before. However, they won the case and is currently selling Gameboy format software without license from Nintendo.

Is there really going to be a GBC2 next generation handheld ?

Yes, according to NCL (Nintendo of Japan). A next generation GBC is in development.

What games written for GB will not work correctly on GBC & why ?

Legend of Zerd - See "Are there any undocumented bugs in the GB ?".

Road Rash - See "Are there any undocumented bugs in the GB ?".

Rtype - The GBC sets sound wave RAM to a square wave on power up (even in GB mode) while the GB had random data in the wave RAM that "tended" to be the same values (but not always) on power up. Rtype never specifically sets wave RAM to known values so as a result some sounds in this game sound different. (Info from Martin Korth.)

How do I go about trying to sell my game/demo to a company ?

(The following information is very subjective and should be treated as such.)

From a collective feedback from many people it appears that it takes a special publisher to publish an original work. Many publishers don't want original work but rather would prefer to tell you what to port from an Arcade,N64,PC,PSX, or motion picture to GBC.
One reason might be that few people want to take responsibility if an original title does not do well. If a game that is ported to GBC doesn't sell well then they can say "That's strange, the version for XYZ sold well so this one should have done well in sales." If an original game doesn't sell well there is always some guy in the company (if the company is large enough) that will say "told you so, I thought it was a dumb idea."
Another reason might be that no one has heard of original titles and so they are less likely to buy it on the shelves. I know there have been cases where parents have come up to me in stores and asked me what they should buy for their kid even though they don't know me. It seems that very often people want to surprise their kids or their relatives kids with a game rather then ask them what they want. They often pick any game title they recognize. The recognition is often due to the fact that the game is a conversion from another game or a movie.

On the other hand, if you have already done some GBC titles then some companies are willing to sit down and discuss your original ideas once they have some confidence in your proven programming ability, project reliability, and demonstrated speed.

If you have never done work for the company you are talking with, your game often must be complete (or 99.9% anyway) before they can make any decisions on whether to publish it or not. (On incomplete games they have no idea what sound or graphics that you might add later that they might find offensive or annoying so it's rare to get any decisions until a game is complete or until they have experience working with you.)

Don't expect people that don't know you extremely well to hand out email addresses or other contact information to companies. No one wants to get an email that says, "Why did you refer this guy to me? He has minimal talent and he harasses me contantly with ROMs by email!" To get games to the right people you generally have to go to large game industry shows and meet as many people as you can. You need to take your completed game with you to the show. Make appointments with every GBC publisher at the show if possible. If the game looks promising and is "flashy" that might get them to look at it. If the game is good and has depth then that might even get them to publish it.

A game that you have done yourself needs to look atleast as good as the average GBC game available. Often it has to look better or do something unique that no other game does to keep a publishers attention long enough for them to consider publishing it. (Get someone that doesn't know you to judge it to get an honest opinion.) Some publishers won't publish it even if it's the best game they've seen if it isn't the type of game that they "typically" publish. Some companies only publish movie conversions, some only publish sports titles, etc...

Here is what Doug Tennapel (creator of Earthworm Jim) had to say in a interview on gamasutra.com:

Have you any advice for developers starting out on their own and looking to land publishing deals ?

Get a great lawyer, do the best work in the industry and you will get a publishing deal. Most developers don't pay attention to the "best work in the industry" part so I'll repeat it: if you don't do the best work in the industry, nobody will or should take a chance on you.

Small Publishers - Be very careful about sending your game to a small publisher even if they sign an NDA first. There often is a reason they are small. They often are honest and trying to break into the market or they can be corrupt. It doesn't take much for a small company to get your game ROM, have their own hackers modify it slightly, copyright it in their own country, and then sell it to some foreign country for a profit. With you receiving nothing in return except them giving you a notice, "We don't think your game is suitable for publication but please submit any future complete games to us for consideration."

Do NOT sent your complete game to a small publisher unless they can give you references to people for which they have published work. If they can't give you references this should set off some major alarms in your head. Either you are their first "experiment" at entering into publishing or they could be corrupt. Keep in mind as well that 9 out of 10 companies fail in their first year of business.

Large Publishers - The larger a company gets, the more that company can get to a point where the left hand doesn't know what the right hand is doing. (A case in point is that Nintendo of America has made some announcements before about products to be introduced or not introduced and Nintendo of Japan [the main location] soon after made a decision that basically reversed the announcement made by NOA.)

In other words, you can't just expect to send your brand new, state-of-the-art, best-graphics-in-the-world GBC game to anyone in the company and expect them to forward it to the actual decision makers. Many people in large companies could care less about what you have done. Many would not know what to do with a ROM even if you sent it to them. Often, your email goes directly to the trash if it is sent to the wrong person. Meeting people at game industry shows is probably the best method for meeting the right people.

Keep in mind as well that fast decisions or any decisions at all are often harder to get from large publishers. Their main way of doing things is to let their market research department decide what to publish next.

Where can a three-sided tool to open the GB be obtained ?

A company named City Tools makes a 33 piece security bit set that contains a bit that will work.

Also, if you get a flat-head screwdriver that is just small enough to fit inside the head of the screw you probably can remove the screws that way.

Where all can I order Bung products ?

Bung Enterprises Limited - Flash carts & cart programmers
www.imerchants.com
Austria distributor for Bung products
Belgium distributor for Bung products
Hong Kong distributor for Bung products
Another Hong Kong distributor for Bung products
USA distributor for Bung products
Another USA distributor for Bung products
UK distributor for Bung products
Another UK distributor for Bung products (free Europe shipping)

How long does it take for them to ship ?

It depends on who you order from. If you order direct from Bung or most of its distributors, expect 1 to 2 weeks for the order to arrive.

If you order from Upstate Games in the USA, expect 2 to 4 weeks for shipment. They often ignore email inquiries about an order status so don't panic if they don't respond. There have been few (if any) complaints from anyone about their service other than the fact that they take so long.

What do I need to order from Bung or a distributor ?

You need a Doctor GB Card (flash cart) 4Mbit, 16Mbit, or 64Mbit, GB XChanger (required to program the flash cart), a parallel cable, and an adapter or batteries to power the GB Xchanger.

Make sure when you order you get the newer version flash carts as these require much less battery power. (Expect about 1-2 hours battery life with older flash carts, ~10 hours with newer flash carts.)

If you order direct from Bung then get their male-to-male DB25 parallel cable. Some of their distributors don't carry this cable but it is required in order to program a cart. You can order this cable elsewhere as long as it is a "straight-through" cable not longer than 6 foot. Not all male-to-male DB25 cables are "straight-through" so make sure you know what you are getting. (A lap-link cable will not work.) If it is longer than 6 foot you may encounter some reliability problems.

You can use 6 AAA batteries or any 8-12 volt DC negative-center adapter for power.

What type of flash carts do Bung sell and what are the specifications for these carts ?

Bung sells a 4Mbit cart with 32k bytes of SRAM, a 16MBit cart with 128k bytes of SRAM, and a 64Mbit cart with 128k bytes of SRAM. Each of these carts has a battery to backup the SRAM.

Make sure when you order you get the newer version flash carts as these require much less battery power. (Expect about 1-2 hours battery life with older flash carts, ~20 hours with newer flash carts.)

What kind of battery life can I expect using their flash carts ?

Somewhere between 1-2 hours of battery life if you have older version flash carts.
Newer Bung flash carts draw power typical to commercial carts which usually allow ~10 hours battery life on a GB Pocket and ~20 hours on a GBC.

How can I tell if I have newer or older Bung flash carts ?

You have to take the flash cart apart which may void any warranty for it. If the chip farthest from the gold edge connector is a Altera EPM70xxSLC44 then you have an older version Bung flash cart. This chip draws massive amounts of battery power so don't expect more than 1-2 hours battery life.
Newer Bung flash carts draw power typical to commercial carts which usually allow ~10 hours battery life on a GB Pocket and ~20 hours on a GBC.

Is it possible to write to the flash chip using code on the GB ?

No. They use pin 31 of the cart interface as a flash write line instead of the standard cart write line.

Is it possible to use the GB Xchanger to download GB camera pictures ?

Yes. Get GBT v1.4 software or later from Bung. Old versions of GBT don't allow you to read the GB camera.

Do the Bung tools work on Windows NT ?

Yes. Both GangaBoy and Bung's GB Xchanger software work correctly under NT4, as long as you install GIVEIO.SYS correctly. Check Important Web Links for installation on WinNT.

How do I improve write-to-cart times during program development ?

Check the related question here.

The GB Xchanger doesn't work with my parallel port. What's the problem ?

There seem to be many programs that allow you to control the GB Xchanger but try the GBT program by Bung first. GBT v1.4 or later should allow you to interface with the GB Xchanger in nearly every parallel port mode.

What are the specifications of the GB Camera ?

GB Camera Specifications:

- 128 x 112 pixel, 4 grey-scale photos
- Up to 30 photos saved in 128k Byte battery-backed SRAM.

How do I transfer pictures from my GB Camera to a PC ?

GBT v1.4 software or later from Bung allows transfering GB camera save RAM to the IBM-PC using the GB XChanger. GB Camera software for Win95 can be found here for viewing GB camera save RAM pictures.

Has anyone modified a GB camera to allow real-time transfer of photos to a PC using the GB link port ?

No.

How do I enable cart RAM using software ?

On power up, cart RAM access is disabled. If you are using GBDK, however, cart RAM may be enabled as part of the power up sequence but don't assume this to always be true in every version of GBDK.

Cart RAM should only be enabled when you are actively reading from or writing to it if you wish to preserve data during power off. Cart RAM contents can possibly be partially or totally lost if a power off occurs while cart RAM is enabled.

Write $0a to address $0000 to enable cart RAM. Write $00 to address $0000 to disable cart RAM.

Once cart RAM is enabled the address space between $a000 and $bfff may be treated as standard RAM.

What is the maximum amount of ROM & RAM that you can put in a cart ?

The GB & GBC can only access 32K bytes ROM & 8K bytes cart RAM at one time. As a result, this is all you can access unless you use a Memory Bank Controller (MBC) or design something similar yourself that does the same function. The total amount of ROM or RAM accessible then becomes a function of the limitation of the MBC itself. Yon can have virtually unlimited ROM or RAM in a cart if you design your own MBC from scratch. If you use a Nintendo MBC, then here are the Nintendo MBC limits.

Which carts containing RAM also contain a battery to preserve the contents of this RAM during power off ?

They all do.

Is there a list of commercial GB & GBC carts somewhere and what they contain ?

Yes. Check the links near the bottom of this doxs page.

What commercial GB & GBC games have unusual or unique features ?

Isometric graphics: Altered Space, Monster Max, Super R.C. Pro-AM, Twouble
Real time Clock: Harvest Moon GB (Japanese version), Harvest Moon GBC (US & Japanese)
Rumble support: Top Gear Pocket
4-Player adapter support: F1-Race, Tennis, Tetris, TetrisAttack, Waverace
SNES/Super Famicon code: Space Invaders (4Mbit version)
Wire-frame 3D games: Battlezone/Super Breakout, Ray Thunder (Japanese), Xekkusu (Japanese)
Filled-surface 3D games: Faceball 2000, Race Drivin'
Non-licensed GB carts: religion carts, Sachen of Taiwan
Totally redesigned carts: GB Camera, Bandai's Fishing Sonar

What commercial carts have been written using the GBDK C compiler ?

Logical, Mahjong King, Meta Mode, Pro Darts, Qix Adventure, Sweet Ange, and some others.

Will a GB cart work on a GBA ?

Yes, but you can only run GB/GBC games. You can not run GBA games on a GB cart. The cart interface uses a different voltage and the cart address and data lines are layed out totally differently. An autodetect circuit is used to tell what type of cart is installed and you are then forced into GB or GBA mode.

What voltage is present on the cart interface ?

5 volts is present on all versions of GB. The GB pocket & GBC use voltage converters to convert battery voltage to 5 volts.
The GBA uses 3.3 volts for GBA carts and 5 volts for GB carts on its cart interface. It uses an autodetect circuit to decide what type of cart is installed. You can not use a GB cart to run native GBA code.

What are the specs for the clock signal (pin 2) on the cart slot ?

On the GB classic, pocket & GBC this clock is 1.048576 MHz. On the SGB this is 1.0738635 MHz. In GBC double speed mode only it is 2.097152 MHz. In all cases the signal is a square wave.

This signal is present except when assembly commands STOP & HALT are active. When these commands are active this signal stays high.

Is it possible to design & build a flash cart that would allow you to program it using GB code ?

Yes. Check Reiner Ziegler's web page on the GB Dev web ring for plans.

Is it possible to design a cart that displays something other than the Nintendo logo on power up ?

The GB Pocket & GB Classic at power up would display the graphic logo in the ROM and then test to make sure that the logo was accurate. For these units you could do some ROM hardware tricks that would swap the graphic logo ROM contents after the logo was displayed and then the game would boot properly.

The GBC has more sophisticated testing. In order to display your own logo on the GBC you need to alternate your logo and the Nintendo logo as follows: powerup with "Nintendo", "Nintendo" logo present for 19.2ms after reset, Custom logo present for 89.6ms, "Nintendo" logo present for 46.4ms, Custom logo present for 262.7ms, "Nintendo" logo present forever. (Info from Ken Kaarvik.)

What are the advantages of using a cart reader with ReadPlus software over using the Bung xchanger with their flash carts ?

Probably the only advantage is that some of the carts designed to work with ReadPlus allow you to write to the cart flash chip using software on the GB.

Where can a cart connector and the tool to open GB carts be obtained ?

From MCM Electronics. The only connector they have is the one that was used in the no longer available GB classic (MCM part# 83-2285). The tool to open carts is MCM part#22-1145. They are located in the USA and their phone number is 1-800-543-4330.

What are GB, EPROM, and IS emulators ?

Emulator can be a confusing word because it have several meanings depending on where it is used:

A GB emulator is a piece of software that runs on a computer and emulates a GB and/or GBC. The reality is that none of the GB emulators available perfectly emulate the real hardware but some come close. Many think that NO$GMB has the current best GB/GBC/SGB emulation & it also has a powerful debugger for helping developers with coding. SMYGB for Win95 currently is thought by many to have the best sound emulation but it can't play channel 3 sound samples very well.

An EPROM emulator is a piece of hardware that perfectly emulates a ROM or EPROM chip. They often connect to the parallel port of a PC and connect to a ROM socket. Software code changes can often be downloaded in seconds to the ROM socket. EPROM emulators are not very practical for most as they tend to be very expensive & require that you physically solder a socket onto a GB cart to allow connecting an EPROM emulator to the cart.

The emulator that Intelligent Systems sells is basically an expensive EPROM emulator. The debugger they sell is the same thing along with hardware break points, trace history buffer, and other debug features. They both program Nintendo flash carts as well.

Why does my code that runs on an emulator not run on a real GB ?

* 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.

* 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 if you are coding in assembly.)

* 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.

* 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. Emulators tend to allow you to write to video memory at any time.

* 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. If you don't wait for V-Blank, once in a while SGB code will just lock up and possibly some GBC will not operate correctly.

* Normally you should only make changes to Sprite RAM during V-Blank unless you know exactly what you are doing. The common way to do this is to use the Sprite DMA register ($ff46) to do a fast copy from your sprite table to sprite RAM at $fe00-$fe9f.

Why do colo(u)rs look washed out on some GB emulators (i.e. no$gmb, etc) but not on others ?

This is because code has been added to in order to do "colo(u)r correction" . This "correction" code creates washed out colo(u)rs to better simulate how the game appears (or would appear) on the real GBC hardware.

Often, GB emulators (such as no$gmb) have a configuration option to enable or disable this "auto correction" feature.

The consensus seems to be that greens and blues reproduce well on the GBC but the reds seem to be faded.

I have read that the GB screen is tile based. What exactly does this mean ?

It means that in order to draw images into the GB background you must design 8x8 tiles and then write these tiles to background tile map memory.

I have heard that the GBC can do more than 4 colo(u)rs per tile, has a secret high resolution mode, and can do 3D graphics in hardware. Is this true ?

None of this is true except possibly the first one. You might can do more than 4 colo(u)rs but only through creative palette manipulation during H-Blank. This is not practical for many things and is only achievable if you know exactly what you are doing.

Read the GBC Specification in the Web Links Section for what video modes the GBC supports.

How do I turn individual pixels on & off (i.e. APA graphics) instead of drawing with tiles ?

The ability to turn individual pixels on & off on the screen is often called APA (or All Points Addressable) graphics.

On the GBC you can do this by putting a unique tile at each screen map location. Since the GBC can have 512 unique tiles, 360 of these are enough to cover the entire screen (20x18 tiles.) Then you can set individual screen pixels to any colo(u)r by writing to tile memory.

On the other GBs (pocket, classic, super) you only have access to 256 tiles at one time which is less than the 360 unique tiles required to fill the screen. But, by using a LYC interrupt you can switch from tile set #1 to tile set #2 midscreen. This effectively allows you access to up to 384 unique tiles (since tile set #1 & #2 half overlap each other) which is enough to fill the screen to allow APA graphics.

How do some GBC games put more than 56 colo(u)rs on the screen ?

By dynamically changing the colo(u)r palettes while the screen is being drawn.

Check here for assembly demo code.

Can GBC palettes be set at any time ?

No. Specifically, you can write to GBC palettes at any time except during video mode 3. While the screen is off you can write to the palettes at any time.

The GBC & SGB both support colo(u)r. Is graphics programming for them similar ?

Not really. They both have in common the ability to display graphics written for GB pocket & GB classic but that is where the similarities end.

The SGB is able to colo(u)rize GB games using primitive techniques that involve sending information to the SNES/Super Famicon. As such, if you attempt to scroll the screen it is sometimes a slow process to send new information to the SNES so colo(u)rized, scrolling screens are difficult to implement.

The GBC, on the other hand, does all colo(u)rizing itself using totally different methods. As a result, colo(u)rized games written for the GBC will not work on the SGB & vice versa.

What is the difference between the GB window and the GB background ?

The GB background map is often used as the "main" game screen map. You can scroll the background in any direction without limitations.

The GB window map appears on top of the background and can only be scrolled to the right and down from the upper left screen coordinate. You can scroll it up as long as the Y coordinate is >=0. You can scroll it left as long as X coordinate is >=-7. The window is often used as a status bar on the right or bottom of the screen. No part of the window can ever be made transparent to the background under it.

If you set a sprite & it's priority to appear below the background & the window, you can see the sprite under window colo(u)r 0 but you can't see the background under window colo(u)r 0. It is as if the window has replaced the background at all points that it appears on screen.

Can any part of the window be made transparent ?

No part of the window can be made transparent to display the background.

You can make the window transparent to sprites under the window anywhere that you use window colo(u)r 0 and also set the sprite priority to appear behind the window / background.

What precautions should I use when reading/writing video RAM ?

When the display is off you may read or write video RAM without restrictions.

When the display is on and not in vblank, you have to check to see if video RAM is available before you can write to it. If video RAM is not available you will read $ff and writes will be ignored.

If you read STAT ($ff41) and bitwise AND the result with 2, the result will be 0 when VRAM is available.

What precautions should I use when doing HDMA on a GBC ?

- If you initiate an HDMA from a ROM bank ($4000-$7fff) to VRAM you can not change the ROM bank until the HDMA has completed. (You can change ROM banks during HDMA ONLY if you are not using a ROM bank as a source for the HDMA data being transferred.)

- If you initiate an HDMA from a RAM bank ($d000-$dfff) to VRAM you can not change the RAM bank until the HDMA has completed. (You can change RAM banks during HDMA ONLY if you are not using a RAM bank as a source for the HDMA data being transferred.)

- Once you initiate an HDMA you can not change the Video RAM bank ($8000-$9fff) until HDMA has completed.

- Source & destination addresses of the HDMA must be 16 byte aligned. (i.e. Source & destination addresses must have a hex value of $xxx0. )

- It will not work if the screen is off. Nothing will happen.

- It will not work if you use the assembly command HALT in your main loop.

- It will not work properly if you try to set up another send while it is still sending the last one. HDMA only takes place during H-blank on screen lines 1-144 (lines 0-143 if using LY numbering system). During other times (including V-blank) HDMA is paused.

- Make sure your transfer length is correct. (i.e. HDMA5 [$ff55]: $80 = 16 bytes, $81 = 32 bytes, .... , $ff = 2048 bytes)

How do I know when an HDMA on the GBC has completed ?

Once you initiate an HDMA, reading bit 7 of HDMA5 ($ff55) will return a value of 0. Upon completion of HDMA this bit will be set to 1.

What precautions should I use when doing GDMA on a GBC ?

- GDMA can only reliably be performed in V-blank while the screen is on. While the screen is off it can be performed at any time. Turn the LCD screen on only AFTER enabling an interrupt if that interrupt is used to service GDMA. That way any pending GDMA will not occur during screen drawing (non-vblank).

- During the execution of GDMA the CPU is devoted to DMA only and no other processing can take place.

- If you plan to transfer more than ~1600 bytes (~100 tiles) in one V-blank while the screen is on then you probably will need to use double-speed mode. GDMA is slightly faster in double-speed mode and will allow transfers up to 2048 bytes in this mode. If your V-blank interrupt code is exceptionally long then large GDMA transfers are not feasible if they overrun the end of V-blank.

- Make sure your source & destination addresses are 16 byte aligned (same as HDMA) and that your transfer length is correct. (i.e. HDMA5 [$ff55]: $00 = 16 bytes, $01 = 32 bytes, .... , $7f = 2048 bytes)

How long does it take for a GDMA to execute on a GBC ?

It takes (220 + (n * 7.63)) microseconds in single speed and (110 + (n * 7.63)) microseconds in double speed mode. (Where n = 0 is 16 bytes, n = 1 is 32 bytes, n=2 is 48 bytes, etc... ) This translates to a transfer rate of 2097152 bytes/sec if you don't consider the initial startup delay of 220uS (110uS in double speed mode.) The value 110uS is accurate within +/-5uS. The value 220uS is accurate within +/- 10uS.

One of the Nintendos docs says, "The new DMA transfer operates at a constant speed, regardless of whether the CPU is in Normal or Double Speed Mode" but this is not exactly correct information.

I read somewhere that you must set GBC tile attributes before setting the GBC tile map. Is this true ?

Setting the tile attributes directly after setting the map tiles can lead to errors. At least a NOP should be placed between. This problem doesn't occur often and probably only occurs in double speed mode.

I'm using the LYC interrupt as a trigger to modify the SCX and/or SCY scroll registers. I get some graphic "trash" on the screen line where the LYC occurs. Why ? And how do I fix it ?

The reason you often will get graphic trash is because the LYC interrupt, itself, is getting delayed from occurring perfectly on time due to some other interrupt (usually due to a timer, serial, or hi/lo interrupt.)

One way to fix this is to make sure that the screen line that this occurs on is always one solid colo(u)r. In this way, the problem still occurs but isn't visibly noticeable so for all practical purposes the problem is "fixed".

Another way to fix this problem is to have the LYC interrupt occur one line before you actually need it. Then, use a tight loop waiting for the LY register to equal the next line. If the other interrupt code (that is causing this problem) is extremely long then you may need to set the LYC interrupt to occur several lines or more before it is actually needed. Mortal Kombat 3 & 4 for GB use this technique to prevent graphic trash.

What are the video timing specs for the GB & GBC ?

Both the GB & the GBC have the same video timing specs. For more info on the various video modes refer to a reference for register STAT ($ff41). Horizontal lines which contain sprites have less H-Blank time available than lines with no sprites. No H-Blank time is available during an active HDMA:

Complete horizontal line timing = 108.7 µsec
V-Blank = 1.09 msec
Mode 2 = 19.31 µsec (~20 machine cycles)
Mode 3 = Variable between 41.37 µsec - 70.69 µsec
Mode 0 = H-Blank = 108.7 µsec - 19.31 - Mode 3

Mode 0 minimum = 18.72 µsec (10 sprites on a line)
Mode 0 maximum = 48.64 µsec (no sprites on a line)

Each video line contains mode 2, mode 3 and mode 0 in that order. GBC palettes can be written during every mode except mode 3. An LYC interrupt occurs at the start of mode 2. An H-Blank interrupt occurs at the start of mode 0.

Why do pictures in the background or window often appear distorted if you scroll them up or down 1 pixel each V-Blank ?

This is because all versions of GB (i.e. GB,GBC,GBP,SGB,...) have interlaced screens and are redrawn 30 times a second. (i.e. All odd lines are drawn in one screen refresh, all even lines are drawn in the next screen refresh.) As a result, if you scroll a picture up or down 1 pixel each V-Blank you are seeing every other line twice during scrolling. (Info of GBC interlace from Pan of Anthrox.)

The GB screen isn't a true interlaced screen. It appears that every line is redrawn with every screen redraw but every other line is just much dimmer.

Can the screen be turned off at any time & what assembly code should be used ?

It's not a good idea to turn off the screen unless you are in V-blank for the following reasons:

- Code written for the Super GameBoy will not always work if you don't wait until V-blank to switch off the screen. The screen "glitch" caused by turning the screen off during redraw can sometimes cause the Super GameBoy screen to "lockup" until reset is pressed.

- It has been reported that Nintendo rejects game titles during the acceptance phase if they don't wait until V-blank to turn off the screen. It has also been reported by Nintendo Acceptance that some code can physically damage the GBC screen circuitry over time. It is believed (but not verified) that this damage occurs due to not waiting until V-blank to turn off the screen.

Here is assembly code that is similar to what Nintendo suggests:

ScreenOff:

        ld      hl,rLCDC

        bit     7,[hl]          ; Is LCD already off?

        ret     z               ; yes, exit

        ld      a,[rIE]

        push    af

        res     0,a

        ld      [rIE],a         ; Disable vblank interrupt if enabled

.loop:  ld      a,[rLY]         ; Loop until in first part of vblank

        cp      145

        jr      nz,.loop

        res     7,[hl]          ; Turn the screen off

        pop     af

        ld      [rIE],a         ; Restore the state of vblank interrupt

        ret

Why does the GB have H-blank & V-blank periods in the video timing ?

The terms H-blank & V-blank are a part of Cathode Ray Tube (CRT) based video games and as a result they have carried over to some LCD-based games. The H-blank period is the amount of time it takes the scanning electron beam to return to the left part of the screen and the V-blank period is the amount of time it takes the scanning electron beam to return to the top of the screen for CRT displays.

Since the Liquid Crystal Display (LCD) of the GB doesn't have an electron beam there are no lag times that require an H-blank or a V-blank period. Since the GB doesn't have dual-port video RAM, allocating these times anyway provides a convenient time for writes to video RAM since the video circuits aren't updating the screen during these periods (and as such aren't preventing access to video RAM during these periods.)

What is an interrupt ?

An interrupt is a piece of code that starts executing automatically when an event occurs. To do this it has to interrupt whatever code was running before the event occurred. As part of this, the Program Counter (PC) is saved on the GB stack and interrupts are turned off as soon as the interrupt code starts executing. At the end of the interrupt, a RETI assembly instruction is usually executed which returns to executing code that was running before the interrupt occurred and enables interrupts again.

What precautions should I use when writing interrupt code ?

You should often try to keep your interrupt code as short & fast as possible. In many cases, no other interrupt can take place while you are servicing an interrupt so shorter interrupt code can be helpful.

Usually you should enable interrupts before turning on the screen. This way, any old vblank or hblank interrupt that is still pending but never got serviced will occur before screen drawing starts. For instance, many people write vblank code that must only occur in vblank or while the screen is off or else problems will result while attempting to execute this code during the screen refresh phase.

Can an interrupt interrupt another interrupt ?

Normally, no. When an interrupt occurs it automatically executes a Disable Interrupts (DI) command. As a result, no other interrupt can be serviced until servicing of that interrupt is complete and the assembly instruction RETI is executed.

To allow an interrupt to interrupt an interrupt you need to put an Enable Interrupt (EI) instruction at the beginning of each interrupt routine where interrupting will be allowed.

What is the difference between assembly commands RET & RETI ?

RET is just a return from subroutine. RETI is two commands in one. RETI is EI / RET in that order. The command EI doesn't take effect immediately but DI does. EI takes effect following the instruction that follows it.

I can't get the HI/LO (joypad) interrupt to work. Ideas ?

You must set the P1 register ($ff00) in order to do this:
P1_REG = $20 ; Cause U,D,L,R to joypad interrupt
P1_REG = $10 ; Cause A,B,SELECT,START to joypad interrupt
P1_REG = $00 ; Cause any button to joypad interrupt

After calling any function which reads the joypad you will need to reset this register since most of these functions set this register to $30 upon completion.

Should a HI/LO interrupt occur when a button is released ?

The HI/LO interrupt occurs (if enabled) when a button on the GB is pressed. The reality is that this interrupt can occur several times during the process of pressing a button and several times while releasing a button due to button "bounce" that often occurs but few realize it.

Can interrupts occur if I enable interrupts & then quickly disable them ?

No. Specifically, when working in assembly you need a small delay between EI & DI. If you insert a NOP command between EI & DI then this allows the interrupt enable command to complete before DI takes effect. Thus allowing all pending interrupts to be serviced before interrupts are disabled. Any pending interrupts will be serviced in the order of their priority and they all will be serviced at once before control is returned to non-interrupt code.

The reason this delay is needed is because the assembly command EI doesn't take effect immediately but DI does. EI takes effect following the instruction that follows it.

Is there anything I should know when using the LCDC interrupt ?

There is one Int $48 signal. When this signal goes from "off" to "on" the LCDC flag in the register IF ($ff0f) gets set, and ONLY on off-to-on transitions.

If the signal is already "on", say by a true (and enabled) LYC=LY condition, then a further interrupt in the same line (say HBLANK int is enabled as well) would set the int48 signal as well, but it's already set so no off-to-on transition, no further interrupt generated.

But the HBLANK interrupt could occur if LYC=LY would get disabled or false (lyc changed) within the line, ie. if int48 signal gets off for a short moment. (Info from M.K.)

What does pin 4 of the link port do ?

On the GB/GB Pocket/GB Classic:
This pin connects to the front panel button array and is a digital output line from register P1 ($ff00) bit 4.

On the GBC:
This pin connects to register RP ($ff56) bit 4 and is a digital input pin. (Info from Ken Kaarvik.)

Can the serial link port be used to connect to other serial devices ?

You possibly could use pin 4 of the link port on the GBC as a serial input. You would need to write a software UART to handle the input data. Since there would be no interrupt involved in monitoring this input pin you would need to devote a good bit of CPU time to monitoring this pin & probably no interrupts would be feasible while looking for serial data.

As far as the GB Pocket or GB Classic goes, the standard serial in & out pins on the GB have a serial format that is not compatible with any known serial standard.

I'm trying to write code to communicate with another GB using the link port. Are there any suggested algorithms for doing this ?

This might possibly be the single most difficult thing on the GB to program: GB-to-GB link code. First, get chapters 4 & 5 of the official GB development manual. You might even can locate these somewhere on the internet. You have 2 different options. The chapter 4 (simple code) or chapter 5 (complicated code). If you wish to transfer more than 1 byte per vblank then you MUST use chapter 5 code for reliability

Chapter 4 has two different modes. The first mode is synchronization (before the game starts) and this flowchart is shown on the first page. The second mode is "after the game starts" flowchart which is shown on page 2.

I get an unreliable game link connection when connecting a GB Pocket or Classic to a GBC. What might cause this ?

When a DMG (GB Pocket or Classic) and CGB are linked together, and an AC Adaptor is used, line noise may occur. This noise can cause communication problems with the SCK (link clock), which could bit-shift received data. This seems to occur mainly when the DMG is the Master mode (using its internal clock.) This problem doesn't seem to occur when the GBC is the master.

There are two ways to solve this. You can forcibly make the GBC the master, or you can create better error checking routines to correct this problem. Nintendo recommends using the latter option.

Is the GB link connector available anywhere ?

The original (larger) GB Classic and the newer (smaller) GB Pocket / GBC link connectors are each custom designs by Nintendo that are not available elsewhere. You need to modify existing cables in order to use these connectors.

With the introduction of the GB Pocket the link port connector is smaller than it was on the GB Classic. Are the changes only physical ?

Yes. With the proper conversion connector you can link different size link port units together.

With the introduction of the GBC, link port pin 4 now has a new function but the official Nintendo game link cable for connecting two different game units has never supported pin 4 so this change has no immediate impact. Pin 4 might possibly be used by some future Nintendo peripheral but currently it is not used.

Is there any direct way to read or write any of the pins on the link port ?

Pin 4 of the link port is the only one. The other pins are Serial IN, Serial OUT, Clock, 5 volts, and ground. None of these other pins are usable as "direct" digital I/O lines in the traditional sense.

What kind of serial input data should I expect if no GB is connected on the other end or the other unit is turned off ?

If no GB is connected on the other end of the link cable then you will receive data = $ff.

If a GB is connected but it is turned off then you will receive data = $00.

Are there any disadvantages to running the GBC in double speed mode ?

Yes. Check the information here.

I read somewhere that there is a boot rom in the GB & GBC. Has anyone been able to read this rom ?

No.

Are the GB & GBC 1 & 2MHz systems or 4 & 8MHz systems ?

This depends on whether you are talking about clock cycles or machine cycles. Nintendo specifications list machine cycles. Many internet document specifications list clock cycles.

The GB & GBC can operate at ~1 million machine cycles/sec or ~4 million clock cycles/sec. The GBC in double-speed mode can operate at ~2 million machine cycles/sec or ~8 million clock cycles/sec.

The simplest assembly instruction in the GB & GBC is a NOP. It takes 1 machine cycle or 4 clock cycles for this instruction to execute.

What are all of the DMA modes & what are they used for ?

Sprite DMA -
This is the original DMA that is available on all models of GB. It is used to do a fast copy of sprite attribute data to Object (sprite) Attribute Memory (OAM) located at $fe00-$fe9f. Sprite attribute data includes sprite X & Y coordinates, tile number, palette number, horizontal flip, vertical flip, and sprite priority. No tile graphics are transferred using this method of DMA.
This method of DMA usually is only performed in high RAM ($ff80-$fffe) due to its limitations.

HDMA -
Otherwise known as Horizontal Blank DMA. It is only available on the GBC. Similar to GDMA, this method of DMA is useful for copying tile graphics or tile maps to video RAM. There are many restrictions to using HDMA listed here but it can be powerful if used carefully. Unlike the other two methods of DMA, this one is most practical for allowing other processing to take place while HDMA is occurring. More specifically, the CPU is used completely during H-blank to process HDMA but at all other times user code may be doing other things.

GDMA -
Otherwise known as General Purpose DMA. It is only available on the GBC. Similar to HDMA, this method of DMA is useful for copying tile graphics or tile maps to video RAM. There are some restrictions listed here for using GDMA but far less than for HDMA. While GDMA is active, the CPU is completely devoted to DMA and no other processing takes place until GDMA is done.

Is the GB a big or little endian system ?

The GB, GBC, & Z80 are all evolutions of the original Intel 8080 and all of these are little endian systems. When pushing data on the stack or using the assembly command LD [xxxx],SP the low byte is stored lower in RAM.

Is it possible to use the Infrared capability of the GBC for a IR remote control ?

Yes.The GBC IR transmitter can even operate at over 100kHz. But, transmit distance drops off dramatically at 100kHz and higher.

One person was able to code an IR remote that would transmit to a TV reliably up to 3 metres (10 feet) and unreliably up to 5 metres (15 feet).

I have written a program for the GBC. Normal GB functions work but I can't get any of the GBC additional registers to work. What am I doing wrong ?

You must set rom address $143 to $80 for a GB/GBC game or to $c0 for a GBC-only game.

With the introduction of the GBC, the internal cart name has been made smaller. Is this correct ?

Yes. Rom location $143 was turned into a flag that indicated a GBC game so the internal cart name was reduced in size from 16 to 15 characters. The GBC flag is $80 for a GBC compatible or dedicated game. Any other value signifies a non-GBC game.

As of 1-Mar-99, all GBC games have an even smaller internal cart name of 11 characters. The four ascii characters following the internal cart name are now dedicated to the game product code. As part of this change, the GBC flag byte was changed: $80 = GBC compatible game, $c0 = GBC dedicated game. Any other values signify a non-GBC game.

So why did Nintendo change rom address $143 from $80 to $c0 for a GBC dedicated game ?

Because they wanted a method of distinguishing a GBC-compatible game from a GBC-dedicated game. There is no hardware reason for this change.

What is the assembly language command HALT used for ?

This stops the CPU until an interrupt occurs. Nintendo recommends using this command in your main game loop in order to save battery power while the CPU has nothing else to do.

When an interrupt occurs while in HALT, the CPU starts back up and pushes the Program Counter onto the stack before servicing the interrupt(s). Except it doesn't push the address after HALT as one might expect but rather the address of HALT itself.

HALT operates differently depending on whether the Interrupt Master Enable (IME) is set or reset. Assembly command EI sets IME to 1. Assembly command DI resets IME to 0.

IME = 1 -

     HALT stops the CPU until the register IF ($ff0f) &

    register IE ($ffff) when logically ANDed together

    have a non-zero result.

     HALT will only start the CPU upon a 0 to 1 transition

    of one of the bits of the non-zero result. (i.e. Pending

    interrupts that occurred before HALT was executed will

    not terminate HALT.)

IME = 0 -

    [$ff0f].AND.[$ffff] = 0 :

        HALT is aborted. Next instruction is executed normally.

         If IME is set to 1 at a later time then a halt condition

        will occur one instruction after IME is set to 1 to complete

        the halt that was not allowed to finish earlier.

    [$ff0f].AND.[$ffff] > 0 :

         HALT is aborted. The first byte of the next instruction

        after HALT is read. The Program Counter (PC) fails to

        increment to the next memory location. As a results, the

        first byte after HALT is read again. From this point on

        the Program Counter once again operates normally.

         If IME is set to 1 at a later time then a halt condition

        will occur one instruction after IME is set to 1 to complete

        the halt that was not allowed to finish earlier.

Nintendo also recommends that you put a NOP after HALT commands. The reason for this is that the Program Counter will not increment properly (CPU bug) if you try to do a HALT while IME = 0 and an interrupt is pending. A single-byte instruction immediately following HALT will get executed twice if IME = 0 and an interrupt is pending. If the instruction following HALT is a multi-byte instruction then the game could hang or registers could get scrambled.

IME is set to 1 even if the assembly instruction EI is executed immediately before the HALT instruction.

Later versions of the RGBASM compiler automatically add a NOP after HALT.

What is the assembly language command STOP and how/when should it be used ?

This stops the CPU & turns off the display until a button is pressed.

In addition, STOP can be used to toggle CPU speed on the GBC.

If a game is idle for 5 minutes or longer you should possibly consider using STOP to stop the CPU & turn off the display to conserve power. Everything is restored after a STOP just by pressing a button. If you use STOP for conversing power you should also consider disabling sound (reset bit 7 of NR52) as the sound circuits seem to draw the most amount of power.

The value in register P1 ($ff00) determines which buttons will terminate the STOP condition. These values are as follows and apply to both the GB & GBC:

$00 - Any button will terminate STOP.
$10 - Only A, B, Select, or Start will terminate STOP.
$20 - Only the direction buttons will terminate STOP.
$30 - No button will terminate STOP.

Are there any undocumented bugs of the GB ?

Yes. If the windows X ($ff4b) is set to 0, and you scroll the background map horizontally (via $ff43), the window scrolls horizontally with it for 8 pixel, then "hops" back to were it should be. This occurs on the original GB, pocket GB, and GBC.

There is the sprites bug that was fixed in the GBC and also the following that doesn't apply to the GBC.

In certain situations, writing to the STAT register ($ff41) seems to cause bit 1 of the IF register ($ff0f) to be set (and thus cause interrupt $48 to occur, if it is enabled). Due to programming bugs, at least two games (Roadrash, Legend of Zerd) insist on this quirk, and are incompatible with the GBC.

As far as has been figured out, the bug happens everytime ANYTHING (including 00) is written to the STAT register ($ff41) while the gameboy is either in HBLANK or VBLANK mode. It doesn't seem to happen when the gameboy is in OAM or VRAM mode, or when the display is disabled. (Info from Martin Korth.)

Are there any undocumented features of the GBC ?

Yes. The byte at location $143 in a ROM determines whether the GBC operates in GBC mode ($80 or $c0) or 4-color GB mode (any other value.) That is the official info anyway. Martin Korth has found that any of the following binary values at $143 cause strange sprite colors & white-only background colors always (X = don't care):

1XXX01XX
1XXX10XX
1XXX11XX

It is as if bits 2 and/or 3 being set cause the colo(u)r palette registers to be write-protected. On power up, the sprite palettes tend to be random pastel colo(u)rs & the background palettes are white.

If you read HDMA5 ($ff55) after starting an HDMA you will read back the tile count you wrote except the high bit will be 0. Every H-blank this count will decrement. After reaching $00 it will change to $ff indicating that HDMA has completed. (i.e. If bit 7 is 0 HDMA is busy, otherwise HDMA is done.)

There are also several undocumented registers (info from Dark Fader):

$FF6C - bit 0 can be read/written
$FF72-$FF74 - whole byte can be read/written
$FF75 - bit 4-6 can be read/written
$FF76-$FF77 - Read only & set to $00

For the record, Nintendo recommends against using undocumented registers in order that your code will work properly with future releases of hardware. It is not clear whether the bit that tells you that HDMA is done was accidentally undocumented or undocumented on purpose.

Does the GBC register LCDC ($ff40) bit 0 do anything ?

When the GBC is in GBC mode, this bit does not disable the background or window. When this bit is 0 sprites will always appear above the background & window regardless of any sprite priority settings or tile attribute background priority settings.

When the GBC is in GB mode, this bit operates properly.

If rom location $143 is $80 or $c0 the GBC is in GBC mode.

Does the GBC start in single or double speed mode ?

Single speed mode.

What assembly code is needed to toggle CPU speed on GBC ?

Use the following code. You must set register P1 ($ff00) to $30 or else a button held down can cause STOP to not complete the process of changing speeds. Register KEY1 ($ff4d) bit 0 is used as a flag to determine if STOP should turn off the display & wait for a button or toggle the CPU speed:

ToggleCPUSpeed:

        di

        ld      hl,rIE

        ld      a,[hl]

        push    af

        xor     a

        ld      [hl],a         ;disable interrupts

        ld      [rIF],a

        ld      a,$30

        ld      [rP1],a

        ld      a,1

        ld      [rKEY1],a

        stop

        pop     af

        ld      [hl],a

        ei

        ret

Has anyone tried to port Unix to a GB ?

No, but UZI is a port of Unix for Z80. If someone was willing to put enough effort into the project they could probably port UZI to GB.

What do the undocumented assembly opcodes do ?

D3,DB,DD,E3,E4,EB,EC,ED,F4,FC, and FD are all undocumented GameBoy assembly language opcodes. Executing any one of these cause every version of the GB to permanently halt operation until power down / power up.

What is high RAM ($ff80-$fffe) useful for ? If used for the GB stack, does it provide better performance ?

No better performance can be obtained by using high RAM for the GB stack. But, if you are coding in assembly language, some opcodes are optimized (and hence operate faster) when using high RAM for data storage. For high performance code it is actually better to put the GB stack in lower RAM and use high RAM for data storage instead.

Sprite DMA code must be performed in high RAM because all other memory is disabled during Sprite DMA.

What does the term Metatile mean ?

In the world of GB, Metatiles are virtual tiles that are larger than 8x8 pixels. In many/most cases, metatiles are 16x16 pixel virtual tiles. In some cases they are 8x16, 16x8, 32x32, or even larger. Metatiles are most often used anywhere that you have large scrolling maps.

Games such as Donkey Kong Land 3, Mortal Kombat 3, Mortal Kombat 4, and Tarzan all use 16x16 metatiles. These games use a metatile map as well. Each position of this map usually contains 1 or 2 bytes. If each position has one byte then up to 256 unique metatiles can be used by the map. These metatile maps can be nearly 4 (or 8 for the GBC) times smaller (ROM space wise) than a similar tile map that doesn't use metatiles. If each position of the map uses 2 bytes then the map can support many unique metatiles. These metatile maps can be 2 (or 4 for the GBC) times smaller (ROM space wise) than a similar tile map that doesn't use metatiles.

Each position of a metatile map has an index into a metatile lookup table. Each position in the metatile lookup table often has 4 bytes (assuming 16x16 metatiles) that index 4 different 8x8 tiles in the tile table and there are often 4 more bytes (assuming 16x16 metatiles) that hold the tile attributes for these tiles for GBC support.

When using metatiles, you lose some ROM space due to the addition of a metatile table but you often gain much more ROM space than you lose due to the much smaller map(s). The space savings often means that you can put a much larger map (pixel-wise) into a single ROM bank which can make things easier programming wise.

The concept of metatiles often assumes as well that 8x8 tiles will be used in multiple metatiles. This is often a very good assumption because the GB & GBC just don't have enough 8x8 tiles available to provide unique tiles at every screen location in large, scrolling maps. It is possible to dynamically load unique tiles at the edges of a scrolling screen (in order to have all unique tiles) but the ROM space required for all of these unique tiles can often be prohibitive.

Is it possible to drain the batteries quicker through poor code ?

Yes. The following table illustrates power usage by the GBC. Sound is enabled by setting bit 7 of register NR52 ($ff26). Using assembly language command HALT in your main game loop saves power. 2X Spd is double speed mode. Infrared Receive is enabled by setting bits 6 & 7 of register RP ($ff56). Infrared Transmit is turned on by setting bit 0 of register RP ($$ff56).

These measurements are for total current (cart & GBC) drawn from the batteries. The cart used for testing was an original MBC5 cart with the ROM replaced with a low-power flash chip. As a result, these measurements should compare to a Nintendo published ROM cart within 1-2 milliamps. The code in this example used about 85% of the clock cycles available each game loop. (With low power consumption for 15% of each game loop when HALT was used.) This probably closely resembles many games.

As can be seen by the results, the best results are obtained by using none of the special features available, sound disabled, and using HALT in the main game loop. Ideally, your game should use as little battery power as is necessary. In other words, do NOT use features that require more battery power unless you can not accomplish what you are trying to do without using them.

For those that are confused by the values below, consider that a game that uses 55 milliamps will run on batteries for twice as many hours as a game that uses 110 milliamps. Actually, it will run for a little over twice as long but this gets into battery physics that are beyond the scope of these FAQs.

Milliamps | Enable Sound | No HALT | 2X Spd | Enable IR Rx | IR Tx On
55 - - - - -
70 X - - - -
58 - X - - -
62 - - X - -
57 - - - X -
128 - - - - X
74 X X - - -
78 X - X - -
83 X X X - -
162 | X | X | X | X | X

Is it important to pad a ROM with a specific value ?

Yes, for three reasons:

- Some flash carts program much more quickly when unused spaces in the ROM are padded with $ff.

- ROMs that have unused areas padded with a known value (rather than random data) compress much better. This is good when you need to send a ROM by email or for smaller, compressed, backup archives.

- Padding makes sure that unused sections of the ROM are filled with a known value rather than random data in your PC RAM. If you don't use padding sometimes source code or other information that you would rather not make public can be included in your ROM without your even knowing about it.

To test code on a GB you often have to pad your ROM size to a multiple of 2 in order for most flash programming tools to use the file. Padding is just unused bytes that are added to fill empty spaces in a ROM or at the end of a ROM. The type of padding you choose can greatly vary the amount of time it takes to program a Bung (and probably other) flash cart. With padding values of $00 being the worst and $ff being the best. Sometimes it can take up to 50% longer (maybe even longer) to flash a cart with pad values of $00. Here are command line switches for properly padding assembly files:

For RGBASM: -zff

For XLINK (RGBASM linker): -zff

For RGBFIX (Version by Otaku): -pff

For TASM: -fff

(Bung cart programming sensitivity to padding values info from Sam Nova.)

What are the specs of the Neo Geo Pocket Color ?

Screen Size: 160[wide] x 152[high] pixels
Colo(u)rs: 146 out of 4096 (simultaneous display)
Sprites: 64 8x8 characters (Up to 64 sprites in one line.)
Scrollable planes: 2 planes
Tiles available: 512
Cart Specs: Up to 16 Mbits linear ROM. (bankswitching required for ROMs over 16 Mbits.)
Other features: Internal clock/calendar chip (with 3V clock battery), Game Link Port, hardware multiply/divide instructions
Main Processor: Toshiba TLCS-900
Sound Processor: Zilog Z80, 6 channel PSG & 12 bit sample channel
Battery Life: 40 hours on 2xAA batteries.

There are no known publically available technical documents on this unit except for the main processor and the sound processor.

Are there any differences in the sound hardware on the GB, GBC, & SGB ?

The GB Classic, GB Pocket, SGB, & GBC all use the same sound hardware so programmming is identical for them all.

The SGB, however, has access to some SNES/Super Famicon sound effects as well. SGB games that have specifically been written to use these sound effects can have better sound, as a result, than average GB games.

Does it matter in which order I set the sound registers ?

Yes. Sound must be turned on before you can set any other sound registers. You do this by writing $80 to NR52 ($ff26).

What all techniques are used to play WAV files on a GB ?

Four bit wave playback - Sound channel 3 has a 4-bit 32 sample long wave RAM that you can set for playing a 32 sample waveform repeatedly. It wasn't specifically designed to handle continuously new samples but you can refill the wave RAM each time 32 samples have been sent to the speaker. This reload of RAM process causes the first sample to always be 0 which shows up in sample playback as a low frequency buzz sound. Depending on what sounds you are playing back this buzz is more or less noticeable.

Your samples will sound a lot clearer (and even louder, apparently) if you heavily optimize your sample playback routine. (Info from Jeremy Evers.)

One bit wave playback - Some games use volume modulation of register NR51 ($ff25) to do one bit wave playback. This involves setting, say for instance, sound channel 3 wave RAM to all $FF's and then just modulating bits 2 & 6 of NR51 to do the sound playback.

I get very low volume when playing WAVs. What can I do ?

This is a major problem and as a result it requires careful consideration.

For one, use compression (audio, not data) to make the sample "stronger".

Also, make sure that the original sound sample files are recorded very strongly. If you have to, use a sound editor program to increase the volume of each sample. Keep doing this even if the signal starts to clip if you want to get more volume.

It is a matter of personal preference as to how much clipping you wish to do to the sample. The more clipping you do the louder the sample will be with the trade off of adding more distortion. Some samples will be more tolerant of distortion than others so keep this in mind.

Should I disable sound if I am not using it ?

Yes. The sound circuits cause your game to use 10-15% more battery power even if you aren't actively playing a sound. Write $00 to NR52 ($ff26) to disable sound.

Can I dynamically modulate the volume envelope of a sound channel while it is playing ?

If you alter the envelope, the envelope may restart (or at least jump), and there are some things you need to take into account. However, if you force the envelope to be off (direction up, speed 0), you can easily make software envelopes by modulating the high nybble. Sometimes there is some small noise associated with this technique, however... (Info from Jeremy Evers.)

Can Wave Pattern RAM ($ff30-ff3f) be modified at any time ?

No. Sound channel 3 must be disabled before you can write data to Wave Pattern RAM ($ff30-ff3f). Sound channel 3 is disabled by writing $00 to NR30 ($ff1a).

Is there anything I need to know about using "sweep" ?

Yes. Using the sweep function on sound channel 1 will destroy the contents of the frequency registers NR13 ($ff13) & NR14 ($ff14).

Are there any bugs in the GB sound hardware ?

Yes. A problem with sound being cut off prematurely can occur when several conditions are met. The conditions are the following:

- the sound length registers, NR11, NR21 or NR31 - bits 0~6 are non-zero
- the continuous bit of sound registers NR14, NR24 or NR34 is set to 0 (continuous)
- and the initial bit of sound registers NR14, NR24 or NR34 is changed from 1 to 0 while the upper frequency is incremented or decremented.

This problem seems to be dependent on the value in the sound length registers, NR11, NR21 or NR31 - bits 0~6. The larger the value, the sooner it stops.

To get around this sound bug, set the duration again after the frequency high byte.

What size sprites does the GB support ?

8 x 8 or 8 x 16 pixels. If you use 8 x 16 size sprites then you can only have an even sprite number. (i.e. First sprite number = 0, second sprite number = 2, third sprite number = 4, ...) The lowest bit of the sprite number in 8 x 16 sprite mode is always 0 no matter what. (i.e. Selecting sprite number 3 gives you sprite number 2 instead.)

Several sprites side-by-side can be used to display a larger, virtual sprite. Several sprites on top of each other can be used to add more colo(u)rs to a sprite.

There are 40 sprites total available regardless of whether you are in 8 x 8 or 8 x 16 sprite mode.

Can 8 x 8 sprites & 8 x 16 sprites be used at the same time ?

Possibly so if you use the LYC interrupt carefully but you can't mix different sized sprites on the same line. Unless you really know what you're doing the answer is probably no to this question.

What happens if you exceed the GBs limit of 10 sprites per line ?

When you exceed 10 sprites per horizontal pixel line, only the sprites with the highest priorities will completely appear in that pixel line. (Sprite #0 has highest priority, sprite #1 next highest, etc.) The other sprites will partially or totally disappear (depending on how many of lines of each sprite break the 10 sprites-per-line limit.)

If you plan to have more than 10 sprites that at some time will share the same lines, then continuously alternate some of the sprite priorities with each vblank. This will cause them to blink annoyingly (but only when the 10 sprites-per-line limit is exceeded) but this is often better then them disappearing partially or completely.

Can you have a sprite appear above the background but below the window ?

On the GBC, yes. You might can do it on the other GBs as well but only with creative use of the LYC interrupt.

What happens if you have one sprite appear above the background and another sprite with higher priority appear below the background ?

In the case where two sprites such as these overlap, the sprite above the background will take on the colo(u)rs of the background pixels directly under it.

Some have said that the Amiga has a similar response under these conditions.

Software-wise, how do most games implement sprites ?

Sprite RAM is located from $fe00-$febf. You can write directly to sprite RAM to modify the sprite attribute table but you should only do this during V-blank for reliability.

Most commercial games don't write directly to sprite RAM. Instead, they use sprite DMA ($ff46). You can just setup a sprite attribute table that is 256 byte aligned ($xx00), in RAM for instance, and then use sprite DMA to transfer this table to sprite RAM. Often, sprite DMA is put at the start of a V-blank interrupt. As a result, your sprite attribute table is copied to sprite RAM automatically each V-blank.

What precautions should I use when writing assembly code & using sprites ?

If your writing GBC dedicated code then the following info doesn't apply.

If you are writing GB or GBC compatible assembly code then you need to make sure that the following instructions don't occur while their double register values are in the range $fe00-$feff or else sprite RAM will once in a while get trashed causing sprite flicker & other strange sprite behaviour:

inc xx
dec xx

(xx = bc, de, hl, or sp)

What precautions should I use when doing Sprite DMA ?

In order to do a sprite DMA you have to have a sprite table with address $xx00. (x = don't care) Depending on the address of this sprite table you have several options:

Sprite table not in video RAM ($8000-$9fff) - Most games do not put their sprite table in video RAM but rather usually use low RAM ($c000-$dfff). The reason for this is because you can write to low RAM at any time but video RAM is picky about writes when the screen is on.

When using sprite DMA ($ff46) with a sprite table that is not in video RAM, the code which performs this DMA function must be located in high RAM ($ff80-$fffe) because, except for video RAM, all other memory areas are disabled. The duration of sprite DMA is ~160 (80 in double speed) microseconds.

In order to do this you have to copy a small routine to high RAM containing the DMA command, a small delay loop, and a RET instruction to return to the main routine that called it. Interrupts MUST be disabled during non-video RAM sprite table DMAs. Example assembly code (rDMA=$ff46):

; $c000 ~ c09f --> OAM

        ld      a,$c0

        ld      [rDMA],a

        ld      a,40

.loop:

        dec     a

        jr      nz,.loop

        ret

Sprite table in video RAM ($8000-$9fff) - If you choose to put your sprite table in video RAM then you can perform sprite DMA using code in ROM (no need to call high RAM) and you don't have to wait for DMA completion. Also, interrupts don't have to be disabled to use sprite DMA in this case. Example assembly code (rDMA=$ff46):

; $9f00 ~ 9f9f --> OAM

        ld      a,$9f

        ld      [rDMA],a

I have read somewhere someone mentioning "background" sprites. What are these and how do I use them ?

The terminology "background" sprite is in reference to a virtual sprite, not a physical sprite. It is a software technique that is used that makes it appear that you are using sprite hardware but in reality you are not.

This was a popular technique that was used in such games as Mortal Kombat 2 in order to draw the actors. It involves writing directly into the background map. You can even do smooth, single pixel scrolls of "background" sprites that contain transparent pixels if your code is advanced enough to handle that.

Since the release of the GBC, this technique is not as feasible to implement due to limitations of the GBC background colo(u)r palettes.

Is it possible to display more than 40 sprites at one time ?

Yes, but it is tricky. Ferrari for GB does this. You have to use an LYC interrupt to modify the OAM (sprite RAM) while the screen is drawing.

How do sprite priority settings work on the GBC ?

Among sprites themselves, sprite #0 has highest priority, sprite #1 has next highest priority, etc.(i.e. Sprite #0 will always appear over sprite #1.)

Bit 7 of the sprite attribute flags determines that a sprite will appear above the background & window if it is 0 and below them both (visible only through colo(u)r 0 of background & window) if it is 1.

When bit 7 of bank 1 of tile attribute memory ($9800-$9fff) is set to 1 it will force the background and/or window to have priority over the sprite attribute flags and to appear over sprites no matter what the setting of the sprite attribute flags.

If bit 0 of register LCDC ($ff40) is 0 then sprites will always appear above the background & window regardless of the settings of sprite attribute flags & tile attribute memory.

What assemblers are available ?

RGBDS, TASM (Table ASseMbler, not Turbo ASseMbler), ADVancedGBIDE, ISAS, and the assembler which is provided with GBDK. ISAS is only available to licensed developers. Many people use RGBDS and it is probably the most powerful assembler available. Quite a few people use TASM due to it's simplicity. For serious work, though, RGBDS or ISAS are probably the best choices.

Here are the main differences between the two most popular & easily available assemblers:

TASM Advantages:

* Allows quick development for short programs because it's a "simple" compiler with linker built in.

* Supports 0xh (hex) & 0xb (binary) formats preferred by some that don't like using $x for hex & %x for binary numbers.

* Has a nice listing output for the compiler results.

* Allows using '[]' or '()' to reference memory. (If you are using the latest TASM table...)

RGBASM Advantages:

* Allows development of GameBoy programs up to 4Mbytes in size. To develop a ROM larger than 32kbytes in size using TASM you have to paste files together using DOS "copy /b f1+f2 f3" or something similar. If you plan on calling code in 2 or more different ROM banks you have to assemble these files separately and manually make sure that CALLs or JPs to these banks are valid as the assembler is incapable of catching errors in this case.

* Allows including all or part of a raw binary data file into your program. Often this means data such as a tileset, tile map, etc. To do this with TASM you have to convert the binary data to the format .byte $xx,$xx,$xx,$xx,.... using a program such as 'dump' by Jens Ch. Restemeier.

* Has a macro language which blows tasm away. Period.

* Automatically optimizes 'LD' instructions. A 'LD' from $ff00-$ffff takes two bytes. A 'LD' from the rest of memory takes three bytes. TASM isn't capable enough to auto-optimize.

* Allows local labels between standard labels:

Test1:

        ld      a,[x]

.loop:

        dec     a

        jr      nz,.loop

Test2:

        ld      a,[x]

.loop:

        dec     a

        jr      nz,.loop

(Local labels start with .) You can not do this with TASM. Local labels are very helpful in large code projects.

* Has a compiler directive BANK(x) that returns the bank number in which label 'x' resides. As a result, it is easy to build a macro such as 'lcall x' that does a call from bank-to-bank and you don't have to specifically state which bank you wish to call. (The macro will figure this out for you by using BANK(x).) With TASM you'd have to do something like 'lcall 5,x' in which you specify the bank and the address in the bank you wish to CALL.

* Uses an Assembler / Linker setup so you can use Borland or GNU MAKE. Therefore don't have to recompile your whole project when making a minor change.

Where can I get bmp/pcx/gif/jpg -> GB picture converters ?

For the GB check...

- MegaMan_X's web page on the GB Dev web ring for a PCX2GB converter.
- Jens Ch. Restemeier's web page for a GIF2GB converter which includes C source code.
- Jeff's GIF2GB converter which includes DJGPP C source code.

For the GB & GBC check...

- 'gcgen' in the download section of Bung's web page. Your screen resolution has to be 24 or 32 bit colo(u)r depth for it to work properly.
- BMP2GB converter in GB Development Studio for GBDK. Check some pages on the GB Dev web ring for this software package.
- Educa's Gfx2GBC converter - No SAVE feature currently.
- Paul Stapley's Gfx2GBC converter

What is a tile editor and why do I need one ?

The basic building blocks of the GB display are tiles. As such, it is handy to have a tile editor to allow you to design tiles that are to be displayed on the GB screen.

What is a map editor and why do I need one ?

A map editor allows you to take tiles that you have designed and arrange these into a 2 dimensional map of arbitrary X and Y size. The GB has a built-in hardware tile map that is composed of 8x8 pixel tiles and the map itself is 32x32 tiles wide/high. As such, tile maps of 32x32 tiles are popular.

Even though the tile map in the GB is limited to 32x32 tiles, you can display maps larger than this by dynamically updating the hardware tile map during screen scrolling.

What map or tile editors are available ?

Tile Buddy by GameBrains is a very powerful Win95 app for Tile/Map/Animation editing.

GB Tile Designer (GBTD) & GB Map Builder (GBMB) by Harry Mulder are excellent Win95 editors.

TILE256 is a nice DOS tile/map editor.

Where can I get a .WAV -> GB converter ?

Check MegaMan_X's web page on the GB Dev web ring.

Important Web Links

GB Specification Information - PDF* Format - Text Format
(Also known as the updated pan/kOOPa document. Latest version is 12-Mar-98. Also contains a fairly complete reference of the Memory Bank Controllers (MBC) used by the GB & GBC. )

GameBoy CPU Manual - PDF* Format - PDF* Zipped
This contains info from the GB specification above plus GB opcodes & timings all in one reference.

GB Printer Specification Information - MS Word Format
Information on the data format that is sent to the GB Printer.

GB Development Web Ring List - Most of the web pages involved in GB/GBC development can be found linked here.

RGBDS & RGBFIX - Check Otaku's page for the latest versions of these programs.

Jen Ch. Restemeier's Page - Good place for a GIF2GB converter & GB data compression tools.

Windows NT GIVEIO.SYS Installation - For using Bung's GB Xchanger on WinNT you will need to install GIVEIO.SYS on you system. On this web page scroll down until you locate 'Windows NT Support'.

* For a free PDF document viewer try Adobe Acrobat Reader.


Imran Nazar: GameBoy Z80 Opcode Map

Skip to navigation

The following is a full opcode map of instructions for the Nintendo-spec modified Z80 included in the GameBoy CPU.

Updated: [2010-07-28] Details added for SWAP, [2010-07-29] XOR n reinstated

 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
0x | NOP | LD BC,nn | LD (BC),A | INC BC | INC B | DEC B | LD B,n | RLC A | LD (nn),SP | ADD HL,BC | LD A,(BC) | DEC BC | INC C | DEC C | LD C,n | RRC A
1x | STOP | LD DE,nn | LD (DE),A | INC DE | INC D | DEC D | LD D,n | RL A | JR n | ADD HL,DE | LD A,(DE) | DEC DE | INC E | DEC E | LD E,n | RR A
2x | JR NZ,n | LD HL,nn | LDI (HL),A | INC HL | INC H | DEC H | LD H,n | DAA | JR Z,n | ADD HL,HL | LDI A,(HL) | DEC HL | INC L | DEC L | LD L,n | CPL
3x | JR NC,n | LD SP,nn | LDD (HL),A | INC SP | INC (HL) | DEC (HL) | LD (HL),n | SCF | JR C,n | ADD HL,SP | LDD A,(HL) | DEC SP | INC A | DEC A | LD A,n | CCF
4x | LD B,B | LD B,C | LD B,D | LD B,E | LD B,H | LD B,L | LD B,(HL) | LD B,A | LD C,B | LD C,C | LD C,D | LD C,E | LD C,H | LD C,L | LD C,(HL) | LD C,A
5x | LD D,B | LD D,C | LD D,D | LD D,E | LD D,H | LD D,L | LD D,(HL) | LD D,A | LD E,B | LD E,C | LD E,D | LD E,E | LD E,H | LD E,L | LD E,(HL) | LD E,A
6x | LD H,B | LD H,C | LD H,D | LD H,E | LD H,H | LD H,L | LD H,(HL) | LD H,A | LD L,B | LD L,C | LD L,D | LD L,E | LD L,H | LD L,L | LD L,(HL) | LD L,A
7x | LD (HL),B | LD (HL),C | LD (HL),D | LD (HL),E | LD (HL),H | LD (HL),L | HALT | LD (HL),A | LD A,B | LD A,C | LD A,D | LD A,E | LD A,H | LD A,L | LD A,(HL) | LD A,A
8x | ADD A,B | ADD A,C | ADD A,D | ADD A,E | ADD A,H | ADD A,L | ADD A,(HL) | ADD A,A | ADC A,B | ADC A,C | ADC A,D | ADC A,E | ADC A,H | ADC A,L | ADC A,(HL) | ADC A,A
9x | SUB A,B | SUB A,C | SUB A,D | SUB A,E | SUB A,H | SUB A,L | SUB A,(HL) | SUB A,A | SBC A,B | SBC A,C | SBC A,D | SBC A,E | SBC A,H | SBC A,L | SBC A,(HL) | SBC A,A
Ax | AND B | AND C | AND D | AND E | AND H | AND L | AND (HL) | AND A | XOR B | XOR C | XOR D | XOR E | XOR H | XOR L | XOR (HL) | XOR A
Bx | OR B | OR C | OR D | OR E | OR H | OR L | OR (HL) | OR A | CP B | CP C | CP D | CP E | CP H | CP L | CP (HL) | CP A
Cx | RET NZ | POP BC | JP NZ,nn | JP nn | CALL NZ,nn | PUSH BC | ADD A,n | RST 0 | RET Z | RET | JP Z,nn | Ext ops | CALL Z,nn | CALL nn | ADC A,n | RST 8
Dx | RET NC | POP DE | JP NC,nn | XX | CALL NC,nn | PUSH DE | SUB A,n | RST 10 | RET C | RETI | JP C,nn | XX | CALL C,nn | XX | SBC A,n | RST 18
Ex | LDH (n),A | POP HL | LDH (C),A | XX | XX | PUSH HL | AND n | RST 20 | ADD SP,d | JP (HL) | LD (nn),A | XX | XX | XX | XOR n | RST 28
Fx | LDH A,(n) | POP AF | XX | DI | XX | PUSH AF | OR n | RST 30 | LDHL SP,d | LD SP,HL | LD A,(nn) | EI | XX | XX | CP n | RST 38
Table 1: Base opcode map
 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
0x | RLC B | RLC C | RLC D | RLC E | RLC H | RLC L | RLC (HL) | RLC A | RRC B | RRC C | RRC D | RRC E | RRC H | RRC L | RRC (HL) | RRC A
1x | RL B | RL C | RL D | RL E | RL H | RL L | RL (HL) | RL A | RR B | RR C | RR D | RR E | RR H | RR L | RR (HL) | RR A
2x | SLA B | SLA C | SLA D | SLA E | SLA H | SLA L | SLA (HL) | SLA A | SRA B | SRA C | SRA D | SRA E | SRA H | SRA L | SRA (HL) | SRA A
3x | SWAP B | SWAP C | SWAP D | SWAP E | SWAP H | SWAP L | SWAP (HL) | SWAP A | SRL B | SRL C | SRL D | SRL E | SRL H | SRL L | SRL (HL) | SRL A
4x | BIT 0,B | BIT 0,C | BIT 0,D | BIT 0,E | BIT 0,H | BIT 0,L | BIT 0,(HL) | BIT 0,A | BIT 1,B | BIT 1,C | BIT 1,D | BIT 1,E | BIT 1,H | BIT 1,L | BIT 1,(HL) | BIT 1,A
5x | BIT 2,B | BIT 2,C | BIT 2,D | BIT 2,E | BIT 2,H | BIT 2,L | BIT 2,(HL) | BIT 2,A | BIT 3,B | BIT 3,C | BIT 3,D | BIT 3,E | BIT 3,H | BIT 3,L | BIT 3,(HL) | BIT 3,A
6x | BIT 4,B | BIT 4,C | BIT 4,D | BIT 4,E | BIT 4,H | BIT 4,L | BIT 4,(HL) | BIT 4,A | BIT 5,B | BIT 5,C | BIT 5,D | BIT 5,E | BIT 5,H | BIT 5,L | BIT 5,(HL) | BIT 5,A
7x | BIT 6,B | BIT 6,C | BIT 6,D | BIT 6,E | BIT 6,H | BIT 6,L | BIT 6,(HL) | BIT 6,A | BIT 7,B | BIT 7,C | BIT 7,D | BIT 7,E | BIT 7,H | BIT 7,L | BIT 7,(HL) | BIT 7,A
8x | RES 0,B | RES 0,C | RES 0,D | RES 0,E | RES 0,H | RES 0,L | RES 0,(HL) | RES 0,A | RES 1,B | RES 1,C | RES 1,D | RES 1,E | RES 1,H | RES 1,L | RES 1,(HL) | RES 1,A
9x | RES 2,B | RES 2,C | RES 2,D | RES 2,E | RES 2,H | RES 2,L | RES 2,(HL) | RES 2,A | RES 3,B | RES 3,C | RES 3,D | RES 3,E | RES 3,H | RES 3,L | RES 3,(HL) | RES 3,A
Ax | RES 4,B | RES 4,C | RES 4,D | RES 4,E | RES 4,H | RES 4,L | RES 4,(HL) | RES 4,A | RES 5,B | RES 5,C | RES 5,D | RES 5,E | RES 5,H | RES 5,L | RES 5,(HL) | RES 5,A
Bx | RES 6,B | RES 6,C | RES 6,D | RES 6,E | RES 6,H | RES 6,L | RES 6,(HL) | RES 6,A | RES 7,B | RES 7,C | RES 7,D | RES 7,E | RES 7,H | RES 7,L | RES 7,(HL) | RES 7,A
Cx | SET 0,B | SET 0,C | SET 0,D | SET 0,E | SET 0,H | SET 0,L | SET 0,(HL) | SET 0,A | SET 1,B | SET 1,C | SET 1,D | SET 1,E | SET 1,H | SET 1,L | SET 1,(HL) | SET 1,A
Dx | SET 2,B | SET 2,C | SET 2,D | SET 2,E | SET 2,H | SET 2,L | SET 2,(HL) | SET 2,A | SET 3,B | SET 3,C | SET 3,D | SET 3,E | SET 3,H | SET 3,L | SET 3,(HL) | SET 3,A
Ex | SET 4,B | SET 4,C | SET 4,D | SET 4,E | SET 4,H | SET 4,L | SET 4,(HL) | SET 4,A | SET 5,B | SET 5,C | SET 5,D | SET 5,E | SET 5,H | SET 5,L | SET 5,(HL) | SET 5,A
Fx | SET 6,B | SET 6,C | SET 6,D | SET 6,E | SET 6,H | SET 6,L | SET 6,(HL) | SET 6,A | SET 7,B | SET 7,C | SET 7,D | SET 7,E | SET 7,H | SET 7,L | SET 7,(HL) | SET 7,A
Table 2: Two-byte instruction codes (CB-prefix table)

Article dated: 16th Nov 2009

Operated by Imran Nazar Ltd, registered in the UK (#07698370). Content copyright Imran Nazar, 2005-2014.
Design and imagery copyright Imran Nazar, 2008-2011; "Parchment" used by license from sxc.
  • Articles
  • Programming
  • Fiction
  • Get in touch
Get the RSS feed


Imran Nazar: GameBoy Emulation in JavaScript: Graphics

Skip to navigation

This is part 4 of an article series on emulation development in JavaScript; ten parts are currently available, and others are expected to follow.

  • Part 1: The CPU
  • Part 2: Memory
  • Part 3: GPU Timings
  • Part 4: Graphics
  • Part 5: Integration
  • Part 6: Input
  • Part 7: Sprites
  • Part 8: Interrupts
  • Part 9: Memory Banking
  • Part 10: Timers

Previously in this series, the shape of a GameBoy emulator was brought together, and the timings established between the CPU and graphics processor. A canvas has been initialised and is ready for graphics to be drawn by the emulated GameBoy; the GPU emulation now has structure, but is still unable to render graphics to the framebuffer. In order for the emulation to render graphics, the concepts behind GameBoy graphics must be briefly examined.

Backgrounds

Just like most consoles of the era, the GameBoy didn't have enough memory to allow for a direct framebuffer to be held in memory. Instead, a tile system is employed: a set of small bitmaps is held in memory, and a map is built using references to these bitmaps. The innate advantage to this system is that one tile can be used repeatedly through the map, simply by using its reference.

The GameBoy's tiled graphics system operates with tiles of 8x8 pixels, and 256 unique tiles can be used in a map; there are two maps of 32x32 tiles that can be held in memory, and one of them can be used for the display at a time. There is space in the GameBoy memory for 384 tiles, so half of them are shared between the maps: one map uses tile numbers from 0 to 255, and the other uses numbers between -128 and 127 for its tiles.

In video memory, the layout of the tile data and maps runs as follows.

Region | Usage
8000-87FF | Tile set #1: tiles 0-127
8800-8FFF | Tile set #1: tiles 128-255 Tile set #0: tiles -1 to -128
9000-97FF | Tile set #0: tiles 0-127
9800-9BFF | Tile map #0
9C00-9FFF | Tile map #1
Table 1: VRAM layout

When a background is defined, its map and tile data interact to produce the graphical display:

Background map and tile data Figure 1: Background mapping

The background map is, as previously mentioned, 32x32 tiles; this comes to 256 by 256 pixels. The display of the GameBoy is 160x144 pixels, so there's scope for the background to be moved relative to the screeen. The GPU achieves this by defining a point in the background that corresponds to the top-left of the screen: by moving this point between frames, the background is made to scroll on the screen. For this reason, the definition of the top-left corner is held by two GPU registers: Scroll X and Scroll Y.

Background scroll registers Figure 2: Background scroll registers

Palettes

The GameBoy is often described as a monochrome machine, capable of displaying only black and white. This isn't quite true: the GameBoy can also handle light and dark grey, for a total of four colours. Representing one of these four colours in the tile data takes two bits, so each tile in the tile data set is held in (8x8x2) bits, or 16 bytes.

One additional complication for the GameBoy background is that a palette is intersticed between the tile data and the final display: each of the four possible values for a tile pixel can correspond to any of the four colours. This is used mainly to allow easy colour changes for the tile set; if, for example, a set of tiles is held corresponding to the English alphabet, an inverse-video version can be built by changing the palette, instead of taking up another part of the tile set. The four palette entries are all updated at once, by changing the value of the Background Palette GPU register; the colour references used, and the structure of the register, are shown below.

Value | Pixel | Emulated colour
0 | Off | [255, 255, 255]
1 | 33% on | [192, 192, 192]
2 | 66% on | [96, 96, 96]
3 | On | [0, 0, 0]
Table 2: Colour reference values Background palette Figure 3: Background palette register

Implementation: tile data

As stated above, each pixel in the tile data set is represented by two bits: these bits are read by the GPU when the tile is referenced in the map, run through the palette and pushed to screen. The hardware of the GPU is wired such that one whole row of the tile is accessible at the same time, and the pixels are cycled through by running up the bits. The only issue with this is that one row of the tile is two bytes: from this results the slightly convoluted scheme for storage of the bits, where each pixel's low bit is held in one byte, and the high bit in the other byte.

Tile data bitmap Figure 4: Tile data bitmap structure

Since JavaScript isn't ideally suited for manipulating bitmap structures quickly, the most time-efficient way of handling the tile data set is to maintain an internal data set alongside the video memory, with a more expanded view where each pixel's value has been pre-calculated. In order for this to accurately reflect the tile data set, any writes to the video RAM must trigger the function to update the GPU's internal tile data.

GPU.js: Internal tile data

   _tileset: [],

   reset: function()
   {
       // In addition to previous reset code:
GPU._tileset = [];
for(var i = 0; i < 384; i++)
{
    GPU._tileset[i] = [];
    for(var j = 0; j < 8; j++)
    {
        GPU._tileset[i][j] = [0,0,0,0,0,0,0,0];
    }
}
   },

   // Takes a value written to VRAM, and updates the
   // internal tile data set
   updatetile: function(addr, val)
   {
       // Get the "base address" for this tile row
addr &= 0x1FFE;

// Work out which tile and row was updated
var tile = (addr >> 4) & 511;
var y = (addr >> 1) & 7;

var sx;
for(var x = 0; x < 8; x++)
{
    // Find bit index for this pixel
    sx = 1 << (7-x);

    // Update tile set
    GPU._tileset[tile][y][x] =
        ((GPU._vram[addr] & sx)   ? 1 : 0) +
        ((GPU._vram[addr+1] & sx) ? 2 : 0);
}
   }

MMU.js: Tile update trigger

   wb: function(addr, val)
   {
       switch(addr & 0xF000)
{
           // Only the VRAM case is shown:
    case 0x8000:
    case 0x9000:
    GPU._vram[addr & 0x1FFF] = val;
    GPU.updatetile(addr, val);
    break;
}
   }

Implementation: Scan rendering

With these pieces in place, it's possible to begin rendering the GameBoy screen. Since this is being done on a line-by-line basis, the renderscan function referred to in Part 3 must, before it renders a scanline, work out where it is on the screen. This involves calculating the X and Y coordinates of the position in the background map, using the scroll registers and the current scanline counter. Once this has been determined, the scan renderer can advance through each tile in that row of the map, pulling in new tile data as it encounters each tile.

GPU.js: Scan rendering

   renderscan: function()
   {
// VRAM offset for the tile map
var mapoffs = GPU._bgmap ? 0x1C00 : 0x1800;

// Which line of tiles to use in the map
mapoffs += ((GPU._line + GPU._scy) & 255) >> 3;

// Which tile to start with in the map line
var lineoffs = (GPU._scx >> 3);

// Which line of pixels to use in the tiles
var y = (GPU._line + GPU._scy) & 7;

// Where in the tileline to start
var x = GPU._scx & 7;

       // Where to render on the canvas
var canvasoffs = GPU._line * 160 * 4;

// Read tile index from the background map
var colour;
var tile = GPU._vram[mapoffs + lineoffs];

// If the tile data set in use is #1, the
// indices are signed; calculate a real tile offset
if(GPU._bgtile == 1 && tile < 128) tile += 256;

for(var i = 0; i < 160; i++)
{
    // Re-map the tile pixel through the palette
    colour = GPU._pal[GPU._tileset[tile][y][x]];

    // Plot the pixel to canvas
    GPU._scrn.data[canvasoffs+0] = colour[0];
    GPU._scrn.data[canvasoffs+1] = colour[1];
    GPU._scrn.data[canvasoffs+2] = colour[2];
    GPU._scrn.data[canvasoffs+3] = colour[3];
    canvasoffs += 4;

    // When this tile ends, read another
    x++;
    if(x == 8)
    {
    x = 0;
    lineoffs = (lineoffs + 1) & 31;
    tile = GPU._vram[mapoffs + lineoffs];
    if(GPU._bgtile == 1 && tile < 128) tile += 256;
    }
}
   }

Next steps: Output

With a CPU, memory handling and a graphics subsystem, the emulator is nearly capable of producing output. In part 5, I'll be looking at what's required to get the system from a disparate set of module files to a coherent whole, capable of loading and running a simple ROM file: tying the graphics registers to the MMU, and a simple interface to control the running of the emulation.

Imran Nazar <tf@imrannazar.com>, Aug 2010.

Article dated: 25th Aug 2010

Operated by Imran Nazar Ltd, registered in the UK (#07698370). Content copyright Imran Nazar, 2005-2014.
Design and imagery copyright Imran Nazar, 2008-2011; "Parchment" used by license from sxc.
  • Articles
  • Programming
  • Fiction
  • Get in touch
Get the RSS feed


Пишем эмулятор Gameboy, часть 2 / Хабрахабр

Разделы
Публикации Хабы Компании Пользователи Q&A Песочница Биржа авторов
Спецпроекты
Конкурс приложений Биржа статей Дарите улыбки
22 октября 2012 в 13:29

Пишем эмулятор Gameboy, часть 2 tutorial

Разработка *, Программирование *
Здравствуйте!

В предыдущей части данного цикла статей мы рассмотрели процессор и память DMG. Следующий логичный шаг — эмуляция того, как DMG выводит изображение.

Пишем эмулятор Gameboy, часть 1
Пишем эмулятор Gameboy, часть 2
Пишем эмулятор Gameboy, часть 3

Оглавление

Дисплей
  • LCD-контроллер. Теория
  • LCD-контроллер. Реализация
  • Графика. Теория
    • Background
    • Window
    • Sprites
  • Графика. Реализация
Таймеры
Управление
Сводим все воедино
Тестирование
Заключение

Дисплей

На данном этапе нам необходимо эмулировать то, как DMG отображает на экране картинку. Все будет находиться в классе Cookieboy::GPU (ссылка на исходный код Cookieboy). Задачу можно поделить на две большие части – эмуляция специфики того, как DMG рисует картинку; эмуляция логики, которая управляет экраном.

LCD-контроллер. Теория
Начнем с логики, поскольку именно она будет диктовать, когда и что нам рисовать. Как всегда, перед реализацией нам надо понять, как работает компонент.

DMG выводит изображение на экране построчно и эмулирует состояния, характерные для ЭЛТ экранов. Каждое состояние длится строго определенное число тактов. Необходимо это для обеспечения доступа к памяти. Для отрисовки графики доступ к видеопамяти и OAM необходим сразу в двух местах – LCD-контроллеру, который выводит все на экран; игре (CPU), которая модифицирует память, чтобы вывести тот кадр, который ей нужен. Для решения этой проблемы вся задача вывода графики была поделена на интервалы (каждому соответствует определенное состояние), которые и определяют время, в течение которого логика экрана или игра может получить доступ к памяти. Всего состояний четыре (номера не случайные, а строго определенные для DMG):
  • 0. H-blank. Для ЭЛТ-экранов означает, что в этот момент сканирующий луч переходит в начало следующей строки. У DMG, естественно, никаких лучей нет. Это состояние означает две вещи. Первое, была выведена одна строка. Второе, видеопамять и OAM не используются LCD-контроллером и доступны CPU.
  • 1. V-blank. Еще одно состояние из мира ЭЛТ, которое означает момент, когда луч дошел до конца последней строки и переходит в начало первой строки. Для нас оно означает две вещи. Первое, были выведены все 144 видимые строки. Второе, видеопамять и OAM не используются LCD-контроллером и доступны CPU.
  • 2. OAM. Данное состояние означает, что LCD-контроллер использует OAM память. CPU она не доступна, зато пока еще доступна видеопамять.
  • 3. OAMRAM. Данное состояние означает, что LCD-контроллер использует OAM и видеопамять. CPU они недоступны.

При отрисовке каждой строки LCD-контроллер проходит через состояния в таком порядке – 2, 3, 0. После отрисовки последней строки он переходит в состояние 1. Затем все начинается заново с первой строки.

Отрисовка одной строки длится ровно 456 тактов. Это время складывается из длительности состояний 2, 3, 0 и всегда равно 456 тактам, но длительность самих состояний может варьироваться. Поскольку экран имеет 144 строки, то их вывод занимает 65664 тактов. Еще ровно 4560 тактов длится состояние 1. Из цифры видно, что это время равно 10 строкам. Это действительно так – в состоянии 1 происходит как бы отрисовка еще 10 строк. Счетчик строк (регистр LY) не останавливается на 143, а доходит до 153. В итоге, полное обновление экрана занимает 70224 тактов или 154 строки по 456 тактов.

Переход между состояниями сопровождается запросами прерываний, если они разрешены. Переход в каждое из четырех состояний, кроме третьего, сопровождается запросом прерывания LCDC. Помимо этого, это прерывание запрашивается, если LY и LYC равны. Запрос осуществляется только если LCDC прерывание для данного состояния разрешено в регистре STAT. Его структура выглядит следующим образом:
Биты | Назначение
6 | Разрешить LCDC прерывание в случае равенства регистров LY и LYC
5 | Разрешить LCDC прерывание при переходе в состояние 2
4 | Разрешить LCDC прерывание при переходе в состояние 1
3 | Разрешить LCDC прерывание при переходе в состояние 0
2 | Бит установлен, если LY и LYC равны. Сброшен иначе
0-1 | Текущее состояние

Важная деталь – LCDC прерывание может быть запрошено только один раз за строку.

Много из этого можно было бы почерпнуть из CPU Manual, но тут есть одно но – это далеко не все, что нужно знать о работе LCD-контроллера для его эмуляции. Я не вдавался во все подробности (собственно, я их и не нашел), а лишь остановился на том, что позволяет корректно выводить графику в протестированных мной играх, а заодно проходить тест LCD-контроллера.

Чтобы хотя бы примерно эмулировать контроллер, нам придется ввести еще один набор состояний – внутренний, доступный лишь для нашего эмулятора. Всего их 8:
enum InternalLCDModes
{
    LCDMODE_LY00_HBLANK,
    LCDMODE_LYXX_HBLANK,
    LCDMODE_LYXX_HBLANK_INC,
    LCDMODE_LY00_VBLANK,
    LCDMODE_LY9X_VBLANK,
    LCDMODE_LY9X_VBLANK_INC,
    LCDMODE_LYXX_OAM,
    LCDMODE_LYXX_OAMRAM
};

Из названий видно, что они соответствуют реальным состояниям. Проходят они в следующем порядке:
Строка | Состояния
0 | LYXX_OAM -> LYXX_OAMRAM -> LYXX_HBLANK -> LYXX_HBLANK_INC
1 | LYXX_OAM -> LYXX_OAMRAM -> LYXX_HBLANK -> LYXX_HBLANK_INC
... | ...
143 | LYXX_OAM -> LYXX_OAMRAM -> LYXX_HBLANK -> LYXX_HBLANK_INC
144 | LY9X_VBLANK -> LY9X_VBLANK_INC
... | ...
152 | LY9X_VBLANK -> LY9X_VBLANK_INC
153 | LY00_VBLANK -> LY00_HBLANK

Промежуточные состояния нужны для более точной синхронизации различных событий. Рассмотрим каждое состояние подробнее.

LCDMODE_LYXX_OAM. При переходе в это состояние мы изменяем в регистре STAT состояние на 2 (чтение OAM). Проверяем, разрешено ли LCDC прерывание. В случае успеха запрашиваем его и помечаем где-то, что LCDC прерывание больше запрашивать нельзя.

В этом состоянии мы учитываем одну особенность DMG. Если у регистра SCX установлен бит 2 (например, регистр равен 4), то именно сейчас нам надо где-то отметить, что следующие состояния должны изменить свою длительность на 4 такта. Данное состояние длится ровно 80 тактов.

LCDMODE_LYXX_OAMRAM. Изменяем в STAT состояние на 3. LCDC прерывания нет. Пока мы не касались темы спрайтов, но именно здесь будет находиться вызов функции, которая составляет очередь спрайтов, которые будут позже выведены на данной строке.

Это еще одна особенность DMG. Вывод спрайтов изменяет длительность состояний. Чем больше их, тем дольше длится состояние 3 и тем короче состояние 0 (нам надо уложиться в 456 тактов, поэтому все пропорционально). DMG может вывести максимум 10 спрайтов в одной строке, поэтому нам хватит массива из 11 элементов со значениями, которые указывают изменение длительности состояний. В нем должны быть следующие значения:
Кол-во спрайтов | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
Такты | 0 | 8 | 20 | 32 | 44 | 52 | 64 | 76 | 88 | 96 | 108

Данное состояние длится 172 такта + кол-во тактов из-за регистра SCX + кол-во тактов из-за спрайтов. Например, если регистр SCX на предыдущем состоянии был равен 4, а спрайтов выводится 6, то длительность состояния будет равна 172 + 4 + 64 такта.

LCDMODE_LYXX_HBLANK. Здесь мы отрисовываем текущую строку, которую можно узнать из регистра LY. Отмечаем в регистре STAT состояние 0. Запрашиваем LCDC прерывание, если оно еще не было запрошено.

Длина такта высчитывается так: 200 тактов – такты из-за SCX – такты из-за спрайтов. Таким образом, здесь мы компенсируем то смещение в длительности, которое произошло в состоянии LCDMODE_LYXX_OAMRAM.

LCDMODE_LYXX_HBLANK_INC. Здесь мы увеличиваем регистр LY на единицу — он является счетчиком строк. Обнуляем флаг, который указывает, что прерывание LCDC уже было запрошено (мы ведь переходим на следующую строку). Здесь же нам необходимо проверить на равенство регистры LY и LYC. Вот примерный псевдокод:
Если LY == LYC
    Если бит 2 в регистре STAT сброшен
        Устанавливаем бит 2 в регистре STAT
        Запрашиваем LCDC прерывание, если оно разрешено
Иначе
     Сбрасываем бит 2 в регистре STAT

Регистр LYC используется играми для отслеживания момента, когда LY достигнет определенного значения. Других назначений у него нет.

Данное состояние длится 4 такта. В какое состояние будет осуществляться переход, зависит от значения LY. Все указано в таблице выше.

LY9X_VBLANK. Если мы перешли в это состояние и LY равен 144, то нам надо как-то отменить, что мы перешли в состояние V-blank. Устанавливаем в STAT состояние 1. Запрашиваем прерывание V-blank. Здесь же надо опять запросить LCDC прерывание, если оно разрешено. Таким образом, здесь может быть запрошено два прерывания. Если LY не равен 144, то ничего этого делать не надо, т.к. мы и так в V-blank.

Данное состояние длится ровно 452 такта.

LY9X_VBLANK_INC. Здесь нам надо инкрементировать LY и проверить на равенство LY и LYC, как мы это делали раньше. Здесь же надо учесть, что при LY равном 153 мы переходим в другое состояние, а не начинаем заново с LY9X_VBLANK – см. таблицу.

Длится данное состояние 4 такта.

LY00_VBLANK. Здесь нам надо обнулить LY.

Длится данное состояние 452 такта.

LCDMODE_LY00_HBLANK. Это последнее в кадре и довольно странное состояние. Оно длится всего 4 такта и устанавливает в регистре STAT состояние 0. После него все начинается заново.

Все, с циклом состояний покончено. Теперь небольшая, но очень важная деталь. Если вы прочитали описание регистра LCDC, то могли заметить, что бит 7 отвечает за вкл/выкл дисплея. Нам надо как-то отразить это в наших состояниях.

Если игра выключила дисплей, то нам надо обнулить регистр LY, а в регистре STAT установить состояние 0. Если игра включила дисплей (обязательное условие – до этого он должен был быть выключен), то нам надо вернуть контроллер в самое начальное состояние – все счетчики тактов обнулены, LY равен нулю, текущим является состояние 2. Т.е. мы начинаем весь цикл состояний с самого начала. Многие игры отказываются работать без этих манипуляций. Примером такой игры является Bomb Jack. Она отказывается доходить даже до начального меню.

Я не случайно привожу примеры игр. Тестовые ROM'ы это хорошо, но их прохождение не гарантирует корректную работу игр, да и не все они проверяют. Где возможно, я буду приводить название игр, которые рекомендуется проверить и на своем эмуляторе. Bomb Jack проверять обязательно — такие трюки с выключением экрана проделывают многие игры.

LCD-контроллер. Реализация
Наконец мы можем приступить к реализации рассмотренной теории. Как я уже говорил, в нашем эмуляторе мы должны синхронизировать с процессором все остальные компоненты – LCD-контроллер не исключение. Для этого мы заведем функцию в классе Cookieboy::GPU, которая на вход принимает число прошедших тактов. Здесь мы и будет осуществлять все, что связано со сменой состояний.

Во всех компонентах синхронизация будет выглядеть примерное одинаково. Заводим счетчик тактов, к которому будем прибавлять прошедшие такты. Как только значение достигло необходимого, мы вычитаем из него это число (не обнуляем, ведь могло пройти больше тактов, чем нам нужно) и делаем то, что нужно.

Вот как будет выглядеть наша функция синхронизации в Cookieboy::GPU:
void Cookieboy::GPU::Step(DWORD clockDelta, Interrupts &INT)
{
    ClockCounter += clockDelta;

    while (ClockCounter >= ClocksToNextState)
    {
        ClockCounter -= ClocksToNextState;

        if (!LCD_ON())
        {
            LY = 0;
            //очистка экрана
            ClocksToNextState = 70224;
            continue;
        }

        switch (LCDMode)
        {
            case LCDMODE_LY00_VBLANK:
            LY = 0;

            LCDMode = LCDMODE_LY00_HBLANK;
            ClocksToNextState = 452;
            break;
        }
    }
}

Естественно это лишь часть, но ее достаточно. И так, ClockCounter служит счетчиком. Далее у нас следует конструкция while, которая проверяет, достиг ли счетчик нужного значения. Переменная ClocksToNextState служит для хранения числа тактов, которое должно пройти до наступления нового состояния. Их мы и вычитаем из счетчика, чтобы он продолжил отсчитывать уже до следующего состояния.

Зачем здесь while? Это важно. Некоторые состояния длятся всего 4 такта и вполне возможно, что это состояние может наступить сразу же. Т.е. установили мы ClocksToNextState, равной 4, а в нашем счетчике и так 4 такта. Чтобы нам не ждать лишний вызов функции синхронизации, мы обработаем новое событие тут же, на следующей итерации цикла. Данный подход стоит взять за правило там, где интервалы между состояниями (событиями) слишком малы и могут быть меньше длительности одной инструкции процессора.

Далее мы видим условие с макросом LCD_ON(). Здесь мы проверяем, не отключен ли дисплей. Если это так, то мы не проходим через весь цикл состояний. Мы лишь очищаем экран и ждем положенные для одного полного обновления экрана 70224 такта.

Если же дисплей включен, то далее с помощью конструкции switch осуществляем необходимые действия для текущего состояния. Специфика моей реализации такова, что мы храним в LCDMode не столько текущее, сколько следующее состояние. Выполнив необходимые действия, мы устанавливаем в LCDMode следующее состояние и показываем, сколько тактов до него должно пройти. Да, LCDMode лишь вспомогательная переменная, для игр она не существует. Реальные состояния хранятся в регистре STAT, где и должны.

Графика. Теория
DMG оперирует не пикселями, а тайлами. Естественно графика выводится попиксельно, но единицей для программиста служит именно тайл, размер которого составляет 8х8 пикселей. Таким образом, в памяти хранятся не цвета пикселей, а номера тайлов. Эти номера ссылаются на другую область памяти, где находится информация о тайлах – из пикселей какого цвета они состоят. Очевидная цель такого подхода – экономия памяти. Похоже на реализацию индексированных цветов.

Вывод графики осуществляется в три этапа в следующем порядке:
  1. Фон (background)
  2. Так называемое «окно» (window)
  3. Спрайты (sprites)

Background
Фон у DMG имеет размер 32х32 тайла или 256х256 пикселей, что, очевидно, больше экрана DMG. С помощью регистров SCX и SCY мы можем указать, какую часть необходимо вывести на экран, как показано на рисунке ниже.

Как видно, если мы вышли за границы фона, то мы оказываемся на его противоположном конце. Фон затайлен, как часто говорят.

Вся информация о тайлах и их содержании находится в видеопамяти. Вот ее структура:
Секция | Назначение
0x8000-0x87FF | Набор тайлов №1: тайлы [0, 127]
0x8800-0x8FFF | Набор тайлов №1: тайлы [128, 255] Набор тайлов №0: тайлы [-128, -1]
0x9000-0x97FF | Набор тайлов №0: тайлы [0, 127]
0x9800-0x9BFF | Карта тайлов №0
0x9C00-0x9FFF | Карта тайлов №1

Вначале хранятся сами тайлы. DMG может хранить до 384 тайлов, поделенных на два набора по 256 тайлов так, что половина из них у наборов общая. Один набор использует для обозначения тайлов номера от 0 до 255. Другой – номера от -128 до 127. Сам фон рисуется согласно выбранной карте тайлов. Их тоже две. Они имеют размер 1024 байта – по одному байту на номер тайла.

Выбор набора тайлов и карты тайлов осуществляется с помощью регистра LCDC. Там же есть флаг, который вкл/выкл отображение фона. Вот, кстати, его структура:
Биты | Назначение
7 | Управление LCD-контроллером: 0: выкл (экран пуст) 1: вкл
6 | Выбор карты тайлов для «окна»: 0: Карта тайлов №0 (0x9800-0x9BFF) 1: Карта тайлов №1 (0x9C00-0x9FFF)
5 | Флаг отображения «окна»: 0: выкл 1: вкл
4 | Выбор набора тайлов для фона и «окна»: 0: Набор тайлов №0 (0x8800-0x97FF) 1: Набор тайлов №1 (0x8000-0x8FFF)
3 | Выбор карты тайлов для фона: 0: Карта тайлов №0 (0x9800-0x9BFF) 1: Карта тайлов №1 (0x9C00-0x9FFF)
2 | Размер спрайтов: 0: 8х8 1: 8:16
1 | Флаг отображения спрайтов: 0: выкл 1: вкл
0 | Флаг отображения фона: 0: выкл 1: вкл

Сами тайлы занимают в памяти по 16 байт. Каждые 2 байта отвечают за одну строку, таким образом, давая нам тайлы размером 8х8. Сама организация тайлов в памяти довольно странная, как показано ниже:

Цвет пикселя составляется из двух бит, где младший бит берется из первого байта, а старший из второго. В результате получаются индексы цветов, которые могут иметь 4 значения: от 0 до 3. Эти индексы используются для выбора цвета из палитры в регистре BGP. Вот его структура:
Биты | Индекс цвета
7-6 | 3
5-4 | 2
3-2 | 1
1-0 | 0

Т.е., имея цвет пикселя равный 2, мы смотрим в регистре BGP значение битов 5-4, которые и дают нам цвет, который тоже может иметь 4 значения от 0 до 3. Это приводит нас к необходимости иметь еще одну палитру для перевода цветов из палитры DMG в настоящие RGB цвета для последующего вывода. Относится это ко всей графике в целом.

Можно использовать черно-белую палитру, что дает нам следующие цвета:
Цвет в палитре | Значение RGB каналов
0 | 0xFF, 0xFF, 0xFF
1 | 0xAA, 0xAA, 0xAA
2 | 0x55, 0x55, 0x55
3 | 0x00, 0x00, 0x00

Или же использовать цвета, более приближенные к таковым на экране настоящего DMG:
Цвет в палитре | Значение RGB каналов
0 | 0xE1, 0xF7, 0xD1
1 | 0x87, 0xC3, 0x72
2 | 0x33, 0x70, 0x53
3 | 0x09, 0x20, 0x21

Цвет со значением 0 (самый яркий) используется для очистки экрана. Соответственно, если экран надо очистить или фон отключен, то просто заливаем все цветом с индексом 0.

Window
После вывода фона необходимо вывести «окно». Оно выводится практически так же как и фон – из LCDC узнаем, какую карту и набор тайлов использовать. Там же есть флаг вкл/выкл вывода. Выводится оно согласно координатам, указанным в регистрах WY и WX. Но чтобы вывести «окно» в левом верхнем углу экрана необходимо указать координаты WX = 7 и WY = 0. Т.е. координаты X и Y левого верхнего угла «окна» равны WX-7 и WY соответственно.

На рисунке ниже показан пример вывода «окна» при WX=87 и WY=70.

Перед выводом надо проверить не только флаг вывода «окна» в LCDC, но и координаты:
  • если WX больше 166, то «окно» скрыто за пределами экрана;
  • если WY больше 143, то «окно» так же скрыто.

Важная деталь. WX и WY могут быть изменены в процессе вывода. Изменения WX вступят в силу уже при выводе следующей строки, а вот изменения WY вступят в силу только на следующем обновлении экрана.

Как я уже говорил, во многом вывод «окна» идентичен выводу фона, но есть одно серьезное отличие.

Для его вывода используется скрытый указатель текущей строки «окна», который инкрементируется после вывода очередной строки. Если «окно» отключено или скрыто из-за координат WX/WY, то его счетчик строк не инкрементируется. Таким образом, если вывод «окна» был отключен на полпути, то при включении вывода в дальнейшем вывод продолжится с того места, на котором он остановился. Справедливо это в течение одного обновления экрана. В конце V-blank счетчик строк «окна» обнуляется.

Помимо этого, значение счетчика изменяется при модификации LCDC. Если «окно» было выключено, а сейчас включено посредством LCDC, то его вывод начнется только на следующем обновлении экрана с первой строки.

Как минимум одна игра использует данную особенность DMG – Ant Soldiers. Сразу после запуска внизу экрана должны отображаться авторы игры. Если при выводе «окна» не учесть упомянутые особенности, то внизу экрана будет пусто. Но еще хуже то, что игровой интерфейс так же не будет виден, из-за чего нормальная игра уже невозможна.

Sprites
Теперь настал черед спрайтов. Они так же состоят из тайлов, но выводятся совсем по-другому.

Спрайты могут иметь размер 8х8 или 8х16, т.е. один или два тайла, что контролируется флагом в регистре LCDC. Информация о спрайтах находится в области памяти OAM. На один спрайт приходится 4 байта, что позволяет хранить до 40 спрайтов в OAM. Эти байты содержат следующую информацию:
Байт | Назначение
0 | Y координата
1 | X координата
2 | Номер тайла (0-255)
3 | Бит 7: приоритет Бит 6: зеркальное отражение по вертикали, если 1 Бит 5: зеркальное отражение по горизонтали, если 1 Бит 4: если 1, используем палитру OBJ1, иначе – OBJ0

Координаты спрайта указаны для нижнего правого угла. Координаты верхнего левого угла равны X-8 и Y-16. Размер спрайта здесь значения не имеет.

Для номера тайла очень важно учитывать размер спрайта. Если он установлен как 8x16, то необходимо обнулить самый младший бит в номере тайла, иначе вывод графики в некоторых играх будет некорректен.

Если приоритет установлен в 1, то спрайт рисуется как бы за фоном и «окном». Пиксели спрайта рисуются только поверх цветов, которые имеют значение равное нулю. Для этого надо вывести фон и «окно», а затем проверить цвет пикселя, где собираемся выводить спрайт. Спрайт как бы «просвечивается» через пиксели с нулевым цветом. Если же приоритет установлен в 0, то спрайт рисуется поверх фона и «окна».

С отражением все понятно. Палитр для спрайтов две (OBP0 и OBP1), выполняют они туже роль с тем исключением, что индекс цвета 0 (биты 0-1 в палитре) означает прозрачный пиксель и его цвет в палитре не имеет значения.

Перед тем как вывести спрайты, необходимо узнать, какие именно нужно выводить и в каком порядке. У спрайтов существует приоритет, который диктует порядок их вывода на экран. Вычисляется он так – спрайты выводятся в порядке их координат X, от больших к меньшим. Т.е. спрайты с меньшим значением координаты X выводятся поверх тех, что с большим значением X. Если координаты X равны, то приоритет вычисляется согласно порядку следования в OAM – спрайты с меньшим адресом в OAM будут выше.

Чтобы определить, нужно ли выводить спрайт, необходимо проверить координату Y из OAM. Она должна быть таковой, чтобы спрайт попадал на текущую линию:

где LY это текущая строка экрана, а SpriteHeight – высота спрайта (8 или 16). Это очень важная формула – неправильное формирование очереди спрайтов приведет к трудноуловимым багам в некоторых играх (многие игры могут отлично работать, как это было у меня). Сначала мы переносим координату Y – как уже было сказано, координаты спрайтов указывают на их нижний правый угол. Опять же, высота спрайта на конкретно эти вычисления не влияет, а вот для формирования интервала, в котором может лежать значение текущей строки, нам уже надо учитывать высоту спрайта.

Координата X может быть любой, даже если она приведет к тому, что спрайт не будет виден на экране – он все равно попадает в список.

Выводятся спрайты согласно их приоритету, но не более 10 штук на одной строке. Таким образом, невидимые из-за координаты X спрайты попадают в список и тем самым ограничивают возможное число спрайтов на данной строке экрана. Соответственно, при выводе спрайтов на экран нужно быть готовым к тому, что они могут лежать за его пределами.

Что касается ограничения в 10 штук – я так и не нашел достоверной информации о том, как надо учитывать это ограничение. Тут может быть два логичных варианта решения:
  • Формирование очереди (проход по содержимому OAM) из всех видимых согласно координате Y спрайтов – сейчас их может быть больше 10. Далее сортировка и только после этого отбрасывание лишних спрайтов. Логично выкидывать спрайты с меньшим приоритетом.
  • Формирование очереди прекращается, если набрано 10 спрайтов. Таким образом, может оказаться, что далее в OAM есть видимые спрайты с большим приоритетом, но мы до них не дошли.

Я выбрал второй вариант, поскольку другие эмуляторы используют именно его. Тестирование в играх не помогло с решением – видимых багов какой-то из реализаций не наблюдалось.

Графика. Реализация
Вот мы и подошли к еще одному ответственному этапу создания эмулятора. Малейшие ошибки здесь могут привести к ужасным последствиям на экране. Некоторые ошибки я нашел только в процессе написания данных статей. До этого приходилось наблюдать полный хаос на экране в некоторых играх.

Больше всего проблем мне, наверное, доставила игра Gameboy Wars Turbo. Добиться правильной картинки в ней я смог только к моменту написания данного цикла статей. Есть и другие хорошие тестовые игры. Ant Soldiers я уже упоминал. Для тестирования вывода спрайтов хорошо подходит Kirbys Pinball Land — вступительных ролик использует спрайты различного размера.

Как обычно, объявляем все необходимые нам регистры, даем к ним доступ извне (конкретно, для класса Cookieboy::Memory). С видеопамятью и OAM чуть сложнее.

Как помните, ранее было сказано, что эта память доступна не всегда и зависит это от текущего состояния в регистре STAT. Появляется вопрос – реализовывать как надо (блокировать доступ к памяти в нужный момент) или как проще (память доступна всегда). Тут все зависит от того, насколько точна эмуляция переходов из состояния в состояние. Несоблюдение длительности интервалов может привести к тому, что память будет недоступна для чтения и записи тогда, когда должна быть доступна. В моей реализации оказалось возможным блокировать доступ – протестированные мной игры отлично работают в таких условиях. На ранних этапах моего проекта эмуляция LCD-контроллера была скудной (все делал как написано в Gameboy CPU Manual), и блокирование доступа приводило к выводу мусора на экране. Не все эмуляторы это делают, но решать вам.

Так же нам необходим буфер кадра, а можно даже два, как я и сделал. Один буфер кадра состоит из индексов цветов – этот буфер необходим, поскольку те же спрайты требуют проверки цвета под ними для правильного вывода. Второй буфер содержит копию того, что находится в первом буфере, но цвета уже представлены в RGB значениях для дальнейшего вывода на экран.

Для вывода графики нам необходимо завести две функции. Одну для вывода текущей строки (LY) на экран, складывающуюся из фона, «окна» и спрайтов — void Cookieboy::GPU::RenderScanline(). Другую для формирования очереди спрайтов для последующего вывода — void Cookieboy::GPU::PrepareSpriteQueue(). В разделе про логику LCD-контроллера я уже упоминал, куда нужно поместить вызовы этих функций.

Собственно, говорить о реализации этих функций нечего – либо говорить все от и до (так, конечно же, неинтересно), либо ничего не говорить. Тут у вас есть свобода действий – ваша задача лишь получить на экране правильную картинку. Какими путями вы к этому придете уже не важно. По исходному коду различных эмуляторов это заметно – подходы везде совершенно разные, в то время как другие компоненты имеют чуть ли не идентичную реализацию.

Подведем итоги. На данный момент наш эмулятор может исполнять код и даже выводить изображение на экран. Это все хорошо, но мы все еще не можем запускать тестовые ROM’ы для оценки проделанной работы. Большинство тестов подсчитывают время, а для этого нам надо реализовать еще один компонент DMG – таймеры.

Таймеры

Таймеры реализованы в виде двух счетчиков, работающих на определенных частотах.

Первый счетчик, который мы будем называть DIV, работает на частоте 16384 Гц (т.е. отсчет происходит каждые 256 тактов процессора). Как принцип, так и его реализация предельно просты. С указанной частотой происходит инкрементирование значения в регистре DIV. DIV зациклен от 0 до 255. Т.е. если его значение равно 255, то следующий отсчет установит в нем значение 0. Если попытаться записать в DIV что-то самостоятельно, то его значение будет обнулено.

Второй счетчик, TIMA, немного сложнее и имеет больше возможностей. В общей сложности под него отведено три регистра:
  • TIMA хранит текущее значение счетчика. Он так же зациклен, но при превышении 255 поведение совершенно иное, нежели у DIV. В этот момент запрашивается прерывание таймера, а в качестве значения TIMA устанавливается не 0, а значение из регистра TMA.
  • TMA хранит начальное значение счетчика TIMA при превышении 255.
  • TAC является управляющим регистром. Ниже приведена его структура.
Биты | Назначение
2 | Остановка таймера: 0 – таймер остановлен 1 – таймер запущен
0-1 | Частота таймера: 00 – 4096 Гц 01 – 262144 Гц 10 – 65536 Гц 11 – 16384 Гц

Реализация практически такая же, как у DIV, только с учетом упомянутых особенностей. Здесь же стоит вспомнить об использовании while (кстати, здесь можно использовать более быстрый и не менее точный подход без цикла) как это было сделано с LCD-контроллером. При частоте 262144 Гц период составляет всего 16 тактов, а значит может возникнуть ситуация, когда за один раз нам придется сделать несколько отсчетов. Если счетчик будет равен двум и более периодам, то простое условие приведет к накоплению ошибки, из-за чего таймер будет работать неверно. На начальных этапах у меня были подобные проблемы.

И так, на данный момент у нас все готово для тестирования нашего эмулятора с помощью тестовых ROM’ов. Перед этим все же коснемся еще одной темы – управления.

Управление

Как можно видеть на фотографии, DMG имеет 8 кнопок: 4-направленная крестовина, кнопки A и B, кнопки Start и Select. Чтобы игра могла узнать, какие кнопки нажаты в данный момент, DMG записывает пользовательский ввод в регистр P1. Вот его структура:
Бит | Назначение
5 | P15
4 | P14
3 | P13
2 | P12
1 | P11
0 | P10

Смотря на таблицу, мы можем заметить, что битов на все кнопки здесь не хватает. В действительности, состояния кнопок записываются только в P13, P12, P11, P10. Биты P15 и P14 модифицируются ROM’ом игры для того, чтобы выбрать, состояние какой четверки клавиш мы хотим считывать через регистр P1. Вот таблица соответствия:
P15 | P14 | P13 | P12 | P11 | P10
1 | 0 | Вниз | Вверх | Влево | Вправо
0 | 1 | Start | Select | B | A

В этом регистре бит со значением 0 означает, что клавиша нажата. Если клавиша нажата, то запрашивается соответствующее прерывание. Именно это прерывание выводит DMG из состояния останова вследствие исполнения инструкции STOP. В таком состоянии основные компоненты не работают, и прерывания не запрашиваются. Только нажатие на кнопку может запросить его. В моем эмуляторе инструкция STOP игнорируется — игры работают нормально, а тестовые ROM'ы ее не проверяют.

Реализация сводится к опрашиванию состояния клавиш, которые эмулируют кнопки DMG, и установке соответствующих битов согласно значениям в P15 и P14. Но есть и свои нюансы.

Опрашивать физические клавиши (клавиатура, геймпад, не важно) каждый раз, скорее всего, не получится – производительность будет хуже некуда. Собственно, этого и не требуется. Лучше завести счетчик тактов и проверять состояние клавиш только по достижении им определенного значения. Какое? Решайте сами, я использовал длительность полного обновления экрана – 70224 такта. Этого достаточно для комфортной игры. Пока счетчик отмеряет время до следующего опроса клавиш, мы устанавливаем биты регистра P1 согласно полученным ранее состояниям клавиш.

Сводим все воедино

Последний штрих – поддержание необходимого темпа работы эмулятора. Поскольку одно полное обновление экрана длится 70224 такта, а частота процессора равна 4194304 Гц, DMG обновляет экран с частотой примерно 59.73 Гц, что для нас будет равно 60 Гц.

В первую очередь нам нужна индикация того, что эмулятор закончил обновление экрана или отработал 70224 такта. Далее заводим цикл обновления, на каждый итерации которого наш эмулятор должен завершить одно обновление экрана. Параллельно с этим мы должны замерить время, которое уйдет у ПК на эмуляцию этого обновления экрана. Скорее всего, оно будет довольно маленьким и нам придется «усыпыть» приложение на некоторые время, чтобы в итоге одна итерация цикла заняла 1000/60 мс. Не забываем только вовремя опрашивать клавиатуру.

Именно здесь можно реализовать популярную в эмуляторах ускоренную эмуляцию, когда мы позволяем эмулятору работать на максимально возможной скорости.

И так, что мы имеем. На данный момент наш эмулятор содержит все функции, необходимые для запуска игр. Вся остальная функциональность является необязательной, но будет рассмотрена позже. Сейчас нам важно проверить корректность эмуляции с помощью тестовых ROM’ов.

Тестирование

Для DMG существует набор тестовых ROM'ов, которые проверяют различные компоненты DMG: процессор, память, звук, LCD. Для прохождения теста необходимо просто запустить ROM в своем эмуляторе. Результаты теста в наиболее удобном виде выводятся на экране. На данный момент мы можем провести тест на корректность работы инструкций процессора, тест длительности инструкций процессора, тест синхронизации процессора и памяти, тест LCD.

Вот ссылка на оригиналы тестов и еще одна на всякий случай, мало ли что случится с сайтом автора, тесты очень и очень полезные. Помимо самих тестов по ссылкам можно найти их исходный код. Крайне рекомендую разобраться в ассемблере — понимание того, как и что тестируется, очень сильно поможет в исправлении ошибок. Сейчас это может быть не так важно, но в тестах звука без понимания исходного кода исправить ошибку иной раз будет попросту невозможно.

Тест процессора проверяет результат и флаги после исполнения каждой инструкции. В случае ошибки на экране будет выведен опкод неправильной инструкции. Данный пакет тестов состоит из 11 тестов различных групп инструкций. Именно отдельные тесты покажут, какие конкретно инструкций неправильно реализованы. Скорее всего, на данном этапе у вас будет достаточно много ошибок, в основном связанных с флагами. Больше всего проблем мне лично доставили POP AF, DAA и HALT. Правильную реализацию POP AF я уже упомянул – нет смысла наступать на те же грабли еще раз и вам, ничего кроме потери времени и интереса это не принесет. Мое негодование насчет настолько тривиальной инструкции не имело пределов, а всего-то надо было обнулить неиспользуемые биты в регистре F. DAA сложна сама по себе и вполне возможно будет проще и надежнее взять чью-то реализацию. HALT я так же упомянул во всех подробностях. Слепое следование CPU Manual привело к еще одной череде негодований…

Естественно не стоит полностью полагаться на тест – сам тестовый ROM может работать неправильно из-за неправильно реализованных инструкций или их длительности. Стоит сразу озаботиться о как можно более точной реализации всех аспектов DMG, чтобы с помощью тестов уже выявить только то, что вы не заметили или авторы забыли указать в документации.

Особенно это важно для тестов, связанных с тактами. Тест длительности инструкций процессора проверяет длительность каждой инструкции в тактах, в том числе, учитывая разницу в длительности у инструкций условных переходов. В Readme этого теста есть таблица с тактами.

Вполне возможно, что тест при запуске выдаст ошибку 255. Этой ошибки нет в исходном коде теста, отчего я вернулся к уже привычному для меня состоянию негодования, но после более тщательного анализа его кода стала понятна причина – тестовый ROM перед запуском проводит некоторые проверки и устанавливает таймеры для дальнейших измерений. Именно эти внутренние проверки не проходят. Для этого пришлось вооружиться таблицами из Readme, после чего тест успешно заработал и показал, где у меня еще закрались ошибки. В инструкциях условных переходов и было дело, практически все остальное было верно.

Тест синхронизации памяти и процессора проверяет состояние памяти в течение процесса исполнения инструкции процессора. Если вы вызываете функции синхронизации только после исполнения инструкции, то тест будет провален. Я уже упоминал о том, как сделать правильно, поэтому пройти этот тест уже не составляет особого труда. Если что, все необходимые данные тест выведет на экране. Если интересно как он работает, то все элементарно. Все операции производятся на счетчике таймера, установленного на максимальной частоте. При правильной синхронизации таймера с исполнением тестовой инструкции получится, что в процессе (именно в процессе) исполнения счетчик должен увеличиться. После исполнения эти значения и проверяются.

Наконец, тест LCD. Он проверяет, насколько точно соблюдается длительность состояний LCD-контроллера, и когда происходят различные события (например, увеличение счетчика строк LY). Без прохождения этого теста не стоит даже и задумываться о том, чтобы эмулировать блокировку видеопамяти и OAM в различных состояниях. Так же он проверит корректность вкл/выкл дисплея посредством 7 бита в регистре LCDC. Я уже упоминал, что манипуляции ROM’а с этим битом требуют определенных действий с нашей стороны.

Заключение

Могу вас поздравить — наш гипотетический эмулятор готов. Он содержит все критически важные функции, остальное реализовывать совсем не обязательно — игры будут прекрасно работать и сейчас.

Играть можно, но без звука эмулятор все же выглядит неполноценным. Именно о звуке и пойдет речь в следующей статье. Тема эта большая, относительно сложная с кучей подводных камней, но очень и очень интересная.
  • эмулятор
  • , gameboy
  • , DMG
  • , C++
+32
7087
118
creker 18,5
Asus Fonepad 8
Отличный подарок к Новому Году!
Похожие публикации
Конференция C++ Russia в Москве сегодня в 10:29
Об удобной навигации и отладке C++ кода в Vim 13 декабря в 10:20
Чтение SVG в C++ 10 декабря в 18:27
Сводная таблица по поддержке C++ 11/14/17 7 декабря в 08:26
Рефакторинг C++ кода в Visual Studio 2015 18 ноября в 11:29
Идеальная передача и универсальные ссылки в C++ 8 ноября в 23:37
Интервью Скотта Мейерса в Яндексе. О настоящем и будущем C++ 28 октября в 15:08
Библиотека Strutext обработки текстов на языке C++ 27 октября в 16:18
Пишем эмулятор Gameboy, часть 3 30 октября 2012 в 08:27
Пишем эмулятор Gameboy, часть 1 16 октября 2012 в 12:00

Комментарии (8)

0
sergeisirik 22 октября 2012 в 14:27 #
А ссылочки на собранный нету? или просто я не вижу?
0
sergeisirik 22 октября 2012 в 14:28 #
↵
↑
Или в предыдущей статье ужё всё включено в сборку?
0
sergeisirik 22 октября 2012 в 14:31 #
↵
↑
Запустил бинарничек по ссылочке из предыдущей статьи, сразу опенфайл дайлог выскакивает, не могли бы выложить для примера нужный файлик?
+1
creker 22 октября 2012 в 14:41 #
↵
↑
Эмулятор при запуске просит ROM файл. Мне не очень хочется включать нелегальные ROM файлы игр в архив с эмулятором или давать ссылки на них здесь. При желании все находится в два счета.
–4
Derailed 22 октября 2012 в 18:48 #
↵
↑
Не находится на самом деле. Далеко не в два, и не в три, и даже не в четыре счета. Не нашел. Можно если не ссылки, то хотя бы список ключевых слов для поиска ROM-ов?
0
creker 22 октября 2012 в 14:37 #
↵
↑
Да, в предыдущей статье ссылка на готовый эмулятор. В репозитории Cookieboy лежат исходники и сборка готового эмулятора. Ссылку на репозиторий я продублировал в этой статье в самом начале.
0
sergeisirik 22 октября 2012 в 15:00 #
↵
↑
Спасибо!
Запустил, даже немного поиграл.
0
readmaxym 23 октября 2012 в 01:38 #
хорошая статья,

особенно понравилось
Он содержит все критически важные функции, остальное реализовывать совсем не обязательно — игры будут прекрасно работать и сейчас.

в принципе правильно,
если игры не используют недокументированные фичи или баги железа

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Метки лучше разделять запятой. Например: общение, социальные сети, myspace.com, подростки, мердок
или закрыть
Популярное за сутки
Светильник декоративный бытовой СДБ-З «Евлампия»
Бизнес не по-детски — Или как восьмиклассник развозил фрукты?
Бесплатная отправка SMS из Arduino без GSM модуля
Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №138 (8 — 14 декабря 2014)
Как сделать греческие буквы в формулах прямым шрифтом в LaTeX
Конференция C++ Russia в Москве
Конференция ZeroNights 2014 — как все было
Обзор наиболее интересных материалов по анализу данных и машинному обучению №26 (8 — 14 декабря 2014)
Сказ о том, как мы в хакатонах участвовали, или Страх и Ненависть в Кремниевой Долине
Публичный тест нового интерфейса HeadHunter
Как добавить карты Bing Maps в Windows-приложение на HTML и JavaScript. Часть 2
Электрофокусер на базе отладочной платы Arduino Uno, часть 3
Replication slots в PostgreSQL
Composer. Небезопасно использовать packagist и приватный источник пакетов одновременно
5 ошибок при обратной связи с пользователями
Моделируем преднапряжение бетонной оболочки АЭС
Обзор возможностей App Annie для Windows и Windows Phone разработчиков
Диагностика проблем после инсталляции или апгрейда ORACLE APEX
Concurrency структуры в .net. ConcurrentQueue изнутри
MyLifeOrganized: папки vs контексты
все лучшие
Лучшее на Geektimes
Нефтерубль
Новогодние космические подарки
Роскосмос планирует построить лунную базу, наладить промышленность и добычу ископаемых
В погоне за синим светодиодом
Сбывшиеся мечты Роберта Бойля. Часть 1
На космическом корабле Orion используется процессор 12-летней давности
Паровые машины… и как появились «лошадиные силы»
Вымрет ли человечество «тепловой смертью»?
Samsung и Apple опять повышают цены в России
Yahoo призывает переходить на Firefox
все посты
Вопросы по теме
Почему не корректно работает make.exe?
Linking GTK-3.0 C++ app with MinGW: undefined reference to `WinMain@16'?
Почему c++ программ не видит файл при запуске вне xcode?
Как работать с кириллицей в c++?
Как рационально сосчитать парники (задача об огурцах)?
Как перекинуть указатель в glfwSetKeyCallback на си?
Можно ли выяснить, содержит ли указатель адрес допустимого объекта или нет?
Как создать два процесса,в котором один отсылает события, а другой распознает их и обрабатывает?
Как в С++ генерировать последовательность чисел без повторов?
Как сгенерировать уникальный число int64 ?
Какие функции использовать чтобы работать с элементами чужого окна?
Как послать emal через c++ ?
На чем написать программу для работы с чужим окном?
Как определить первый и последний сегмент в сырых TCP пакетах при IP фрагментации?
Как лучше всего создать свой красивый tray notification?
Инструмент для тестирования программной комбинационной схемы (алгоритма)?
С++ проект под Android. С чего начать?
Как передать указатель на порт Serial в библиотеку Arduino и ОДНОКРАТНО создать на него ссылку внутри всей библиотеки?
Что лучше выбрать для разработок?
Что не так с моим кодом?
Что обсуждают?
Установка, настройка и тестирование Fedora 21 Workstation на личном или игровом компьютере 42
Публичный тест нового интерфейса HeadHunter 47
Бизнес не по-детски — Или как восьмиклассник развозил фрукты? 13
Пару слов об интеграции в TFS системы управления дефектами с системой управления версиями 144
Об удобной навигации и отладке C++ кода в Vim 80
Если заказчик — редакция 17
Установка BIND9 DNS на CentOS 1
Google Chrome пометит HTTP-сайты как небезопасные 150
Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №138 (8 — 14 декабря 2014) 2
Конференция C++ Russia в Москве 3
все публикации
Компания дня
КРОК

Последняя публикация: Как мы монтируем ДДИБП: огромные маховики в дата-центрах и средство аварийного резерва ответственных объектов

2019 подписчиков
Как выглядит рабочий день IT-инженера, если ты девушка
Как мы озвучивали стадион «Спартака»
Гарри Каспаров проиграл суперкомпьютеру Deep Blue в шахматы из-за компьютерного сбоя
Brainstorage
Android Developer
Python разработчик
PHP - программист
Web Designer
PHP developer
JavaScript разработчик (Middle level)
Junior Web Developer (JavaScript, Node.js)
PHP программист
Ведущий веб-разработчик
Ruby on Rails разработчик для постоянных случайных рабочих связей
все вакансии
Nodejs/C++ developer, разработка модуля
Доделать сайт на open card
Добавить пользовательское поле в корпортал Битрикс
Выполнить интеграцию двух страниц на Yii в существующий проект
Поиск дыр в безопасности на WP
Печать бирок для одежды (Москва)
Добавить боковое меню в корпоративный портал Битрикс
Андроид-разработка (серверный менеджер задач)
Дизайн Landing'a в FLAT
Создание страницы продукта по аналитике (дизайн+верстка)
все заказы
Войти
Регистрация
Разделы
Публикации
Хабы
Компании
Пользователи
Q&A
Песочница
Инфо
О сайте
Правила
Помощь
Соглашение
Услуги
Реклама
Тарифы
Семинары
Спецпроекты
Разное
Приложения
Тест-драйвы
Помощь стартапам
TM © 2006–2014

Служба поддержки

Мобильная версия


Пишем эмулятор Gameboy, часть 1 / Хабрахабр

Разделы
Публикации Хабы Компании Пользователи Q&A Песочница Биржа авторов
Спецпроекты
Конкурс приложений Биржа статей Дарите улыбки
16 октября 2012 в 12:00

Пишем эмулятор Gameboy, часть 1 из песочницы tutorial

Разработка *, Программирование *
Здравствуйте!

Не так давно на Хабре появилась статья о создании эмулятора chip-8, благодаря которой удалось хотя бы поверхностно понять, как пишутся эмуляторы. После реализации своего эмулятора появилось желание пойти дальше. Выбор пал на оригинальный Gameboy. Как оказалось, выбор был идеальным для ситуации, когда хочется реализовать что-то более серьезное, а опыт разработки эмуляторов практически отсутствует.

С точки зрения эмуляции Gameboy относительно прост, но даже он требует изучения достаточно большого объема информации. По этой причине разработке эмулятора Gameboy будет посвящено несколько статей. Конечным итогом будет эмулятор с неплохой совместимостью, поддержкой практически всех функций оригинала, в том числе звука, который нередко отсутствует в других эмуляторах. В качестве бонуса наш эмулятор будет проходить практически все тестовые ROM’ы, но об этом позже.

Данные статьи не будут содержать исчерпывающего описания реализации эмулятора. Это слишком объемно, да и весь интерес от реализации пропадает. До конкретного кода будет доходить лишь в редких случаях. Перед собой я ставил задачу дать в большей степени теоретическое описание с небольшими намеками на реализацию, что, в идеале, должно позволить вам без особых затруднений написать свой эмулятор и в тоже время чувствовать, что вы написали его самостоятельно. Где надо я буду ссылаться на собственную реализацию – при необходимости вы сможете найти нужный код без того, чтобы продираться через тонны строк кода.

В данной статье мы познакомимся с Gameboy и начнем с эмуляции его процессора и памяти.

Пишем эмулятор Gameboy, часть 1
Пишем эмулятор Gameboy, часть 2
Пишем эмулятор Gameboy, часть 3

Оглавление

Введение
Архитектура
Процессор
Прерывания
Память
Заключение

Введение

Gameboy – портативная консоль Nintendo, выпуск которой начался в 1989 году. Речь пойдет именно об оригинальном черно-белом Gameboy. Стоит заметить, что в различных документах, которыми мы будем руководствоваться, используется кодовое название Gameboy – DMG(Dot Matrix Game). Далее я буду использовать именно его.

Перед тем как приступить, необходимо ознакомиться с техническими характеристиками DMG:

Процессор | 8-битный Sharp LR35902 работающий на частоте 4.19 МГц
Оперативная память | 8 Кбайт
Видеопамять | 8 Кбайт
Разрешение экрана | 160x144
Частота вертикальной развертки | 59.73 Гц
Звук | 4 канала, стерео звук

Ознакомившись с подопытным, следующим шагом является документация. Объемы необходимой информации не позволяют разместить в статье абсолютно все, поэтому необходимо заранее вооружиться документацией.

Для DMG существует замечательный документ под названием Gameboy CPU Manual. Он включает в себя несколько известных документов от именитых разработчиков и содержит практически всю необходимую нам информацию. Естественно это не все, но на данном этапе этого более чем достаточно.

Сразу предупреждаю, что в документах будут ошибки, даже в официальных. В течение данного цикла статей я постараюсь упомянуть все недочеты различных документов какие смог найти (вспомнить). Так же постараюсь восполнить многие пробелы. Суть в том, что для DMG не существует исчерпывающего описания. Доступные материалы дают лишь поверхностное представление о работе многих узлов консоли. Если программист не в курсе таких «подводных камней», то разработка эмулятора станет намного сложнее, чем она могла бы быть. DMG достаточно прост, когда на руках достоверная и подробная информация. И проблема в том, что многие важные детали можно почерпнуть только из исходного кода других эмуляторов, что, тем не менее, не делает нашу задачу проще. Код известных эмуляторов или излишне сложен (Gambatte), или представляет собой жуткое нагромождение, кхм, низкокачественного кода (Visual Boy Advance – смотреть без слез на его код невозможно).

Поскольку статьи написаны с оглядкой на мой эмулятор, то вот сразу ссылка на исходники и бинарник CookieBoy.

Архитектура

Начнем с архитектуры будущего эмулятора. Для эмуляции DMG нам придется реализовать множество модулей, которые практически независимы друг от друга. В подобных условиях было бы глупо идти напролом, складывая все в одну кучу (что нередко наблюдается в других эмуляторах. Привет VBA). Более элегантным решением является реализация отдельных частей DMG как отдельных классов, эмулирующих свои части железа.

Я говорю это не просто так – именно с нагромождения всех компонентов в один суперкласс я и начинал разработку эмулятора. Вскоре стало очевидно, что дальше дело пойдет намного проще, если каждый будет выполнять только то, что должен. Хотя стоит признать, что в таком подходе есть и очевидная сложность. Нужно иметь достаточно неплохое понимание внутреннего устройства DMG, чтобы правильно разграничить ответственность классов.

Итак, приступим.

Процессор

DMG содержит 8-битный процессор Sharp LR35902, работающий на частоте 4194304 Гц (не стоит удивляться такой точности – это число понадобится нам в будущем). Можно считать его упрощенной версией процессора Zilog Z80, который, в свою очередь, основан на Intel 8080. По сравнению с Z80 отсутствуют некоторые регистры и наборы инструкций.

Процессор содержит восемь 8-битных регистров A, B, C, D, E, F, H, L и два 16-битных регистра специального назначения — PC и SP. Некоторые инструкции позволяют объединять 8-битные регистры и использовать их как 16-битные регистры, а именно AF, BC, DE, HL. Например, регистр BC представляет собой «склеенные» регистры B и C, где регистр C исполняет роль младшего байта, а B – старшего.
Регистры A, B, C, D, E, H, L являются регистрами общего назначения. Регистр A так же является и аккумулятором. Регистр F содержит флаги процессора и напрямую недоступен. Ниже приведена схема регистра. Биты от 0 до 3 не используются.
Бит | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
Флаг | Z | N | H | C | 0 | 0 | 0 | 0

Назначение флагов:
  • Zero Flag (Z) – флаг установлен (бит равен 1), если результат последней математической операции равен нулю или два операнда оказались равными при сравнении.
  • Substract Flag (N) – флаг установлен, если последней операцией было вычитание.
  • Half Carry Flag (H) – флаг установлен, если в результате последней математической операции произошел перенос из младшего полу-байта.
  • Carry Flag (С) – флаг установлен, если в результате последней математической операции произошел перенос.

Регистр PC (program counter), как несложно догадаться, является счетчиком инструкций и содержит адрес следующей инструкции.

Регистр SP (stack pointer), соответственно, является указателем на вершину стека. Для тех, кто не в курсе, стек это область памяти, в которую записываются значения переменных, адреса возврата и прочее. SP содержит адрес вершины стека – стек растет вниз, от старших адресов к младшим. Для него всегда существует как минимум две операции. PUSH позволяет вставить некое значение – сначала регистр SP уменьшается, а затем происходит вставка нового значения. POP позволяет извлечь значение – сначала по адресу SP значение извлекается из памяти, а затем SP увеличивается.

Так же процессор содержит так называемый IME (interrupt master enable) – флаг, который разрешает обработку прерываний. Принимает, соответственно, два значения – запретить (0) и разрешить (1).

С теорией все, можно приступать к реализации. Поскольку нам придется работать как с 8-битными регистрами, так и с их 16-битными парами, то целесообразно реализовать механизм, который позволяет иметь одновременный доступ и к тем, и к тем без необходимости использовать битовые операции. Для этого объявим следующий тип:

union WordRegister
{
    struct
    {
        BYTE L;
        BYTE H;
    } bytes;
    WORD word;
};

Регистры процессора будем хранить как пары, а к отдельным частям будем иметь доступ благодаря объединению WordRegister. Поле «word» даст доступ ко всему 16-битному регистру. Поле «bytes» дает доступ к отдельным регистрам в паре. Единственное, регистры А и F стоит хранить отдельно. Регистр А является аккумулятором, а значит используется очень часто. Похожая ситуация с регистром F – флаги процессора приходится устанавливать довольно часто.

Теперь приступим к реализации собственно процессора – за это будет отвечать класс Cookieboy::CPU. Чтение и исполнение инструкций будет реализовано по обычной схеме – чтение опкода из памяти, а затем декодирование и исполнение посредством конструкции switch:

BYTE opcode = MMC.Read(PC);
PC++;

switch (opcode)
{
case 0x00:
    break;
}

Все опкоды имеют длину 1 байт, но некоторые инструкции используют так называемый префикс – первым байтом идет префикс набора инструкций (для нас единственный префикс это 0xCB), вторым байтом идет собственно опкод из этого набора. Реализация элементарная – как только мы наткнулись на 0xCB, то читаем еще один байт и декодируем его вложенным switch.

Данный код помещается в функцию void Step(), которая за один вызов исполняет одну инструкцию процессора и производит другие необходимые операции.

Естественно для чтения и записи в память нам понадобится другой класс – Cookieboy::Memory, объект которого можно видеть выше под именем «MMC». На данном этапе достаточно заглушки с основными методами:

class Memory
{
public:
    void Write(WORD addr, BYTE value);
    BYTE Read(WORD addr);
};

Процессор DMG имеет достаточно большое число инструкций, список которых можно найти в Gameboy CPU Manual. Там же указано, какие флаги процессора необходимо устанавливать и сколько тактов занимает исполнение каждой инструкции. ОЧЕНЬ внимательно читайте описание флагов – неправильно реализованная установка флагов нередко приводит к неработоспособности игр, а отладка превращается в пытку. Но спешу немного успокоить – существует тестовые ROM’ы для флагов процессора, но до исполнения ROM’ов нам еще далеко.

К слову о тактах. Если chip-8 был достаточно прост, и его эмуляция не требовала учета длительности исполнения инструкций, то c DMG дело обстоит иначе. Компоненты консоли работают не абы как, а синхронизированы с помощью генератора тактовой частоты. Для нас это означает то, что нам необходимо синхронизировать работу всех компонентов нашего эмулятора с процессором.

Решить эту проблему достаточно просто. Процессор является центральным звеном нашего эмулятора. Исполнив инструкцию, мы передаем другим компонентам затраченное процессором время в тактах для синхронизации всех компонентов между собой. Для этого я используют макрос SYNC_WITH_CPU(clockDelta), в который и передается затраченное процессором время на исполнение инструкции. Он уже вызывает функции синхронизации остальных компонентов эмулятора. Решение проблемы синхронизации можно было бы легко вынести за пределы класса процессора, если бы не одно но.

Компоненты консоли работают одновременно, никто не ждет, пока процессор закончит исполнение инструкции, как это делаем мы. Некоторые инструкции требует длительного времени на исполнение и в процессе происходит чтение и запись данных в память. Процессор, как можно догадаться, тратит определенное время на чтение/запись в память (4 такта). Это приводит к тому, что в процессе исполнения содержимое памяти может измениться, что, естественно, неплохо было бы тоже эмулировать.

В этом случае требуется несколько раз использовать макрос синхронизации в процессе исполнения, чтобы в памяти находились правильные данные в момент их чтения или записи. Большинство инструкций не требует такой точной синхронизации, и позволяют производить ее после исполнения. Другие же требуют точной последовательности функций синхронизации и операций чтения/записи в память.

Правильнее и красивее сделать все же по-другому. Мы точно знаем, что каждая операция записи или чтения из памяти одного байта занимает 4 такта. Достаточно добавить вспомогательные функции чтения и записи, которые сами вызывают функции синхронизации. Как только это будет сделано, большинство инструкций сразу обретет правильную длительность, ведь в действительности их время исполнения складывается как раз из операций чтения и записи. Получение опкода команды тоже сюда относится. Именно так я поступил в своем эмуляторе, что практически полностью освободило меня от ручной синхронизации и подсчета тактов. Лишь некоторые инструкции потребовали моего вмешательства.

А теперь немного отвлечемся, чтобы прояснить ситуацию с тактами. В различной документации наблюдается путаница. Некоторые документы пишут такие числа, что, например, NOP имеет длительность 4 такта, другие – 1 такт (так, например, написано в официальной документации Nintendo). Для понимания причины стоит немного отвлечься на теорию.

Любая инструкция процессора имеет определенную длительность, которую назовем машинный цикл. За один машинный цикл процессор может произвести одно действие от и до, как например чтение опкода, его декодирование и исполнение команды; чтение или запись значения в памяти. В свою очередь, машинный цикл состоит из машинных тактов, поскольку за один машинный цикл процессор может совершить несколько операций. И вот мы приходим к нашему процессору. Если мы говорим, что NOP длится 4 такта, то мы говорим о машинных тактах. Если мы говорим об 1 такте для NOP, то мы говорим о машинных циклах. Процессор DMG именно так и работает – его машинный цикл длится 4 машинных такта и многие инструкции имеют длительность ровно 4 такта или 1 машинный цикл – процессор DMG способен прочитать опкод из памяти, декодировать его и исполнить инструкцию всего за 4 машинных такта.

Здесь и далее я буду использовать более привычные машинные такты. Именно они соответствуют одному периоду тактового генератора, а значит, являются минимальной и неделимой единицей времени для нашего эмулятора. Таким образом, операция NOP будет длиться 4 такта.

На данном этапе уже можно полностью реализовать эмуляцию всех команд процессора. Отдельно стоит упомянуть некоторые из них:

  • HALT имеет достаточно интересное поведение, которое описано в CPU Manual (2.7.3. Low-Power Mode). Решение в лоб приведет к тому, что тест инструкции HALT не будет пройден. Здесь нужно быть внимательным как в реализации самой инструкции, так и в реализации обработчика прерываний (об этом далее). Реализация инструкции такова, что она не приостанавливает исполнение и приводит к упомянутому багу только в том случае, если IME равен нулю и в данный момент нет прерываний, которые необходимо обработать (об этом так же далее) – именно последний момент опущен в большинстве документов. В противном случае бага нет, и исполнение приостанавливается. Естественно, тактовый генератор и все остальные компоненты продолжают свою работу, а значит надо продолжать вызывать функции синхронизации, давая в качестве аргумента 4 такта (нет смысла считать в этом режиме по одному такту). Как будто процессор исполняет NOP.
  • В POP AF стоит учесть тот факт, что в регистре F есть неиспользуемые биты. Для этого необходимо обнулить младшие 4 бита регистра F после того, как его содержимое будет извлечено из стека.
  • Инструкции RLCA, RLA, RRCA, RRA всегда обнуляют флаг Z в регистре F.

Помимо этих недочетов есть и другие. CPU Manual содержит неполное описание длительности инструкций. Как можно догадаться, инструкции условных переходов должны иметь разную длительность в зависимости от того, произошел переход или нет. Можно было бы воспользоваться тестовыми ROM’ми, но они неправильно работают сами по себе из-за этих инструкций, поэтому выводят неизвестную ошибку даже не начав тестирование. Вот таблица этих инструкций с указанием их длительности:

Опкоды | Переход не произошел | Переход произошел
0xC2, 0xCA, 0xD2, 0xDA | 12 | 16
0x20, 0x28, 0x30, 0x38 | 8 | 12
0xC4, 0xCC, 0xD4, 0xDC | 12 | 24
0xC0, 0xC8, 0xD0, 0xD8 | 8 | 20

Так же для инструкций RST n (опкоды 0xC7, 0xCF, 0xD7, 0xDF, 0xE7, 0xEF, 0xF7, 0xFF) указана неправильная длительность. Правильное значение равно 16 тактам.

И так, на данный момент наш «процессор» способен читать инструкции из памяти, исполнять их и синхронизировать другие компоненты с собой (как бы синхронизирует, пока все это функции-пустышки). После этого нам необходимо проверить, не произошло ли прерывание после всей проделанной работы.

Прерывания

Прерывание – это событие, которое приостанавливает исполнение текущих инструкций процессора и передает управление обработчику прерывания. DMG работает именно по такому принципу.

В ходе синхронизации мы вызываем методы синхронизации других компонентов эмулятора, которые могут запросить прерывание. В DMG это осуществляется следующим образом. Существует два регистра (где они находятся будет рассмотрено далее) – IF (interrupt flags) и IE (interrupt enable). Их биты имеют определенное назначение, которое идентично в обоих регистрах:

Бит | Прерывание
4 | Joypad
3 | Serial I/O transfer complete
2 | Timer overflow
1 | LCDC
0 | V-Blank

Биты регистра IF показывают, какие прерывания были запрошены. Если бит установлен, то прерывание запрошено.

Биты регистра IE разрешают обработку прерываний. Если бит установлен в единицу и соответствующее прерывание было запрошено, то оно будет обработано. Если нет, то прерывание обработано не будет.

Как видно, идентичное назначение битов очень кстати и позволяет с помощью логической операции И узнать, какие прерывания следует обработать.

Одна важная деталь состоит в том, что прерывание выводит процессор из состояния останова, произошедшее в результате исполнения HALT или STOP. И здесь очень важен алгоритм, по которому проверяются регистры прерываний. Алгоритм таков:
  1. Проверяем, есть ли вообще прерывания, которые стоит обработать. Делается это с помощью операции логического И между регистрами IE и IF. Дополнительно стоит произвести операцию логического И с результатом и числом 0x1F для удаления возможного мусора, поскольку старшие три бита не используются в обоих регистрах.
  2. Если таких прерываний нет, то выходим из функции. Если же они есть, то именно сейчас мы должны вывести процессор из состояния останова.
  3. Теперь мы приступаем к обработке прерываний. Для этого мы проверяем, не запрещает ли флаг IME их обработку. Если нет, то:
    1. обнуляем IME;
    2. загружаем регистр PC в стек;
    3. вызываем обработчик прерывания путем установки регистра PC равному адресу обработчика в памяти;
    4. обнуляем бит регистра IF соответственно обработанному прерыванию.

Прерывания обрабатываются по одному за раз и в строго определенном порядке. Вся информация о приоритетах и адресах обработчиков указана в CPU Manual.

Важная деталь. Опять же, кому-то уже могла прийти в голову мысль – обработка прерывания очень похожа на вызов процедуры, а значит должна занимать какое-то время. Это действительно так и занимает она 20 тактов. По какой-то причине этот момент опущен в документах, описывающих DMG.

Теперь приступаем к реализации. Прерываниями у нас будет заниматься класс Cookieboy::Interrupts. В него мы помещаем регистры IE и IF и объявляем функции для доступа к этим регистрам (они понадобятся нам позже), а так же функцию, которая позволяет запросить определенное прерывание (нам же не хочется каждый раз манипулировать битами, чтобы запросить какое-то прерывание). Так же нам понадобится функция, которая будет проверять, какие прерывания стоит обработать. Помещаем вызов этой функции в конце функции Step процессора и дополнительно синхронизируем компоненты.

Немного про запрос прерываний. Он осуществляется установкой в регистре IF соответствующих битов. Перед установкой проверка регистра IE не требуется. Даже если биты в нем запрещают конкретное прерывание, мы все равно устанавливается биты в IF регистре для этого прерывания.

Если вы смотрели исходный код моей реализации Cookieboy::Interrupts, то могли заметить, что я возвращаю значение регистров IE и IF после того, как установлю в единицу все неиспользуемые в них биты (операция ИЛИ со значением 0xE0). Делаю я это не просто так. Многие регистры в I/O ports (об этом чуть ниже) используют не все биты, другие ограничивают доступ на чтение к некоторым битам или ко всему регистру сразу. Это так же необходимо учитывать – для этого неиспользуемые и запрещенные для чтения биты стоит установить в 1 перед возвращением.

Подведем итоги. Наш эмулятор способен исполнять инструкции процессора, осуществляет синхронизацию всех компонентов эмулятора между собой, обрабатывает прерывания. Правда пока это все лишь на словах. Для получения действительно работающего эмулятора нам необходимо эмулировать память DMG.

Память

Заранее определимся с одним термином – банк памяти. Под этим понимается область памяти строго определенного размера. Существует два вида банков – ROM банки, имеющие длину 0x4000 байт, и RAM банки, имеющие длину 0x2000 (стоит сразу привыкнуть к 16-ричной системе счисления, так будет проще и мне, и вам). Зачем это нужно? Процессор DMG способен работать с 16-битным адресами, а значит адресное пространство ограничено 0x10000 байтам. Из них только 0x8000 байт отведено на образ игры. В большинстве случаев этого недостаточно и в ход вступают банки памяти.

Обращаясь по адресам 0x4000-0x7FFF, без банков памяти мы бы попали именно по этому адресу в образе игры. С помощью банков памяти мы можем установить так, чтобы образ был поделен на банки, а по адресу 0x4000-0x7FFF был отображен выбранный банк. Таким образом, в один момент в этой области находится второй банк, в другой – десятый. Как мы захотим, в общем. Таким образом, мы приходим к виртуальным и физическим адресам. 0x4000-0x7FFF – это виртуальные адреса, которые не обязаны совпадать с физическими. Физический адрес – это настоящий адрес, по которому происходит доступ к ячейкам памяти.

Все это необходимо для того, чтобы наш DMG мог работать с образами игры, которые намного превышают не только 0x8000 байт, но и все адресное пространство. На словах это все может показаться слишком сложным, но в ходе реализации будет понятно, что это предельно элементарные вещи, которые проще и быстрее реализовать, чем объяснить.

Все тоже самое относится и к оперативной памяти. Банки позволяют расширить ее объем, поместив микросхемы в картридж. Кроме того, так можно реализовать полноценную систему сохранений, используя встроенный в картридж аккумулятор для питания оперативной памяти.

Задача перевода виртуального адреса в физический лежит на MBC-контроллере, который находится внутри картриджа. Все операции чтения и записи в области ROM’а проходят через него. Так же сюда перенаправляются операции, связанные с внешней оперативной памятью.

Естественно мы не может изменить содержимое ROM. Операции записи используются как управляющие команды для MBC. В CPU Manual можно прочитать, какие адреса, за какие функции отвечают. Таким образом, записав по определенному адресу число 9, мы говорим, что хотим выбрать банк 9. После этого мы можем читать его содержимое, обращаясь по адресам 0x4000-0x7FFF.

На рисунке ниже представлена простейшая схема работы MBC. Здесь область 0x0000-0x3FFF всегда перенаправляется в банк 0, как в некоторых реальных контроллерах, а вот область 0x4000-0x7FFF перенаправляется в текущий банк.

Рассмотрим схему адресного пространства DMG:

Секция памяти | Начальный адрес | Конечный адрес
ROM bank 0 | 0x0000 | 0x3FFF
Switchable ROM bank | 0x4000 | 0x7FFF
Video RAM | 0x8000 | 0x9FFF
Switchable RAM bank | 0xA000 | 0xBFFF
Internal RAM 1 | 0xC000 | 0xDFFF
Echo of Internal RAM 1 | 0xE000 | 0xFDFF
OAM | 0xFE00 | 0xFE9F
Не используется | 0xFEA0 | 0xFEFF
I/O ports | 0xFF00 | 0xFF4B
Не используется | 0xFF4C | 0xFF7F
Internal RAM 2 | 0xFF80 | 0xFFFE
Interrupt enable register | 0xFFFF | 0xFFFF

Подробнее о каждой секции:

  • ROM bank 0. Switchable ROM bank. Данные области мы уже рассмотрели.
  • Video RAM. Подробнее будет рассмотрена при реализации графики.
  • Internal RAM 1. Оперативная память внутри DMG.
  • Echo of Internal RAM 1. Здесь дублируется содержимое Internal RAM 1.
  • OAM. Здесь хранится описание спрайтов.
  • I/O ports. Здесь мы получаем доступ к регистрам других компонентов DMG.
  • Internal RAM 2. Оперативная память внутри DMG.
  • Interrupt enable register. В этом регистре хранятся флаги, которые разрешают обработку определенных прерываний. Это тот самый регистр IE, о котором мы уже говорили.

Поскольку архитектура эмулятора предполагает, что каждый компонент DMG будет иметь свой класс, то класс Cookieboy::Memory, эмулирующий память, будет содержать лишь следующие области памяти – ROM банки, internal RAM 1, Echo of internal RAM 1, Switchable RAM bank, internal RAM 2. При обращении ко всем другим областям будут вызываться методы доступа соответствующих классов.

Начнем с операций чтения и записи в памяти. Все предельно просто – смотрим на адрес и перенаправляем операции в соответствующие области памяти. Я сделал следующим образом. Как можно заметить, многие области памяти хорошо выравнены, что позволяет реализовать все с помощью switch и логических операций. Вот как это выглядит:

switch (addr & 0xF000)
{
case 0x8000:
case 0x9000:
    //осуществляем операции с видеопамятью
    break;
}

И никаких громоздких условных конструкций. Пока можно оставить лишь заготовку, поскольку некоторые области памяти будут находиться в других классах (например, видеопамять), которые мы еще не реализовали. Можно лишь реализовать то, что действительно есть в Cookieboy::Memory. Здесь стоит обратить внимание на банки ROM’а и Switchable RAM bank.

Если картридж, с которого был снят ROM, содержал MBC-контроллер, то в этих областях памяти нам надо реализовать логику этих контроллеров. Для этого можно поступить очень просто – доступ к этим областям перенаправляется в классы, которые реализуют соответствующие MBC-контроллеры, а они уже сами пусть решают, куда, как и что. Рассмотрим два примера – MBC 2 и MMM01. Первый – как пример, который позволит вам реализовать остальное. MMM01 – довольно странный MBC. По нему практически нет документации, а его реализация довольно сильно отличается от других MBC. Не повредит восполнить этот пробел в деле эмуляции DMG.

Для начала обзаведемся базовым классом MBC. Выглядеть он будет следующим образом:

const int ROMBankSize = 0x4000;
const int RAMBankSize = 0x2000;

class MBC
{
public:
    virtual void Write(WORD addr, BYTE value) = 0;
    virtual BYTE Read(WORD addr) = 0;

    virtual bool SaveRAM(const char *path, DWORD RAMSize);

    virtual bool LoadRAM(const char *path, DWORD RAMSize);

protected:
    MBC(BYTE *ROM, DWORD ROMSize, BYTE *RAMBanks, DWORD RAMSize) : ROM(ROM), ROMSize(ROMSize), RAMBanks(RAMBanks), RAMSize(RAMSize) {}

    BYTE *ROM;
    BYTE *RAMBanks;

    DWORD ROMOffset;
    DWORD RAMOffset;

    DWORD ROMSize;
    DWORD RAMSize;
};

Как видно, сначала идут функции записи и чтения – именно они будут вызываться из нашего Cookieboy::Memory. Далее идут функции сохранения и загрузки RAM. Тут мы сразу готовим почву для будущей эмуляции памяти в картридже, которая запитана от аккумулятора для сохранения ее содержимого после выключения консоли. Их реализацию я опущу – это всего лишь сохранение и чтение массива RAMBanks из файла, не более. Затем предельно очевидный конструктор и несколько полей:

  • ROM. Здесь у нас находится весь образ игры.
  • RAMBanks. Здесь находится оперативная память картриджа.
  • RAMOffset и ROMOffset. Это смещения, которые указывают на текущий банк памяти.
  • ROMSize и RAMSize, думаю, не требуют пояснений. Значения хранятся в банках памяти, а не в байтах.

С базовым классом покончено, теперь приступим к реализации класса, который эмулирует MBC2. Сразу посмотрим на код, а затем уже разберемся, как работает этот контроллер:

class MBC2 : public MBC
{
public:
    MBC2(BYTE *ROM, DWORD ROMSize, BYTE *RAMBanks, DWORD RAMSize) : MBC(ROM, ROMSize, RAMBanks, RAMSize)
    {
        ROMOffset = ROMBankSize;
        RAMOffset = 0;
    }

    virtual void Write(WORD addr, BYTE value)
    {
        switch (addr & 0xF000)
        {
        //ROM bank switching
        case 0x2000:
        case 0x3000:
            ROMOffset = value & 0xF;
            ROMOffset %= ROMSize;

            if (ROMOffset == 0)
            {
                ROMOffset = 1;
            }

            ROMOffset *= ROMBankSize;
            break;

        //RAM bank 0
        case 0xA000:
        case 0xB000:
            RAMBanks[addr - 0xA000] = value & 0xF;
            break;
        }
    }

    virtual BYTE Read(WORD addr)
    {
        switch (addr & 0xF000)
        {
        //ROM bank 0
        case 0x0000:
        case 0x1000:
        case 0x2000:
        case 0x3000:
            return ROM[addr];

        //ROM bank 1
        case 0x4000:
        case 0x5000:
        case 0x6000:
        case 0x7000:
            return ROM[ROMOffset + (addr - 0x4000)];

        //RAM bank 0
        case 0xA000:
        case 0xB000:
            return RAMBanks[addr - 0xA000] & 0xF;
        }

        return 0xFF;
    }
};

С функцией чтения все просто. ROMOffset используется как смещение для обращения к текущему банку ROM. С оперативной памятью есть одна деталь. MBC2 имеет 512 4-битовых блоков RAM. Мы, естественно, выделяем все 512 байт, просто операции записи и чтения обрезают значения до 4 младших бит.

Теперь функция записи. Именно здесь эмулируется логика MBC. MBC2 поддерживает только смену банков ROM. Меняются они с помощью записи номера банка длиной 4 бита в область адресов 0x2000-0x3FFF. Нулевой банк выбрать нельзя, т.к. он и так находится в 0x0000-0x3FFF. Здесь же стоит проверять выход за границы ROM. Некоторые игры по неопределенной причине пытаются выбрать банк, которого не существует. Это, естественно, приводит к ошибке. С проверкой игра работает, как ни в чем не бывало. Одной из таких игр является WordZap. Может это последствия неточной эмуляции (на идеальную эмуляцию DMG я, естественно, не претендую), но в любом случае проверка не повредит.

Да, 0xFF возвращается не случайно – на DMG данное значение возвращается в случае, когда содержимое не определено.

Наконец, рассмотрим MMM01. Я не уверен в правильности своего кода, поскольку описание этого контроллера было найдено на форуме, а само оно написано неизвестно кем. Код:

class MBC_MMM01 : public MBC
{
public:
    enum MMM01ModesEnum
    {
        MMM01MODE_ROMONLY = 0,
        MMM01MODE_BANKING = 1
    };

    MBC_MMM01(BYTE *ROM, DWORD ROMSize, BYTE *RAMBanks, DWORD RAMSize) : MBC(ROM, ROMSize, RAMBanks, RAMSize)
    {
        ROMOffset = ROMBankSize;
        RAMOffset = 0;
        RAMEnabled = false;
        Mode = MMM01MODE_ROMONLY;
        ROMBase = 0x0;
    }

    virtual void Write(WORD addr, BYTE value)
    {
        switch (addr & 0xF000)
        {
        //Modes switching
        case 0x0000:
        case 0x1000:
            if (Mode == MMM01MODE_ROMONLY)
            {
                Mode = MMM01MODE_BANKING;
            }
            else
            {
                RAMEnabled = (value & 0x0F) == 0x0A;
            }
            break;

        //ROM bank switching
        case 0x2000:
        case 0x3000:
            if (Mode == MMM01MODE_ROMONLY)
            {
                ROMBase = value & 0x3F;
                ROMBase %= ROMSize - 2;
                ROMBase *= ROMBankSize;
            }
            else
            {
                if (value + ROMBase / ROMBankSize > ROMSize - 3)
                {
                    value = (ROMSize - 3 - ROMBase / ROMBankSize) & 0xFF;
                }

                ROMOffset = value * ROMBankSize;
            }
            break;

        //RAM bank switching in banking mode
        case 0x4000:
        case 0x5000:
            if (Mode == MMM01MODE_BANKING)
            {
                value %= RAMSize;
                RAMOffset = value * RAMBankSize;
            }
            break;
        //Switchable RAM bank
        case 0xA000:
        case 0xB000:
            if (RAMEnabled)
            {
                RAMBanks[RAMOffset + (addr - 0xA000)] = value;
            }
            break;
        }
    }

    virtual BYTE Read(WORD addr)
    {
        if (Mode == MMM01MODE_ROMONLY)
        {
            switch (addr & 0xF000)
            {
            //ROM bank 0
            case 0x0000:
            case 0x1000:
            case 0x2000:
            case 0x3000:
            //ROM bank 1
            case 0x4000:
            case 0x5000:
            case 0x6000:
            case 0x7000:
                return ROM[addr];

            //Switchable RAM bank
            case 0xA000:
            case 0xB000:
                if (RAMEnabled)
                {
                    return RAMBanks[RAMOffset + (addr - 0xA000)];
                }
            }
        }
        else
        {
            switch (addr & 0xF000)
            {
            //ROM bank 0
            case 0x0000:
            case 0x1000:
            case 0x2000:
            case 0x3000:
                return ROM[ROMBankSize * 2 + ROMBase + addr];

            //ROM bank 1
            case 0x4000:
            case 0x5000:
            case 0x6000:
            case 0x7000:
                return ROM[ROMBankSize * 2 + ROMBase + ROMOffset + (addr - 0x4000)];

            //Switchable RAM bank
            case 0xA000:
            case 0xB000:
                if (RAMEnabled)
                {
                    return RAMBanks[RAMOffset + (addr - 0xA000)];
                }
            }
        }

        return 0xFF;
    }

private:
    bool RAMEnabled;
    MMM01ModesEnum Mode;
    DWORD ROMBase;
};

Как видите, кода много. Не буду объяснять каждую строчку – после предыдущего примера я надеюсь, что вам не составит труда понять, что и зачем делается. Скажу лишь, что MMM01 вроде бы используется всего в 2 играх, поэтому не случайно он есть далеко не во всех эмуляторах.

Возвращаясь к эмуляции памяти, стоит немного прояснить область памяти под названием I/O ports. Т.к. DMG состоит из различных компонентов, то неплохо бы иметь возможность как-то влиять на их работу и даже контролировать. Для этого в области памяти I/O ports мы имеем доступ к регистрам всех остальных компонентов DMG: контроллер экрана, звук, таймеры, управление и т.д. Естественно, все эти регистры в нашем эмуляторе будут находиться в соответствующих классах, а значит Cookieboy::Memory будет лишь перенаправлять все операции в них. Список и назначение всех регистров можно найти в CPU Manual. Так же я буду рассматривать их при необходимости. Кстати, один из них мы уже рассмотрели – IF. Этот регистр доступен именно в этой области памяти, поэтому необходимо перенаправить операции чтения и записи в класс Cookieboy::Interrupts. Мы уже можем это сделать, т.к. обеспокоились об этом заранее при рассмотрении прерываний.

Настало время еще одной важной функции – загрузки ROM из файла. Перед реализацией загрузки образа в память самое время упомянуть, что же происходит при включении DMG.

Сначала идет исполнение Bootstrap ROM’а, который хранится внутри DMG. Его содержимое можно найти в исходном коде класса Cookieboy::Memory. Он не делает ничего особенного, кроме проверки содержимого картриджа и отображения лого Nintendo. Он имеет длину 256 байт, исполнение начинается с 0 – т.е. после включения регистр PC процессора равен нулю. Его исполнение заканчивается командой, которая осуществляется запись по адресу 0xFF50. По этому адресу находится скрытый регистр, который указывает, откуда в данный момент поступают команды для процессора – из Bootstrap ROM’а или картриджа. Как ни странно, описания этого регистра нет практически нигде. Более того, нет даже упоминаний о нем.

Интересный факт. Bootstrap ROM был получен не так давно, а извлекли его посредством фотографии кристалла процессора. Автор сфотографировал часть процессора, в которой находился этот ROM, и на глаз по одному биту считал все содержимое.

Замечу, что при включении оперативная память DMG и картриджа содержит случайные числа. Это незначительная деталь, поэтому обычно эмуляторы заполняют эти области нулями. Как поступать – решать вам. Скорее всего, лучше или хуже от этого игры работать не станут. Учтите, речь только об оперативной памяти. Заполнение случайными значениями других областей приведет к некорректной работе эмулятора.

Конечно, не хотелось бы каждый раз запускать этот образ. Для этого можно сделать следующее. Регистр PC должен быть равен 0x100 – именно по этому адресу находится первая команда в образах игр. Далее, все регистры процессора и область памяти I/O ports необходимо проинициализировать значениями, которые оставляет после себя Bootstrap ROM – эти значения можно найти в CPU Manual. Не все игры настолько хорошо написаны, чтобы устанавливать все необходимые значения самостоятельно, некоторые могут полагаться на значения, которые установлены после выполнения Bootstrap ROM. Для этого все компоненты содержат функцию EmulateBIOS, посредством которой устанавливаются все необходимые значения.

И так, приступим к загрузке обараза. Весь файл образа читается в массив, а из заголовочной части образа читаются метаданные образа. Самое важное это узнать тип картриджа (тип MBC-контроллера) и размер внешней оперативной памяти внутри картриджа. Адреса указаны в CPU Manual. Так же стоит реализовать те проверки, которые делает Bootstrap ROM. С помощью них можно легко узнать, действительно ли файл является образом для DMG. Первая проверка – лого Nintendo. Каждый ROM содержит логотип Nintendo, который и отображается при выполнении Bootstrap ROM. Он должен иметь строго определенное значение. Какое – указано в CPU Manual. Так же можно проверить контрольную сумму заголовка образа. Для этого можно воспользоваться следующим кодом:

BYTE Complement = 0;
for (int i = 0x134; i <= 0x14C; i++)
{
    Complement = Complement - ROM[i] - 1;
}
if (Complement != ROM[0x14D])
{
    //проверка не пройдена
}

Если проверки прошли, то мы выделяем место под оперативную память картриджа и создаем объект соответствующего MBC чипа.

Касательно оперативной памяти картриджа, то неплохо всегда иметь под рукой как минимум один банк памяти, даже если образ «говорит», что она не используется. Некоторые игры выдают себя за картриджи без MBС, но, тем не менее, могут иметь простенький чип лишь для оперативной памяти.

Все, с памятью покончено.

Заключение

Для вступительной статьи мы проделали очень большую работу, но это даже не половина того, что нам необходимо. На данный момент наш эмулятор способен читать образ и исполнять его код. Что самое главное, он действительно может все это делать. Естественно, на экране при этом мы ничего не видим. Это мы и исправим в следующей статье.
  • эмулятор
  • , gameboy
  • , DMG
  • , C++
+114
30608
291
creker 18,5
Автоматизированное
продвижение сайтов
  • 50% экономии на ссылках
  • Запуск проекта за 10 минут
  • Вывод и удержание в ТОП 10
Подробнее
Похожие публикации
Об удобной навигации и отладке C++ кода в Vim вчера в 10:20
Чтение SVG в C++ 10 декабря в 18:27
Сводная таблица по поддержке C++ 11/14/17 7 декабря в 08:26
Рефакторинг C++ кода в Visual Studio 2015 18 ноября в 11:29
Идеальная передача и универсальные ссылки в C++ 8 ноября в 23:37
Интервью Скотта Мейерса в Яндексе. О настоящем и будущем C++ 28 октября в 15:08
Библиотека Strutext обработки текстов на языке C++ 27 октября в 16:18
Разработка трехмерных игр для Windows 8 с помощью C++ и Microsoft DirectX 27 октября в 09:27
Пишем эмулятор Gameboy, часть 3 30 октября 2012 в 08:27
Пишем эмулятор Gameboy, часть 2 22 октября 2012 в 13:29

Комментарии (32)

+4
Megas 16 октября 2012 в 12:23 #
На хабре раньше был цикл статей про эмуляцию GameBoy на JavaScript. Почему-тo сейчас уже не находит, но если кому-то интересно, то вот оригинал: imrannazar.com/GameBoy-Emulation-in-JavaScript:-The-CPU
0
asfd 19 октября 2012 в 13:27 #
↵
↑
Это я переводил. Правда не все статьи еще обработал. blog.asfdfdfd.com/?s=Imran
+1
sergeisirik 16 октября 2012 в 12:52 #
Хорошая, статья, только: . Хотелось запустить посмотреть. DirectX что ли нужен или что это за библиотечка?
0
creker 16 октября 2012 в 13:32 #
↵
↑
Спасибо. Да, я использую Direct3D 9, это как раз одна из его библиотек.
+1
nzeemin 16 октября 2012 в 15:18 #
↵
↑
При написании эмулятора лучше сразу думать о переносимости. Можно выделить ядро эмуляции и делать разный интерфейс для разных фронтэндов и платформ. А можно использовать SDL в качестве переносимого фронтэнда.
0
creker 16 октября 2012 в 20:18 #
↵
↑
Несмотря на то, что в мои планы переносимость не входила (как и доведение эмулятора до презентабельного состояния), я обеспокоился об отделении ядра эмулятора от всего остального заранее. По сути, наружу торчать лишь три вещи — буфер кадра, буфер сэмплов, состояние кнопок, соответственно, видео, аудио, пользовательский ввод. А уж распоряжаться ими можно как угодно.
+2
Hint 16 октября 2012 в 13:41 #
Сколько времени потратили на изучение и разработку?
+2
creker 16 октября 2012 в 15:27 #
↵
↑
Сложно сказать. Начал я в где-то в начале этого года. Закончил буквально неделю назад. Естественно писал я его не целыми днями, а по вечерам. Иногда делал перерыв на пару недель. Большего всего времени отняли процессор и звук. Процессор долго писать сам по себе, да и отлаживать его то еще удовольствие. Звук — коварная штука со скудной документацией, один раз пришлось переделывать с нуля из-за неудачного подхода к эмуляции. О звуке я обязательно напишу в будущих статьях.
0
Fandir 16 октября 2012 в 15:10 #
Если не секрет на кого вы учились? ))
0
creker 16 октября 2012 в 22:15 #
↵
↑
Закончил ИТМО как инженер по специальности информационные системы и технологии
–4
Derailed 16 октября 2012 в 16:06 #
–4
6opoDuJIo 16 октября 2012 в 16:52 #
↵
↑
Вы намекнули на проблему программы, но увы, проблема именно у вас — нужно найти эту библиотеку direct x (элементарно ищется через гугл) и положить в %systemroot%\system32
+1
Derailed 16 октября 2012 в 16:58 #
↵
↑
«Спасение утопающих дело самих утопающих» — не ново. Решил проблему следующим образом — переименовал имеющийся у меня файл D3DX9_42.dll в D3DX9_43.dll, и программа запустилась. Она при загрузке спрашивает еще какой-то файл. Что ей надо подсунуть? Наверное файл картриджа? Не подскажет ли уважаемый автор, где эти файлы картриджей можно найти?
+3
6opoDuJIo 16 октября 2012 в 19:02 #
↵
↑
>>переименовал имеющийся у меня файл D3DX9_42.dll в D3DX9_43.dll
facepalm
0
Derailed 16 октября 2012 в 19:17 #
↵
↑
А что не так? Тупо сделал копию, положил её в папку программы, переименовал. Вы как будто с луны упали.
0
Agent_Smith 17 октября 2012 в 11:06 #
↵
↑
А скачать свежий DirectX c сайта производителя не?
0
Derailed 17 октября 2012 в 11:20 #
↵
↑
Не предлагает сайт производителя для Windows XP ничего новее, увы. Вроде как самый свежий у меня Direct X, который существует в природе для моей системы. Вот что показал dxdiag.exe: DirectX 9.0c (4.09.0000.0904).

Проблема в том, что сама Windows XP у меня считается уже устаревшей, но пока устраивает по всем параметрам. Не советуйте пожалуйста переустановить Windows на более новую версию, или купить новый компьютер =).
0
6opoDuJIo 17 октября 2012 в 15:38 #
↵
↑
А вы читали что я писал вообще в своём ответе на ваш скриншот?
–1
Derailed 17 октября 2012 в 15:46 #
↵
↑
Читал конечно. Но ничего полезного там не почерпнул.
–2
Zigmar 16 октября 2012 в 18:44 #
void Write(WORD addr, BYTE value);

Что-то я не припомню, что бы в С++ были типы данных WORD и BYTE…
Зачем использовать платформо-зависимый диалект языка, там где не идет на прямую работа с системыми API?
0
Derailed 16 октября 2012 в 19:42 #
↵
↑
Типы могут быть любые, если их определить. Это просто потому, что так удобнее программисту. Возможно, использовался готовый код. Наверняка человек раньше писал в основном на C.
+2
arkamax 16 октября 2012 в 19:57 #
Я бы взял вас на работу… если бы хватило денег.
0
Epsiloncool 17 октября 2012 в 03:38 #
А нельзя просто взять эмулятор ZX-Spectrum'а под названием QAOP, написанный на JS/HTML, и выпилить из него всё лишнее?)
0
creker 17 октября 2012 в 12:04 #
↵
↑
Конечно можно, все таки и там и там довольно похожие процессоры, но так мы получим только эмуляцию инструкций процессора, к тому же между Z80 и процессором Gameboy есть существенные отличия. Все остальное все равно придется писать самому. Я одно время хотел так же поступить, но моей целью было получение опыта в деле эмуляции, а тут уж лучше все самому.
0
Zuy 17 октября 2012 в 06:02 #
Исходники не смотрел, лучше спрошу.
Интересно как происходит синхронизация времени исполнения с реальной скоростью работы геймбоя? На современных компьютерах каждая эмулируемая инструкция будет исполняться значительно быстрее оригинала. Вы как-то меряете время исполнения инструкции и добавляете паузу?
0
creker 17 октября 2012 в 12:31 #
↵
↑
Одно время я использовал паузы, но время исполнения отдельных инструкций измерять не требуется. Ядро эмулятора крутится в бесконечном цикле. На каждой итерации я жду, пока эмулятор не произведет полное обновление «экрана», что равняется строго определенному числу тактов процессора Gameboy. Мне достаточно измерить, сколько ушло времени на эмуляцию одного полного обновления «экрана» Gameboy, а там уже делаю паузу, чтобы уложиться в 60 Гц частоту обновления.

Когда я наконец-то сделал адекватную эмуляцию звука, то получилось отказаться от ручной синхронизации (так что правильно, что не смотрели исходники), но обо всем по порядку. Объяснять все сейчас, значит пересказывать куски будущих статей. О синхронизации тоже пойдет речь, это довольно существенная проблема, особенно, когда есть звук.
0
Zuy 18 октября 2012 в 11:04 #
↵
↑
Спасибо вам за работу, буду ждать с нетерпением продолжения цикла статей.
0
nzeemin 18 октября 2012 в 09:15 #
↵
↑
У разработчиков MAME был цикл статей про тайминг — там описывался правильный подход.
Но он далеко не прост, действительно проще привязаться к обновлениям экрана.

CPU Scheduling in MAME
part 1 aarongiles.com/?p=137
part 2 aarongiles.com/?p=138
part 3 aarongiles.com/?p=139
part 4 aarongiles.com/?p=140
0
Zuy 18 октября 2012 в 11:02 #
↵
↑
Понятно, что не прост, я по-этому и спросил.
Спасибо за ссылки, обязательно почитаю.
0
nzeemin 18 октября 2012 в 10:21 #
> следующим шагом является документация

Не увидел ссылки на «Pan Docs» — для программиста под GameBoy это в общем-то основной справочный источник: nocash.emubase.de/pandocs.htm
0
creker 18 октября 2012 в 12:24 #
↵
↑
Gameboy CPU manual содержит Pan Docs наряду с другими документами. Одними Pan Docs все таки не обойдешься.
0
Stylerxxx 3 февраля 2013 в 02:25 #
интересная статья. я бы наверное использовал jump table для опкодов, вместо множества кейсов.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Метки лучше разделять запятой. Например: общение, социальные сети, myspace.com, подростки, мердок
или закрыть
Популярное за сутки
Светильник декоративный бытовой СДБ-З «Евлампия»
Бизнес не по-детски — Или как восьмиклассник развозил фрукты?
Бесплатная отправка SMS из Arduino без GSM модуля
Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №138 (8 — 14 декабря 2014)
Как сделать греческие буквы в формулах прямым шрифтом в LaTeX
Конференция C++ Russia в Москве
Конференция ZeroNights 2014 — как все было
Обзор наиболее интересных материалов по анализу данных и машинному обучению №26 (8 — 14 декабря 2014)
Сказ о том, как мы в хакатонах участвовали, или Страх и Ненависть в Кремниевой Долине
Публичный тест нового интерфейса HeadHunter
Как добавить карты Bing Maps в Windows-приложение на HTML и JavaScript. Часть 2
Электрофокусер на базе отладочной платы Arduino Uno, часть 3
Replication slots в PostgreSQL
Composer. Небезопасно использовать packagist и приватный источник пакетов одновременно
5 ошибок при обратной связи с пользователями
Моделируем преднапряжение бетонной оболочки АЭС
Обзор возможностей App Annie для Windows и Windows Phone разработчиков
Диагностика проблем после инсталляции или апгрейда ORACLE APEX
Concurrency структуры в .net. ConcurrentQueue изнутри
MyLifeOrganized: папки vs контексты
все лучшие
Лучшее на Geektimes
Нефтерубль
Новогодние космические подарки
Роскосмос планирует построить лунную базу, наладить промышленность и добычу ископаемых
В погоне за синим светодиодом
Сбывшиеся мечты Роберта Бойля. Часть 1
На космическом корабле Orion используется процессор 12-летней давности
Паровые машины… и как появились «лошадиные силы»
Вымрет ли человечество «тепловой смертью»?
Samsung и Apple опять повышают цены в России
Yahoo призывает переходить на Firefox
все посты
Вопросы по теме
Почему не корректно работает make.exe?
Linking GTK-3.0 C++ app with MinGW: undefined reference to `WinMain@16'?
Почему c++ программ не видит файл при запуске вне xcode?
Как работать с кириллицей в c++?
Как рационально сосчитать парники (задача об огурцах)?
Как перекинуть указатель в glfwSetKeyCallback на си?
Можно ли выяснить, содержит ли указатель адрес допустимого объекта или нет?
Как создать два процесса,в котором один отсылает события, а другой распознает их и обрабатывает?
Как в С++ генерировать последовательность чисел без повторов?
Как сгенерировать уникальный число int64 ?
Какие функции использовать чтобы работать с элементами чужого окна?
Как послать emal через c++ ?
На чем написать программу для работы с чужим окном?
Как определить первый и последний сегмент в сырых TCP пакетах при IP фрагментации?
Как лучше всего создать свой красивый tray notification?
Инструмент для тестирования программной комбинационной схемы (алгоритма)?
С++ проект под Android. С чего начать?
Как передать указатель на порт Serial в библиотеку Arduino и ОДНОКРАТНО создать на него ссылку внутри всей библиотеки?
Что лучше выбрать для разработок?
Что не так с моим кодом?
Что обсуждают?
Установка, настройка и тестирование Fedora 21 Workstation на личном или игровом компьютере 42
Публичный тест нового интерфейса HeadHunter 47
Бизнес не по-детски — Или как восьмиклассник развозил фрукты? 13
Пару слов об интеграции в TFS системы управления дефектами с системой управления версиями 144
Об удобной навигации и отладке C++ кода в Vim 80
Если заказчик — редакция 17
Установка BIND9 DNS на CentOS 1
Google Chrome пометит HTTP-сайты как небезопасные 150
Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №138 (8 — 14 декабря 2014) 2
Конференция C++ Russia в Москве 3
все публикации
Компания дня
КРОК

Последняя публикация: Как мы монтируем ДДИБП: огромные маховики в дата-центрах и средство аварийного резерва ответственных объектов

2019 подписчиков
Будущее [отсутствие] интерфейсов браузеров от Яндекса
Исследование причин аномального голосования на сайте РОИ или особенности электронной демократии в России
Необычные материалы в DIY. Стоматология + электроника =? Часть 1
Brainstorage
Android Developer
Python разработчик
PHP - программист
Web Designer
PHP developer
JavaScript разработчик (Middle level)
Junior Web Developer (JavaScript, Node.js)
PHP программист
Ведущий веб-разработчик
Ruby on Rails разработчик для постоянных случайных рабочих связей
все вакансии
Nodejs/C++ developer, разработка модуля
Доделать сайт на open card
Добавить пользовательское поле в корпортал Битрикс
Выполнить интеграцию двух страниц на Yii в существующий проект
Поиск дыр в безопасности на WP
Печать бирок для одежды (Москва)
Добавить боковое меню в корпоративный портал Битрикс
Андроид-разработка (серверный менеджер задач)
Дизайн Landing'a в FLAT
Создание страницы продукта по аналитике (дизайн+верстка)
все заказы
Войти
Регистрация
Разделы
Публикации
Хабы
Компании
Пользователи
Q&A
Песочница
Инфо
О сайте
Правила
Помощь
Соглашение
Услуги
Реклама
Тарифы
Семинары
Спецпроекты
Разное
Приложения
Тест-драйвы
Помощь стартапам
TM © 2006–2014

Служба поддержки

Мобильная версия


Пишем эмулятор Gameboy, часть 3 / Хабрахабр

Разделы
Публикации Хабы Компании Пользователи Q&A Песочница Биржа авторов
Спецпроекты
Конкурс приложений Биржа статей Дарите улыбки
30 октября 2012 в 08:27

Пишем эмулятор Gameboy, часть 3 tutorial

Разработка *, Программирование *
Здравствуйте!

В предыдущей части данного цикла статей мы закончили работу над критически важными компонентами нашего эмулятора. Для полноты картины в данной статье мы рассмотрим звуковую систему DMG.

Пишем эмулятор Gameboy, часть 1
Пишем эмулятор Gameboy, часть 2
Пишем эмулятор Gameboy, часть 3

Прежде чем начать, вот ссылка на репозиторий Cookieboy, где можно найти его исходный код и последнюю сборку.

Оглавление

Звуковая система
Звуковые каналы 1 и 2
  • Sweep unit
  • Length counter
  • Envelope unit
Звуковой канал 3
Звуковой канал 4
Реализация
Тестирование
Что дальше
Заключение

Звуковая система

DMG позволяет выводить стереозвук с помощью смешивания 4 независимых звуковых каналов. К каждому каналу подключены компоненты модуляции, которые и может контролировать игра для вывода необходимого звука. Всего компонентов три и назначение для всех каналов у них идентично:
  • Sweep unit. Осуществляет изменение частоты звука с заданным периодом и шагом.
  • Length counter. Контролирует длительность вывода звука.
  • Envelope unit. Осуществляет изменение громкости звука с заданным периодом.

Каждый канал дает в распоряжение ряд регистров, которые позволяют контролировать эти компоненты и сам канал. Они (регистры) нумеруются определенным образом – NRXY, где X – номер канала (1, 2, 3, 4), Y – номер регистра. Где необходимо я буду опускать номер канала и просто писать X.

Следующие звуковые каналы доступны на DMG:
  • Прямоугольная волна. Содержит все три компонента модуляции.
  • Прямоугольная волна. Содержит компоненты контроля громкости и длительности.
  • Волна произвольной формы. Содержит только компонент контроля длительности. Громкость устанавливается в одном из регистров вручную.
  • Генератор шума. Содержит компоненты контроля громкости и длительности.

Первые два канал идентичны и отличаются лишь набором компонентов модуляции.

Третий канал позволяет воспроизводить сигнал произвольной формы из специальной области памяти в секции I/O ports – Wave Pattern RAM. Таким образом, можно воспроизводить цифровой звук произвольного содержания путем своевременного обновления указанной области памяти. Некоторые умудряются воспроизводить что-то похожее на речь.

Четвертый канал позволяет генерировать шум различного характера. Хорошо подходит для озвучивания различных спецэффектов.

Вот упрощенная схема формирования звука в DMG:

После прохождения всех компонентов модуляции звуковой сигнал попадает в микшер, который смешивает различные каналы и выводит в один из выходов. S01 – правое ухо. S02 – левое ухо. Операция смешивания сводится к простому сложению сигналов ото всех источников для конкретного выхода – NR51 указывает, куда и какие каналы должны быть выведены. Далее учитывается громкость для каждого из выходов – сигнал после смешивания умножается на значение громкости данного выхода в регистре NR50 плюс 1.

Не стоит пытаться полностью разобраться в этой схеме – по ходу дела все, что на ней нарисовано, будет рассмотрено подробнее.

NR50 и NR51 являются общими регистрами. Кроме них есть общий регистр NR52, который содержит флаг отключения всего звука, а так же биты, показывающие статус звуковых каналов. Изменять можно только флаг отключения звука. Биты статуса доступны только для чтения и постоянно обновляются.

Если в регистре NR52 отключен звук, то происходит следующее:
  • Все регистры обнуляются, кроме счетчиков Length counter. Это означает, что нужно обнулить только биты, относящиеся к Duty cycle (далее станет понятно, о чем я).
  • Запрещается запись во все регистры, кроме NRX1. Причем, запись может осуществляться только в те биты, которые относятся к Length counter.

К слову о доступности битов регистров. Обращайте внимание на то, какие биты не используются или недоступны для чтения. При попытках прочитать регистры извне нужно установить в единицу все биты, которые не используются или недоступны для чтения. Тестовые ROM’ы проверяют это. Изменять сам регистр не нужно ни в коем случае. Звуковые компоненты, естественно, имеют полный доступ к регистрам.

Все компоненты, формирующие звук, синхронизированы с тактовым генератором. Для генерации звуковых волн определенной частоты используется сам тактовый генератор. Для модулирующих компонентов выделен отдельный тактовый генератор Frame Sequencer, работающий на частоте 512 Гц. Он так же работает от основного тактового генератора, но позволяет генерировать низкочастотные отсчеты. Для Sweep Unit используется частота 128 Гц. Для Length Counter – 256 Гц. Для Envelope Unit — 64 Гц. Вот как выглядит процесс работы этого тактового генератора, где каждая строка означает один отсчет Frame Sequencer’а:
Length Counter | Envelope unit | Sweep unit
отсчет | - | отсчет
- | отсчет | -
отсчет | - | -
- | - | -
отсчет | - | отсчет
- | - | -
отсчет | - | -
- | - | -

В таблице отмечено, какие отсчеты Frame Sequencer’а генерируют отсчет для компонентов модуляции. Получается, что он циклично проходит такую последовательность отсчетов, что дает нам 8 возможных состояний Frame Sequencer (пронумеруем их от 0 до 7). Здесь важно учитывать, с какой фазой происходят отсчеты для компонентов. Так же стоит учесть, что при запуске звука (флаг в регистре NR52) Frame Sequencer стартует с состояния 1. Очень важно при этом дать понять компонентам модуляции, что вы изменили состояние Frame Sequencer. Одно время я еле нашел эту ошибку, из-за которой не мог пройти один из тестовых ROM’ов.

Разобравшись с общим устройством, перейдем к рассмотрению каждого конкретного канала.

Звуковые каналы 1 и 2

Сначала рассмотрим каналы, генерирующие прямоугольные волны. Здесь не имеет смысла разделять 1 и 2 каналы. Рассмотрев канал 1, можно будет реализовать канал 2 простым урезанием функционала, поскольку они идентичны за исключением Sweep unit.

И так, что такое прямоугольная волна. Внизу на рисунке изображена как раз такая волна.

Не особенно важно, где находится временная ось. В моем эмуляторе используется волна, которая генерирует отрезки «есть сигнал» (1-2, 3-4, 5-6), «нет сигнала» (0-1, 2-3, 4-5). Можно было бы сделать по-другому и поместить временную ось посередине, но это лишь усложнит реализацию, а результат будет идентичным.

На этом рисунке скважность сигнала равна 2, поскольку отрезки с разной амплитудой имеют одинаковую длительность. DMG позволяет генерировать прямоугольные волны с различными значениями скважности, хотя в документации принято использовать величину обратную скважности – коэффициент заполнения (duty cycle). Он кстати и более нагляден, им и будем пользоваться. Выбор дается из 4 различных значений коэффициента заполнения – 0.125, 0.25, 0.5, 0.75. Коэффициент заполнения никак не влияет на частоту, а лишь на характер сигнала. На рисунке ниже показаны различия сигналов при различных коэффициентах заполнения и одинаковой частоте.

Предоставлено 4 значения, хотя, по сути, значений 3 – коэффициенты заполнения 0.25 и 0.75 дают разные на вид волны, но звучание у них идентичное. При воспроизведении звука значение имеет изменение амплитуды, которое имеет одинаковый характер при коэффициентах заполнения 0.25 и 0.75.

Значение коэффициента заполнения содержится в регистре NRX1 в двух верхних битах.

Естественно нам надо знать, какой частоты должен генерироваться сигнал. Для этого используются регистры NRX3 и NRX4. Частота указывается числом длиной 11 бит – младшие 8 бит содержатся в регистре NRX3, старшие 3 бита в регистре NRX4. Таким образом, частота может лежать в пределах от 0 до 2047, но эти значения не относятся к реальной частоте звука. Для перевода этих значений в реальную частоту необходимо воспользоваться следующей формулой:

F = 4194304 / (32 * (2048 – X)) Гц,

где X – частота из регистров NRX3 и NRX4, F – частота звука.

Таким образом, частота звука лежит в пределах от 64 Гц до 131 072 Гц. Не стоит беспокоиться о таких высоких частотах – мало того, что нам будет довольно сложно должным образом сгенерировать звук такой частоты (по теореме Котельникова частота дискретизации должна быть больше 262 144 Гц); так все усложняется тем, что наша техника не способна воспроизвести такое, а наши уши не способны услышать. Более реальный диапазон ограничивается 22 000 Гц – это примерно соответствует верхней границе динамического диапазона человеческого слуха и совсем не случайно и большинства акустических систем. А для таких частот нам достаточно привычной частоты дискретизации в 44 100 Гц.

Формула выше обычно дается в документации как данное, но неплохо бы понимать, почему вычисляется именно так. Посмотрим еще раз на схему работы звуковой системы, там есть компонент Wave generator. В нем содержится таймер, с помощью которого генерируется волна нужной частоты. Период этого таймера равен 4 * (2048 – X). Чтобы волна прошла полный период, таймер должен сделать 8 отсчетов, что и дает нам заветные 32 * (2048 – X) – это значение полного периода волны.

Упомянутый таймер при должной реализации позволит вам не беспокоиться о переводах частот. Если таймер в эмуляторе будет синхронизирован с процессором так же, как и все остальные компоненты,
то все будет работать само собой. Формула 4 * (2048 – X) дает период таймера в тактах.

За 8 отсчетов этого таймера звуковая волна пройдет полный период. Теперь вернемся к коэффициенту заполнения. Он диктует характер изменения волны в течение ее периода. В документации приводятся следующие значения (1 и 0 в правом столбце означает, соответственно, «есть сигнал» и «нет сигнала»):
Коэффициент заполнения | Форма одного периода сигнала
0.125 | 00000001
0.25 | 10000001
0.5 | 10000111
0.75 | 01111110

Помимо частоты в регистре NRX4 хранятся и другие данные. Вот кстати его структура:
Биты | Назначение
7 | Перезапуск канала
6 | Бесконечное/конечное воспроизведение
2-0 | Нижние 3 бита частоты

Если бит 6 сброшен, то звук воспроизводится бесконечно. Если бит установлен, то в дело вступает Length Counter.

Бит 7 перезапуска именно это и делает. Если в него записывается 1, то канал перезапускается. Это может показаться странным для канала, который воспроизводит бесконечный периодический сигнал, но в действительности это имеет большее значение для компонентов модуляции. О них позже. Помимо этого, упомянутые выше формы сигналов позволяют генерировать правильный сигнал – при перезапуске канала период сигнала тоже стартует с самого начала согласно указанным формам.

Это все, что нужно знать о каналах 1 и 2 в общем. Далее в ход вступают компоненты модуляции. Несмотря на то, что каждый канал содержит свои компоненты модуляции, их принцип работы идентичен. Сейчас мы рассмотрим все компоненты модуляции (канал 1 их все содержит), чтобы больше уже не повторяться.

Sweep unit
Данный компонент контролирует частоту сигнала. Он работает в двух режимах – увеличение или уменьшение частоты. Поддерживаются различные периоды и размеры шага. Контролируется компонент посредством регистра NRX0. Вот его структура:
Биты | Назначение
6-4 | Период: 000 – компонент выключен 001 – 1/128 с 010 – 2/128 с 011 – 3/128 с 100 – 4/128 с 101 – 5/128 с 110 – 6/128 с 111 – 7/128 с
3 | Режим: 0 – увеличение частоты 1 – уменьшение частоты
2-0 | Шаг

Периоды указаны в миллисекундах, и их следовало бы перевести в такты для отсчета времени, но при должной реализации Frame Sequencer нам это не понадобится. Sweep unit работает на частоте 128 Гц, поэтому периоды не случайно вычисляются относительно 1/128 — это и позволяет забыть о ручном подсчете тактов. Так дело обстоит и с остальными компонентами – Frame Sequencer все считает, остальным ни о чем не надо «беспокоиться».

Теперь шаг. Объяснять его бессмысленно, проще привести формулу, по которой вычисляется следующее значение частоты при очередном отсчете:

F(t) = F(t – 1) ± F(t – 1) / 2n,

где F(t) – следующее значение частоты, F(t – 1) – текущее значение частоты, n – значение шага из регистра NRX0. Замечу, что нам нужно использовать не деление, а битовый сдвиг вправо на число шагов из регистра NRX0.

На рисунке ниже изображена работа Sweep unit при NRX0 = 0x61:

Изменение частоты происходит постоянно до тех пор, пока не будет достигнут один из пределов для значения частоты или кто-то не отключит Sweep unit. Если включен режим уменьшения частоты и очередная частота получилась отрицательной, то сохраняется предыдущее значение и вычисления прекращаются. Если включен режим увеличения частоты, и она перевалила за максимальное значение (2047), то канал останавливается, а в соответствующем бите статуса регистра NR52 записывается ноль, сигнализирующий о том, что канал остановлен.

На этом заканчиваются простые вещи и начинаются неочевидные детали. Sweep содержит несколько скрытых регистров, недоступных извне – флаг активности (internal enabled flag) и регистр-буфер для частоты (frequency shadow register). Так же он содержит счетчик для соблюдения установленного в регистре NRX0 периода.

Я уже упоминал, что бит перезапуска в регистре NRX4, который еще называют trigger, имеет значение для компонентов модуляции. При его установке в Sweep Unit происходит следующее:
  • Частота канала (NRX3 и NRX4) копируется в регистр-буфер частоты.
  • Счетчик сбрасывается. Для этого из регистра NRX0 нужно скопировать биты 6-4, т.е. в счетчике окажется число отсчетов на частоте 128 Гц. Frame Sequencer будет генерировать отсчеты на этой частоте, поэтому и счетчик должен ей соответствовать. Как видите, нет лишних преобразований, если все делать правильно.
  • Флаг активности устанавливается, если период или шаг не равны нулю. Иначе сбрасывается.
  • Если шаг не равен нулю, то тут же происходит вычисление новой частоты и ее проверка на переполнение (не более 2047), но новая частота не сохраняется – все делается лишь ради проверки на переполнение.

И так, что же происходит, когда счетчик «говорит», что пора обновить частоту. Сначала мы сбрасываем счетчик. Затем проверяем флаг активности – если он установлен, то происходит вычисление новой частоты согласно формуле выше с той разницей, что в качестве F(t – 1) выступает регистр-буфер частоты. Тут же происходит проверка на переполнение – если новая частота превысила 2047, то канал отключается.

Если переполнения не было и шаг не равен нулю, то новое значение частоты записывается в регистры NRX3 и NRX4, а так же в регистр-буфер частоты. Сразу же происходит еще одно вычисление новой частоты и проверка на переполнение, но эта частота не сохраняется – это все делается только ради еще одной проверки переполнения.

Регистр-буфер частоты здесь опустить не получится. Его присутствие приводит к тому, что полноценный ручной контроль частоты канала во время работы Sweep unit невозможен. Мы можем изменить частоту сами, но она будет таковой до тех пор, пока не будет произведен отсчет Sweep unit – поскольку он использует в вычислениях регистр-буфер частоты, то наше значение частоты будет проигнорировано и перезаписано, а вычисления продолжатся, как ни в чем не бывало.

Теперь две странности, которых у компонентов модуляции в DMG предостаточно:
  • Как было упомянуто ранее, период со значением 0 означает, что Sweep unit отключен. Это логично и должно было бы быть именно так, но в реальности для DMG период со значением 0 в регистре NRX0 означает, что период равен 8. Никаких вычислений частоты не происходит, просто Sweep unit работает вхолостую. Тестовые ROM’ы это проверяют.
  • Представим такой сценарий. Игра установила режим уменьшения частоты. Прошло некоторое время и успело произойти вычисление новой частоты. Если после этого игра попытается установить режим увеличения частоты, то канал будет тут же отключен. Таким образом, если в режиме уменьшения частоты произошло хоть одно вычисление новой частоты, то смена режима на увеличение частоты отключает канал.

Length counter
Данный компонент является простейшим счетчиком. Он отмеряет определенное кол-во отсчетов, а затем отключает подключенный к нему звуковой канал. Данный компонент присутствует у всех каналов и использует для своей работы регистр NRX1 – в нем хранится длительность воспроизведения канала. Приводить его структуру не буду – у всех каналов отведено различное число бит под значение длительности. Помимо этого для Length Counter имеет значение бит 6 в регистре NRX4, который вкл/откл данный компонент.

Frame Sequencer генерирует отсчеты для этого компонента на частоте 256 Гц. Значения в регистре NRX1 указаны в отсчетах на такой частоте, что, в очередной раз, означает свободу от конвертирования. При каждом отсчете изменения записываются обратно в регистр – регистр NRX1 и является счетчиком.

Перед использованием значения длительности из регистра NRX1 его надо преобразовать по следующей формуле:

Counter = (~NRX1 & Mask) + 1,

где Counter – счетчик, Mask – маска, с помощью которой из регистра извлекается только значение длительности (в нем иногда еще находится коэффициент заполнения). Это и даст нам число отсчетов на частоте 256 Гц.

Вроде бы все предельно очевидно – элементарный счетчик. При очередном отсчете проверяем флаг в регистре NRX4 на истинность и в случае успеха помечаем отсчет в счетчике компонента. Если счетчик достиг конечного значения, то канал отключается. Сложности возникают при реализации очередных странностей – все они сосредоточены в обработчике изменений регистра NRX4:
  • Если компонент был выключен и сейчас включается посредством бита 6, счетчик не достиг нуля, а текущее состояние Frame Sequencer осуществляет отсчет нашего компонента (все четные состояния), то мы тут же осуществляем отсчет. Это может привести к тому, что счетчик достигнет нуля и канал будет отключен, но тут надо учитывать еще одно условие – отключение канала здесь происходит только в случае, если не осуществляется перезапуск канала, т.е. бит перезапуска равен нулю.
  • Если осуществляется перезапуск и счетчик достиг нуля, то в регистр NRX1 записывается максимально возможное значение длительности, т.е. все биты длительности обнуляются (см. формулу выше). Если при этом осуществляется включение компонента (предыдущее значение не важно) и текущее состояние Frame Sequencer осуществляет отсчет нашего компонента, то тут же производим отсчет. Тут уже условий никаких не надо – отключился канал, значит отключился.

Envelope unit
Данный компонент контролирует громкость звука, уменьшая или увеличивая ее с постоянным шагом с определенным периодом. Громкость в данном случае означает амплитуду генерируемого сигнала. Контролируется данный компонент посредством регистра NRX2:
Биты | Назначение
7-4 | Начальное значение амплитуды
3 | Режим: 0 – уменьшение 1 – увеличение
2-0 | Период

Frame Sequencer генерирует отсчеты для этого компонента на частоте 64 Гц. Значения в регистре NRX2 указаны в отсчетах на такой частоте. При каждом отсчете происходит отсчет внутреннего счетчика периода. Когда он отсчитает один период, то значение амплитуды увеличивается/уменьшается на единицу. При достижении граничных значений вычисления прекращаются. При амплитуде 0, очевидно, канал заглушен, но активен.

На рисунке выше показан график изменения амплитуды сигнала при работе Envelope unit со значением NRX2 равным 0x55. Начальное значение амплитуды устанавливается при перезапуске канала (trigger) – в данном случае оно равно 5. В процессе работы оно больше не используется и не модифицируется. Далее с каждым периодом амплитуда уменьшается на один до тех пор, пока не достигнет нуля.

Теперь очередные странности. Сначала при модификации NRX4:
  • Как и для Sweep unit период 0 означает, что период равен 8. Опять же, никаких вычислений не происходит – компонент работает вхолостую.
  • Если канал перезапускается, и следующее состояние Frame Sequencer генерирует для Envelope отсчет, то счетчик периода устанавливается на единицу больше, чем должен быть.
  • Если канал перезапускается, начальное значение амплитуды равно нулю и выставлен режим уменьшения амплитуды, то канал тут же отключается.

Теперь при модификации NRX2:
  • Если текущее (новое значение пока не записали) значение NRX2 содержит период, равный нулю, а счетчик периода еще не закончил отсчет (мы же помним, что период 0 означает 8), то необходимо увеличить на единицу текущее значение амплитуды. В противном случае, проверяем текущий режим – если это уменьшение, то амплитуду надо увеличить на 2. Т.е. это все приведет к немедленному повышению амплитуды генерируемого каналом сигнала.
  • Если происходит смена режима, то амплитуда устанавливается равной 16 минус значение амплитуды.
  • После всех операций значение амплитуды обрезается до младших 4 бит.

Envelope unit имеет наиболее странное поведение из всех, но, к сожалению, документированы лишь упомянутые странности. Поведение на реальном DMG намного сложнее, но ни один эмулятор не может похвастаться его точной реализацией.

Звуковой канал 3

Данный звуковой канал генерирует волну согласно содержимому в Wave Pattern RAM, которая расположена в области памяти I/O ports. Wave Pattern RAM имеет длину 16 байт и содержит 32 сэмпла. Каждый байт содержит по 2 сэмпла – первый сэмпл в старших 4 битах, второй в младших 4 битах. Содержимое памяти проигрывается циклично с заданной в регистрах NR33 и NR34 частотой (структура этих регистров идентична 1 и 2 каналам).

Этот канал содержит только Length Counter из компонентов модуляции – его работа идентична остальным каналам и контролируется с помощью регистра NR31. Амплитуда сигнала регулируются вручную. Значение амплитуды устанавливается с помощью регистра NR32, в котором используются только 6 и 5 биты. Они могут иметь следующие значения:
  • 00: канал заглушен, но активен.
  • 01: Wave Pattern RAM воспроизводится как есть.
  • 10: Wave Pattern RAM воспроизводится с предварительным сдвигом каждого сэмпла на 1 бит вправо.
  • 11: Wave Pattern RAM воспроизводится с предварительным сдвигом каждого сэмпла на 2 бита вправо.

Помимо этого в регистре NR30 в бите 7 содержится флаг, разрешающий воспроизведение звука (другие биты не используются). Если он равен 0, то запрещено. Иначе – разрешено. Важно понимать, что этот флаг не имеет однозначного соответствия с битом статуса в регистре NR52 для этого канала. Если бит 7 в NR30 установлен, то звук может быть воспроизведен – бит статуса для этого канала в NR52 так и может остаться нулевым, а звук не будет выводиться. Воспроизведение разрешено, но не запущено. Если же флаг сбрасывается, то это приводит и к отключению канала, что ведет к сбросу бита статуса в NR52.

Таймер в Wave generator работает с периодом 2 * (2048 – X), где X – частота из регистров NRX3 и NRX4. В данном случае частота означает не частоту звука, а частоту, с которой происходит чтение очередного сэмпла из Wave Pattern RAM.

Канал 3 помимо всего прочего содержит указатель на текущий сэмпл и буфер сэмплов – их присутствие в эмуляторе обязательно для точной эмуляции. При очередном отсчете таймера указатель передвигается на новую позицию, а в буфер сэмплов копируется текущий байт (очевидно, что байт будет один и тот же на протяжении каждых двух отсчетов). Далее в дело вступают очередные странности:
  • При перезапуске канала указатель обнуляется, но в буфер сэмплов не копируется первый байт из Wave Pattern RAM – это произойдет только при следующем отсчете таймера. Это означает, что сначала будет проигран первый сэмпл из буфера сэмпла, который все еще содержит старый байт; при следующем отсчете таймера в буфер будет скопирован первый байт из Wave Pattern RAM, указатель передвинут на второй сэмпл, а значит будет проигран второй сэмпл из Wave Pattern RAM. Далее все продолжится как должно. В итоге, при перезапуске первый сэмпл не будет проигран до тех пор, пока вся Wave Pattern RAM не будет проиграна хотя бы один раз.
  • Если канал 3 работает, то игра имеет доступ к Wave Pattern RAM только, если в этот же момент канал 3 производит чтение из нее. Иначе операции чтения возвращают 0xFF, а операции записи игнорируются. Если канал 3 работает и происходит чтение или запись в момент, когда канал 3 читает сэмпл, то операции происходят только на байте, на который указывает указатель текущего сэмпла — не имеет значение, откуда происходит чтение или запись. Операции чтения и записи работают нормально только при отключенном канале 3 – тогда можно читать и записывать в любом месте Wave Pattern RAM.
  • Перезапуск канала 3 во время чтения сэмпла приводит к повреждению четырех первых байт в Wave Pattern RAM. Если указатель текущего сэмпла в пределах первых четырех байтов, то первый байт Wave Pattern RAM будет переписан содержимым буфера сэмплов. Если указатель текущего сэмпла в другой позиции, то будут переписаны все 4 первых байта содержимым той четверки байтов (4-7, 8-11, 12-15), где находится указатель. Например, если указатель на 10 байте, то содержимое первых четырех байт будет переписано байтами 8-11.

С первым пунктом все элементарно. Остальные уже не так уж просто реализовать, тем более, когда в интернете нет никаких упоминаний о тонкостях реализации. Их реализация в CookieBoy – следствие практически случайных попыток манипуляции со счетчиком таймера, перемещающего указатель текущего сэмпла. Вот, что мне удалось раскопать.

И так. Ключ к реализации последних двух пунктов – понимание того, что происходит при перезапуске канала (trigger) посредством регистра NR34. Очевидно, что нам надо сбросить счетчик таймера и указатель текущего сэмпла. Указатель сэмпла сбрасываем согласно первому пункту выше – тут все просто. Со счетчиком все не так просто, тут и кроется ключ к решению проблемы.

Обнуление счетчика при перезапуске канала – это очевидное и неправильное решение. На самом деле счетчик инициализируется таким образом, что происходит задержка перед тем, как начинается обновление позиции текущего сэмпла. Задержка равняется периоду таймера (формулу я уже приводил) плюс некая константа (скорее всего не больше 8 тактов), которую вам придется подобрать самим. Т.е. вместо того, чтобы отсчитать один период и обновить позицию указателя, таймер отсчитывает два периода плюс некая константа. После этого таймер работает в штатном режиме, отсчитывая положенный один период.

Вот как это работает в моем эмуляторе. Переменная ClockCounter является счетчиком тактов. Она имеет знаковый тип. Как только она достигает значения, равного периоду таймера, я обновляю позицию указателя текущего сэмпла и сбрасываю счетчик (вычитаю из него значение периода). При перезапуске канала посредством NR34 я устанавливаю ClockCounter = -Period — 3, где Period – значение периода таймера в тактах согласно приведенной ранее формуле, 3 – та самая магическая константа. Это дает необходимую задержку и позволяет узнать, в какой момент времени можно производить чтение/запись Wave Pattern RAM. Если в момент чтения или записи в Wave Pattern RAM переменная ClockCounter равна 3, то эти операции доступны. Иначе – возвращаем 0xFF.

Теперь указатель сэмплов. При перезапуске я записываю в него 1. Именно эта комбинация задержки и значения указателя сэмплов при перезапуске позволяет пройти тестовые ROM’ы. Не забываем только про тот факт, что второй проигрываемый сэмпл после перезапуска – это второй сэмпл в Wave Pattern RAM. Из-за задержки будет два раза проиграно старое содержимое буфера сэмплов (см. первую странность), а затем третий сэмпл из Wave Pattern RAM. Такова особенность моей реализации, поэтому, как только таймер после перезапуска канала пройдет всю задержку (станет неотрицательным), я обновляю содержимое буфера сэмплов.

С повреждением четырех первых сэмплов все элементарно, только теперь ClockCounter должен быть равен 1, чтобы происходило повреждение и перезапись первых байтов Wave Pattern RAM.

Не забываем, что перезапуск канала – это не просто запись в NR34. Все перечисленное и сам перезапуск происходит только тогда, когда в старший бит NR34 записывается 1 и регистр NR30 разрешает воспроизведение (старший бит установлен).

Звуковой канал 4

Этот канал генерирует шум. К нему подключены Length counter и Envelope unit – их поведение ничем не отличается от такового в других каналах. Под них отведены те же самые регистры – NR41 и NR42 соответственно. Данный канал не содержит частоты в привычном понимании – NR43 используется для совершенно других целей, а NR44 содержит все привычные флаги, но биты под частоту не используются.

Генератор шума основан на так называемом LFSR – Linear Feedback Shift Register или регистр сдвига с линейной обратной связью. Это генератор псевдослучайной битовой последовательности. Принцип его работы достаточно прост.

Регистр сдвига представляет собой хранилище для битовой последовательности определенной длины (в DMG регистр сдвига может иметь длину 7 или 15 бит). Определенные биты регистра сдвига отмечаются как отводы (taps) – именно благодаря им генерируется последовательность. В DMG отводами являются 0 и 1 биты регистра сдвига. Для непрерывной работы LFSR используется тактовый генератор, который генерирует отсчеты для вычисления очередного бита псевдослучайно последовательности.

В начале регистр сдвига инициализируется любой ненулевой битовой последовательностью – если все биты будут равны нулю, то на выходе LFSR мы всегда будем получать ноль. При очередном отсчете происходит следующее:
  1. Отводы суммируются по модулю 2 (операция XOR), и результат сохраняется для дальнейших операций.
  2. Крайний правый бит (бит 0) регистра сдвига берется в качестве очередного значения генерируемой последовательности.
  3. Регистр сдвига сдвигается вправо на один бит.
  4. В освободившийся крайний левый бит записывается результат суммы отводов по модулю 2, который мы сохранили на первом шаге.

На выходе получается псевдослучайная битовая последовательность. Псевдослучайная она по той причине, что она имеет период – с определенного момента вся последовательность зацикливается. Длина периода (T) вычисляется по следующей формуле:

T = 2N – 1,

где N – длина регистра сдвига в битах. Период определяется максимальным числом различных состояний регистра сдвига кроме одного, когда все биты равны нулю. Таким образом, для 7-битного регистра период будет равен 127, а для 15-битового равен 32767. Это приводит нас к вопросу – вычислять все честно или же использовать заранее сгенерированные последовательности. Результат будет идентичен, поскольку LFSR гарантированно зацикливается. Я использовал второй подход. Последовательности можно найти в файлах LFSR7.inc и LFSR15.inc.

Для управления LFSR используется регистр NR43. Вот его структура:
Биты | Назначение
7-4 | Сдвиг частоты таймера: 0000: 1/2 0001: 1/22 0002: 1/23 0003: 1/24 … 1101: 1/214 1110: не используется 1111: не используется
3 | Длина регистра сдвига: 0: 15 бит 1: 7 бит
2-0 | Множитель частоты: 000: 2 001: 1 010: 1/2 011: 1/3 100: 1/4 101: 1/5 110: 1/6 111: 1/7

С длиной регистра сдвига все понятно. Остальные биты используются для вычисления частоты тактового генератора LFSR. Вычисляется она (F) по следующей формуле:

F = f * Shift * Ratio,

где f = 4194304 Гц, Shift – сдвиг частоты таймера (значения указаны в таблице выше), Ratio – множитель частоты (значения указаны в таблице выше). Если биты сдвига частоты равны 1110 или 1111, то LFSR не получает отсчетов, а значит канал 4 заглушен.

Реализация

Для реализации звука я выбрал SDL. Эта библиотека имеет предельно простой API для генерации процедурного звука – указываем параметры звука, длину буфера сэмплов, callback-функцию и все. SDL автоматически вызывает эту функцию, где мы и «скармливаем» ей очередную порцию сэмплов. После их воспроизведения функция вызывается еще раз и т.д. Помимо простого API еще одно преимущество SDL – хорошая работа с предельно малыми буферами сэмплов, а для нас как раз очень важна латентность.

Я не буду вдаваться в подробности реализации самих компонентов звуковой системы. Теоретическая часть содержит все необходимое. Лишь коснусь проблемы синхронизации.

Проблема заключается в том, что теперь нам нужно поддерживать не только темп обновления экрана, но и темп генерации сэмплов. SDL вызывает callback-функцию через равные (хотя гарантий этого в документации я не видел) промежутки времени и «ожидает», что мы запишем новую порцию сэмплов. Если этих сэмплов не будет в нужный момент, то мы получим прерывистый звук. В тоже время, может оказаться, что темп эмуляции слишком высокий и очередные порции сэмплов надо будет где-то сохранить для воспроизведения позже.

Для хранения сэмплов лучше всего подходит кольцевой буфер. Эмулятор пишет в него порции сэмплов, а callback-функция забирает их при необходимости. Кольцевой буфер решает сразу несколько проблем:
  • нам не надо следить за границами массива – данная структура данных все сделает за нас. Бонусом является еще и то, что кольцевой буфер имеет фиксированный размер;
  • хранение порций сэмплов в правильном порядке;
  • упрощение реализации ускоренной эмуляции. Дело в том, что при отсутствии достаточного количества свободного места кольцевой буфер перезаписывает уже имеющиеся данные. При ускоренной эмуляции порции сэмплов будут поступать быстрее, чем мы сможем их воспроизвести. Поскольку мы никого не ждем и пишем поверх старого, то темп звука будет соответствовать темпу эмуляции;
  • поддержание темпа эмуляции. Это несколько противоречит предыдущему пункту, но дело вот в чем. Если поступила очередная порция сэмплов, а в кольцевом буфере нет для нее места, то вместо перезаписи мы ждем, пока callback-функция не заберет из буфера порцию сэмплов. Только после этого мы добавляет очередные сэмплы в буфер и продолжаем эмуляцию.

Последний пункт дает интересный побочный эффект – мы можем полностью отказаться от ручного поддержания темпа эмуляции (60 Гц). Необходимые задержки в эмуляции обеспечит ожидание вызова callback-функции. Для этого в SDL есть условные переменные (SDL_cond). С помощью них поток переходит в режим ожидания и ждет сигнала от другого потока о том, что можно продолжить работу. Для нас ждущим потоком является поток эмуляции – он ждет, когда другой поток (callback-функция) заберет из кольцевого буфера сэмплы и тем самым освободит место для очередной порции. Когда же нам нужна максимально возможная скорость эмуляции, мы никого не ждем и пишем в кольцевой буфер. Естественно не забываем про мьютексы.

Все так хорошо работает по одной простой причине – генерация сэмплов происходит с тем же темпом, с каким работает процессор DMG.

Тестирование

Как и для других компонентов, для звука тоже есть тестовые ROM’ы. Есть только одна хитрость – для DMG и Gameboy Color комплекты тестов разные и запускать стоит их все. Тесты DMG должны быть пройдены без ошибок, а вот тесты для Gameboy Color реальный DMG проходит с ошибками и выводит на экран следующее:

Если при запуске всех тестов сразу их выполнение не прекращается, а зацикливается, то беспокоиться не о чем. Это именно тот случай, когда ROM пытается установить банк ROM’а, которого не существует. Если обрезать номер банка, как это делаю я, то тесты зацикливаются. Тоже самое наблюдается и у Gambatte, а ему доверять можно.

Рекомендую сразу вооружиться исходным кодом тестов и разобраться в том, как они работают. Это намного ускорит процесс, а иной раз это единственный способ понять, что вам нужно сделать. На экран хоть и выводится описание ошибки, но порой сложно понять, что конкретно от вас требуется и какие регистры задействованы.

И так, самые простые тесты – это «01-registers» и «11-regs after power». Первый тестирует то, как происходит чтение и запись регистров звука. Я уже упоминал про то, как стоит учитывать неиспользуемые и недоступные для чтения биты регистров – именно это и проверяет тест. Помимо этого, проверяются результаты операций при вкл/выкл звуке. Второй тестирует поведение регистров при выключении звука. В исходном коде написано подробнее, что конкретно тестируется.

«02-len ctr» тестирует поведение Length counter в граничных условиях. Тестирование происходит для каждого канала отдельно, в процессе выводится номер протестированного канала.

«03-trigger» еще один тест для Length counter, но теперь проверяется его поведение при модификации регистра NRX4. В основном, именно здесь тестируются все упомянутые странности Length counter.

«04-sweep», «05-sweep details» и «06-overflow on trigger» тестируют Sweep unit. Помимо нормальной работы здесь тестируются все упомянутые странности компонента. Для прохождения теста «06-overflow on trigger» в ходе него на экран должно быть выведено:

«07-len sweep period sync» тестирует правильность синхронизации Sweep unit и Length counter. Если Frame Sequencer реализован правильно, то проблем с этим тестом быть не должно.

«08-len ctr during power» тестирует поведение Length counter при выключении звука. Для прохождения теста в ходе него на экране должно быть выведено:

«09-wave read while on» тестирует операции чтения во время работы канала 3. Для прохождения теста на экране должно быть выведено:

На экран через раз выводится значение из Wave Pattern RAM и FF из-за запрета на чтение. Они чередуются (00 FF, 11 FF и т.д.), но в начале операция чтения не проходит два раза и выводится FF FF. Как раз этого добиться сложнее всего, и потребовало у меня перебора разных значений константы и счетчика (определение момента, когда операция чтения разрешена).

«10-wave trigger while on» тестирует повреждение первых байтов Wave Pattern RAM при перезапуске канала 3 во время чтения сэмпла. На экран выводится очень много информации, и полностью она не помещается. Вот результат пройденного теста:

Даже по этому куску видно, где должно происходить повреждение Wave Pattern RAM.

«12-wave write while on» тестирует операции записи в Wave Pattern RAM при работе канала 3. Тут тоже выводится слишком много информации, вот результат пройденного теста:

Вполне видно, где и что искать – тест пытается записать значение 0xF7.

Стоит сказать, что прохождение этих тестов лично мне ничего не дало в протестированных мной играх. И без их прохождения звук был нормальный, а судя по другим эмуляторам с завидной совместимостью, проваливающих все тесты звука – это вообще не нужно. Хотя приятно осознавать, что эмулятор работает так, как реальное железо. Если и попадется игра, зависимая от тонкостей железа, то она будет работать корректно.

Что дальше

На данный момент наш эмулятор поддерживает все обязательные и не совсем функции DMG. Естественно есть куда развиваться, а именно:
  • Поддержка последовательного порта или Game link.
  • Поддержка малораспространенных MBC-контроллеров – Pocket Camera, Bandai TAMA5, Hudson HUC-3, Hudson HUC-1, картриджи с вибрацией.

Напоследок остается одна особенность DMG, которую ни один из мне известных эмуляторов не поддерживает, даже самые точные вроде Gambatte. Это баг железа, который приводит к записи мусора в область памяти OAM. Эта особенность практически никак не документирована и довольно коварна в реализации.

Баг происходит при исполнении определенных инструкций процессора с определенными значениями операндов. Это простая часть. Сложнее эмулировать тот факт, что мусор записывается совсем не случайно и имеет определенное содержание. Еще сложнее эмулировать то, что данный баг происходит только в определенные промежутки времени работы LCD-контроллера. Именно для этих деталей я не нашел никакой документации – есть только тестовые ROM’ы, которые не очень помогают. С ними можно реализовать этот баг лишь частично.

Смысла в реализации этого бага минимум – вряд ли какой-то разработчик сознательно его использовал или оставил в релизе игры. Теже баги звука из-за Wave Pattern RAM встречаются в играх, и в них есть практический смысл.

Заключение

На этом цикл статей подошел к концу. Как я и говорил, итогом является эмулятор с хорошей совместимостью и поддержкой всех самых важных функций. Такой не стыдно поставить рядом с другими реализациями. А благодаря использованию тестовых ROM'ов для реализации и тестирования можно говорить о высокой точности эмуляции железа, а не простой совместимости с играми.

Естественно, эмулятору есть куда развиваться. Помимо упомянутого, есть еще одно логичное направление развития эмулятора — эмуляция Gameboy Color (СGB). DMG и CGB являются не просто консолями одного семейства — они практически идентичны внутренне. К существующему эмулятору необходимо дописать буквально несколько модулей.
На данный момент Cookieboy не эмулирует CGB, но я планирую заняться этим в ближайшее время. Об этом тоже будет статья.

Если кто-то решится на реализацию своего эмулятора или же что-то непонятно в самих статьях, то можете обращаться с вопросами, буду рад помочь. В комментариях к статьям или в хабрапочту — неважно.
  • эмулятор
  • , gameboy
  • , DMG
  • , C++
+27
6093
85
creker 18,5
Автоматизированное
продвижение сайтов
  • 50% экономии на ссылках
  • Запуск проекта за 10 минут
  • Вывод и удержание в ТОП 10
Подробнее
Похожие публикации
Об удобной навигации и отладке C++ кода в Vim 13 декабря в 10:20
Чтение SVG в C++ 10 декабря в 18:27
Сводная таблица по поддержке C++ 11/14/17 7 декабря в 08:26
Рефакторинг C++ кода в Visual Studio 2015 18 ноября в 11:29
Идеальная передача и универсальные ссылки в C++ 8 ноября в 23:37
Интервью Скотта Мейерса в Яндексе. О настоящем и будущем C++ 28 октября в 15:08
Библиотека Strutext обработки текстов на языке C++ 27 октября в 16:18
Разработка трехмерных игр для Windows 8 с помощью C++ и Microsoft DirectX 27 октября в 09:27
Пишем эмулятор Gameboy, часть 2 22 октября 2012 в 13:29
Пишем эмулятор Gameboy, часть 1 16 октября 2012 в 12:00

Комментарии (4)

0
qw1 30 октября 2012 в 21:47 #
Мне интересно, как вы реализовали преобразование последовательности пар (номер_такта, амплитуда) в сигнал частотой 44100 Hz. Ведь за один семпл звукового результирующего сигнала переключений может произойти несколько
0
creker 30 октября 2012 в 22:32 #
↵
↑
Я не совсем понял вопрос, но попробую ответить.

Говоря о том, что сэмплы генерируются с с тем же темпом, что и процессор, я имел ввиду следующее. Я завел счетчик с периодом в тактах, равным 4194304/{частота дискретизации}, т.е. частота процессора деленная на частоту дискретизации. Он так же как и все остальные компоненты синхронизирован с процессором. Каждый его отсчет генерирует два сэмпла — для левого и правого уха. Звуковые каналы в процессе работы хранят свое выходное значение амплитуды — его я и забираю при каждом отсчете. Таким образом сэмплы накапливаются в буфер сэмплов, который потом вставляется в кольцевой буфер. Оттуда SDL забирает их на воспроизведение.

По сути, получается довольно грубая передискретизация (resampling).
0
qw1 31 октября 2012 в 01:04 #
↵
↑
Понятно, алгоритм Nearest-neighbor.
0
ogamespec 20 января 2014 в 15:54 #
Может кого заинтересует:

forum.emu-russia.net/viewtopic.php?t=5244

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Метки лучше разделять запятой. Например: общение, социальные сети, myspace.com, подростки, мердок
или закрыть
Популярное за сутки
Светильник декоративный бытовой СДБ-З «Евлампия»
Бизнес не по-детски — Или как восьмиклассник развозил фрукты?
Бесплатная отправка SMS из Arduino без GSM модуля
Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №138 (8 — 14 декабря 2014)
Как сделать греческие буквы в формулах прямым шрифтом в LaTeX
Конференция C++ Russia в Москве
Конференция ZeroNights 2014 — как все было
Обзор наиболее интересных материалов по анализу данных и машинному обучению №26 (8 — 14 декабря 2014)
Сказ о том, как мы в хакатонах участвовали, или Страх и Ненависть в Кремниевой Долине
Публичный тест нового интерфейса HeadHunter
Как добавить карты Bing Maps в Windows-приложение на HTML и JavaScript. Часть 2
Электрофокусер на базе отладочной платы Arduino Uno, часть 3
Replication slots в PostgreSQL
Composer. Небезопасно использовать packagist и приватный источник пакетов одновременно
5 ошибок при обратной связи с пользователями
Моделируем преднапряжение бетонной оболочки АЭС
Обзор возможностей App Annie для Windows и Windows Phone разработчиков
Диагностика проблем после инсталляции или апгрейда ORACLE APEX
Concurrency структуры в .net. ConcurrentQueue изнутри
MyLifeOrganized: папки vs контексты
все лучшие
Лучшее на Geektimes
Нефтерубль
Новогодние космические подарки
Роскосмос планирует построить лунную базу, наладить промышленность и добычу ископаемых
В погоне за синим светодиодом
Сбывшиеся мечты Роберта Бойля. Часть 1
На космическом корабле Orion используется процессор 12-летней давности
Паровые машины… и как появились «лошадиные силы»
Вымрет ли человечество «тепловой смертью»?
Samsung и Apple опять повышают цены в России
Yahoo призывает переходить на Firefox
все посты
Вопросы по теме
Почему не корректно работает make.exe?
Linking GTK-3.0 C++ app with MinGW: undefined reference to `WinMain@16'?
Почему c++ программ не видит файл при запуске вне xcode?
Как работать с кириллицей в c++?
Как рационально сосчитать парники (задача об огурцах)?
Как перекинуть указатель в glfwSetKeyCallback на си?
Можно ли выяснить, содержит ли указатель адрес допустимого объекта или нет?
Как создать два процесса,в котором один отсылает события, а другой распознает их и обрабатывает?
Как в С++ генерировать последовательность чисел без повторов?
Как сгенерировать уникальный число int64 ?
Какие функции использовать чтобы работать с элементами чужого окна?
Как послать emal через c++ ?
На чем написать программу для работы с чужим окном?
Как определить первый и последний сегмент в сырых TCP пакетах при IP фрагментации?
Как лучше всего создать свой красивый tray notification?
Инструмент для тестирования программной комбинационной схемы (алгоритма)?
С++ проект под Android. С чего начать?
Как передать указатель на порт Serial в библиотеку Arduino и ОДНОКРАТНО создать на него ссылку внутри всей библиотеки?
Что лучше выбрать для разработок?
Что не так с моим кодом?
Что обсуждают?
Установка, настройка и тестирование Fedora 21 Workstation на личном или игровом компьютере 42
Публичный тест нового интерфейса HeadHunter 47
Бизнес не по-детски — Или как восьмиклассник развозил фрукты? 13
Пару слов об интеграции в TFS системы управления дефектами с системой управления версиями 144
Об удобной навигации и отладке C++ кода в Vim 80
Если заказчик — редакция 17
Установка BIND9 DNS на CentOS 1
Google Chrome пометит HTTP-сайты как небезопасные 150
Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №138 (8 — 14 декабря 2014) 2
Конференция C++ Russia в Москве 3
все публикации
Компания дня
КРОК

Последняя публикация: Как мы монтируем ДДИБП: огромные маховики в дата-центрах и средство аварийного резерва ответственных объектов

2019 подписчиков
Какой язык программирования больше всего любят в Яндексе? И всегда ли любовь взаимна
Встречайте, IntelliJ IDEA 14 уже здесь
Детекторы углов
Brainstorage
Android Developer
Python разработчик
PHP - программист
Web Designer
PHP developer
JavaScript разработчик (Middle level)
Junior Web Developer (JavaScript, Node.js)
PHP программист
Ведущий веб-разработчик
Ruby on Rails разработчик для постоянных случайных рабочих связей
все вакансии
Nodejs/C++ developer, разработка модуля
Доделать сайт на open card
Добавить пользовательское поле в корпортал Битрикс
Выполнить интеграцию двух страниц на Yii в существующий проект
Поиск дыр в безопасности на WP
Печать бирок для одежды (Москва)
Добавить боковое меню в корпоративный портал Битрикс
Андроид-разработка (серверный менеджер задач)
Дизайн Landing'a в FLAT
Создание страницы продукта по аналитике (дизайн+верстка)
все заказы
Войти
Регистрация
Разделы
Публикации
Хабы
Компании
Пользователи
Q&A
Песочница
Инфо
О сайте
Правила
Помощь
Соглашение
Услуги
Реклама
Тарифы
Семинары
Спецпроекты
Разное
Приложения
Тест-драйвы
Помощь стартапам
TM © 2006–2014

Служба поддержки

Мобильная версия


Programmer-Net : The home of all programming information on the net

Wayback Machine
ДЕК | ИЮЛ | авг
Previous capture | 21 | Next capture
2008 | 2013 | 2014
36 captures
24 янв 01 - 21 июл 13
sparklines
Close Help

NEWS
+ Current News

GAMEBOY
+ Emulators
 + Tutorials
 + Cribsheet
 - Opcodes and Timings
 - Memory Map
 - Cartridge Header
 - Hardware Registers
 + Downloads
 + Links

PC
+ Source Code
- C++
- ASM
+ Links

CONTACT
+ Adrian@enliten.force9.co.uk
+ ICQ 8715274

You are visitor

Since 21st May 2000

Copyright (c) 1999, 2000 Adrian Brown
If you have any complaints about this website or its contents, please email me and I will remove or update the offending section.

Gameboy Opcodes and Timings

The GameBoy is based on the Zilog Z80 processor. There are seven main registers (A, B, C, D, E, H, L) all are 8bit registers, six of these can pair together to make three 16bit registers (BC, DE, HL). There is also a Flags (F) register, which for the purposes of some commands is paired with the A register to make a 16bit register (AF) , a Stack Pointer and a Program Counter. Following is a table showing these as well as the bits of the flags register.

REGISTERS

HIGH BYTE (bits 15 - 8 if paired) | LOW BYTE (bits 7 - 0) | DESCRIPTION
A | F | Accumulator / Flags registers
B | C | General Purpose Registers
D | E | General Purpose Registers
H | L | General Purpose Registers - Usually a memory pointer
SP |  | Stack Pointer
PC |  | Program Counter

FLAGS REGISTER

BIT NUMBER | FLAG | DESCRIPTION
7 | Z | Zero Flag. Set if the last math operation resulted in a zero
6 | N | Subtract Flag. Set if the last math operation involved a subtraction
5 | H | Half Carry Flag. Set if the last math operation cause a carry from the lower nibble (bit 3-4)
4 | C | Carry Flag. Set if the last math operation cause a carry (bit 7-8)
3 | X | Not Used
2 | X | Not Used
1 | X | Not Used
0 | X | Not Used

COMMAND DESCRIPTIONS

ADC x, y ADD x, y AND x | Add y + CF to x Add y to x And x to A
BIT b, x | Test bit b of x
CALL c, x CALL x CCF CP x CPL | If condition c is true, call subroutine x Call subroutine x Complement carry flag Compare A with x Complement A
DAA DEC x DI | Decimal adjust A Decrement x by 1 Disable interrupts
EI | Enable interrupts
HALT | Halt (wait for interrupt or reset)
INC x | Increment x by 1
JP c, x JP x JR c, d JR d | If condition c is true jump to location x Jump to location x If condition c is true jump relative by d Jump relative by d
LD x, y LDD x, y LDI x, y | Load x with y Load A with (HL), DEC HL Load A with (HL), INC HL
NOP | No operation
OP x | OR x to A
POP x PUSH x | Pop x from the top of the stack, updating SP Push x onto the top of the stack, updating SP
RES b, x RET RET c RETI RST x | Reset bit b of x Return from subroutine If condition c is true, return from subroutine Return from interrupt Call subbroutine at x (x = 00h, 08h, 10h, 18h, 20h, 28h, 30h, 38h)
SBC x SCF SET b, x STOP SUB x SWAP x | Subtract y + CF from x Set carry flag Set bit b of x Stop CPU until P1-P10 go high Subtract x from A Swap register nibbles
XOR x | XOR x to A

COMMANDS

Please note that the timings for the instructions are given in Machine Cycles, to convert these to Clock Cycles just multiply the number by four. The four columns Z,N,H,C refer to the flags in the flags register, 0 means reset, 1 means set, X means set as expected (i.e. if the result is zero then the zero flags would be set, else reset) and blank means it is not effected.

 | Op-code | Destination | Source | Z | N | H | C | M/C | Bytes
A | ADC A, (HL) ADC A, n8 ADC A, r8 | A A A | (HL) 8bit integer A, B, C, D, E, H, L | X X X | 0 0 0 | X X X | X X X | 2 2 1 | 1 2 1
 | ADD A,(HL) ADD A, n8 ADD A, r8 ADD HL, r16 ADD SP, e8 | A A A HL SP | (HL) 8bit integer A, B, C, D, E, H, L BC, DE, SP 8bit offset | X X X 0 | 0 0 0 0 0 | X X X X X | X X X X X | 2 2 1 2 4 | 1 2 1 1 2
 | AND (HL) AND n8 AND r8 | A A A | (HL) 8bit integer A, B, C, D, E, H, L | X X X | 0 0 0 | 1 1 1 | 0 0 0 | 2 2 1 | 1 2 1
B | BIT n3, (HL) BIT n3, r8 | Zero Flag Zero Flag | (HL) A, B, C, D, E, H, L | X X | 0 0 | 1 1 |  | 3 2 | 2 2
C | CALL cc, n16 CALL n16 | PC PC | 16bit address 16bit address |  |  |  |  | 6/3 6 | 3 3
 | CCF | Carry Flag |  |  | 0 | 0 | X | 1 | 1
 | CP (HL) CP n8 CP r8 | Flags Flags Flags | (HL) 8bit integer A, B, C, D, E, H, L | X X X | 1 1 1 | X X X | X X X | 2 2 1 | 1 2 1
 | CPL | A | A |  | 1 | 1 |  | 1 | 1
D | DAA | A | A | X |  | 0 | X | 1 | 1
 | DEC (HL) DEC r16 DEC r8 | (HL) BC, DE, HL, SP A, B, C, D, E, H, L | (HL) BC, DE, HL, SP A, B, C, D, E, H, L | X X | 1 1 | X X |  | 3 2 1 | 1 1 1
 | DI |  |  |  |  |  |  | 1 | 1
E | EI |  |  |  |  |  |  | 1 | 1
H | HALT |  |  |  |  |  |  | 1 | 1
I | INC (HL) INC r16 INC r8 | (HL) BC, DE, HL, SP A, B, C, D, E, H, L | (HL) BC, DE, HL, SP A, B, C, D, E, H, L | X X | 0 0 | X X |  | 3 2 1 | 1 1 1
J | JP (HL) JP cc, n16 | PC PC | (HL) 16bit address |  |  |  |  | 1 4/3 | 1 3
 | JP n16 JR cc, e8 JR e8 | PC PC PC | 16bit address 8bit offset 8bit offset |  |  |  |  | 4 3/2 3 | 3 2 2
L | LD (C), A LD (HL), n8 LD (HL), r8 LD (n16), A LD (n16), SP LD (r16), A LD A, (C) LD A, (n16) LD A, (r16) LD HL, SP + e8 LD r16, n16 LD r8, (HL) LD r8, n8 LD r8, r8 LD SP, HL | (C) (HL) (HL) (16bit address) (16bit address) (BC), (DE), (HL) A A A HL BC, DE, HL, SP A, B, C, D, E, H, L A, B, C, D, E, H, L A, B, C, D, E, H, L SP | A 8bit integer A, B, C, D, E, H, L A SP A (C) (16bit address) (BC), (DE), (HL) SP + 8bit offset 16bit integer (HL) 8bit integer A, B, C, D, E, H, L HL | 0 | 0 | X | X | 2 3 2 4 5 2 2 4 2 3 3 2 2 1 2 | 1 2 1 3 3 1 1 3 1 2 3 1 2 1 1
 | LDD (HL), A LDD A, (HL) | (HL) A | A (HL) |  |  |  |  | 2 2 | 1 1
 | LDH (e8), A LDH A, (e8) | (8bit offset) A | A (8bit offset) |  |  |  |  | 3 3 | 2 2
 | LDI (HL), A LDI A, (HL) | (HL) A | A (HL) |  |  |  |  | 2 2 | 1 1
N | NOP |  |  |  |  |  |  | 1 | 1
O | OR (HL) OR n8 OR r8 | A A A | (HL) 8bit integer A, B, C, D, E, H, L | X X X | 0 0 0 | 0 0 0 | 0 0 0 | 2 2 1 | 1 2 1
P | POP r16 | AF, BC, DE, HL | (SP) |  |  |  |  | 3 | 1
 | PUSH r16 | (SP) | AF, BC, DE, HL |  |  |  |  | 4 | 1
R | RES n3, (HL) RES n3, r8 | Bit in memory Bit in register | (HL) A, B, C, D, E, H, L |  |  |  |  | 3 2 | 2 2
 | RET RET cc | PC PC | Condition Flag |  |  |  |  | 4 5/2 | 1 1
 | RETI | PC |  |  |  |  |  | 4 | 1
 | RL (HL) RL r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | X X | 4 2 | 2 2
 | RLA | A | A | 0 | 0 | 0 | X | 1 | 1
 | RLC (HL) RLC r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | X X | 4 2 | 2 2
 | RLCA | A | A | 0 | 0 | 0 | X | 1 | 1
 | RR (HL) RR r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | X X | 4 2 | 2 2
 | RRA | A | A | 0 | 0 | 0 | X | 1 | 1
 | RRC (HL) RRC r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | X X | 4 2 | 2 2
 | RRCA | A | A | 0 | 0 | 0 | X | 1 | 1
 | RST f | PC |  |  |  |  |  | 4 | 1
S | SBC A, (HL) SBC A, n8 SBC A, r8 | A A A | (HL) 8bit integer A, B, C, D, E, H, L | X X X | 1 1 1 | X X X | X X X | 2 2 1 | 1 2 1
 | SCF | Carry Flag |  |  | 0 | 0 | 1 | 1 | 1
 | SET n3, (HL) SET n3, r8 | Bit in memory Bit in memory | (HL) A, B, C, D, E, H, L |  |  |  |  | 3 2 | 2 2
 | SLA (HL) SLA r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | X X | 4 2 | 2 2
 | SRA (HL) SRA r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | X X | 4 2 | 2 2
 | SRL (HL) SRL r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | X X | 4 2 | 2 2
 | STOP |  |  |  |  |  |  | 1 | 2
 | SUB (HL) SUB n8 SUB r8 | A A A | (HL) 8bit integer A, B, C, D, E, H, L | X X X | 1 1 1 | X X X | X X X | 2 2 1 | 1 2 1
 | SWAP (HL) SWAP r8 | (HL) A, B, C, D, E, H, L | (HL) A, B, C, D, E, H, L | X X | 0 0 | 0 0 | 0 0 | 4 2 | 2 2
X | XOR (HL) XOR n8 XOR r8 | A A A | (HL) 8bit integer A, B, C, D, E, H, L | X X X | 0 0 0 | 0 0 0 | 0 0 0 | 2 2 1 | 1 2 1


Programmer-Net : The home of all programming information on the net

Wayback Machine
ОКТ | ИЮЛ | авг
Previous capture | 21 | Next capture
2008 | 2013 | 2014
33 captures
24 янв 01 - 21 июл 13
sparklines
Close Help

NEWS
+ Current News

GAMEBOY
+ Emulators
 + Tutorials
 + Cribsheet
 - Opcodes and Timings
 - Memory Map
 - Cartridge Header
 - Hardware Registers
 + Downloads
 + Links

PC
+ Source Code
- C++
- ASM
+ Links

CONTACT
+ Adrian@enliten.force9.co.uk
+ ICQ 8715274

You are visitor

Since 21st May 2000

Copyright (c) 1999, 2000 Adrian Brown
If you have any complaints about this website or its contents, please email me and I will remove or update the offending section.

Gameboy Memory Map

The Gameboy has 64k of memory that is accessable at any one time, this is split up into sections such as ROM, RAM and hardware ports. Follows is a breakdown of the different sections of memory.

MEMORY ADDRESS (in Hex) | CONTENTS
FFFF - FFFF | Interrupt Enable Flag (see below)
FF80 - FFFE | Zero Page (127 bytes)
FF00 - FF7F | Hardware Registers (see Hardware section)
FEA0 - FEFF | Unusable Memory
FE00 - FE9F | Object Attribute Memory (OAM)
E000 - FDFF | Echo RAM (Reserved - Not Usable)
D000 - DFFF | Internal RAM - Banks 1 - 7 (CGB is switchable as it has 32k Internal RAM)
C000 - CFFF | Internal RAM - Bank 0 (fixed)
A000 - BFFF | Switchable 8k Game Cartridge RAM
9C00 - 9FFF | Background Display Data 2, Tile Indices/Attributes (bank switched)
9800 - 9BFF | Background Display Data 1, Tile Indices/Attributes (bank switched)
8000 - 97FF | Bank 0 and 1 Character Data (bank switched)
4000 - 7FFF | User Program Area - Bank 1 to n (switchable)
0150 - 3FFF | User Program Area - Bank 0 (fixed)
0100 - 014F | Cartridge Header (see Cart. Header section)
0000 - 00FF | Interrupt vectors and RST # vectors

The different sections of memory have different attributes. Some of the areas can only be written to while others can only be read from. The following table shows which areas have which attributes.

 | DESCRIPTION | ATTRIBUTES | MEMORY
VRAM Memory Map | Tile Map 2 | Read / Write | 9C00 - 9FFF
 | Tile Map 1 | Read / Write | 9800 - 9BFF
 | Tiles 00 - 7F (FF40, bit 4 = 0) | Read / Write | 9000 - 97FF
 | Tiles 80 - FF | Read / Write | 8800 - 8FFF
 | Tiles 00 - 7F (FF40, bit 4 = 1) | Read / Write | 8000 - 87FF
Memory Map (Read / Write) | Interrupt Enable | Read / Write | FFFF - FFFF
 | High RAM | Read / Write | FF80 - FFFE
 | I/O Registers | Read / Write | FF00 - FF7F
 | OAM RAM | Read / Write | FE00 - FE9F
 | Low RAM | Read / Write | C000 - DFFF
 | Cartridge RAM | Read / Write | A000 - BFFF
 | Video RAM | Read / Write | 8000 - 9FFF
Memory Map (Read Only) | ROM Bank #1 to n | Read Only | 4000 - 7FFF
 | ROM Bank #0 | Read Only | 0000 - 3FFF
VRAM Memory Map (Write Only) | RAM/ROM Select (MBC1) | Write Only | 6000 - 7FFF
 | RAM Bank Select | Write Only | 4000 - 5FFF
 | ROM Bank Select MSB (MBC5) | Write Only | 3000 - 3FFF
 | ROM Bank Select LSB | Write Only | 2000 - 2FFF
 | RAM Bank Enable | Write Only | 0000 - 1FFF


Imran Nazar: GameBoy Emulation in JavaScript: Interrupts

Skip to navigation

This is part 8 of an article series on emulation development in JavaScript; ten parts are currently available, and others are expected to follow.

  • Part 1: The CPU
  • Part 2: Memory
  • Part 3: GPU Timings
  • Part 4: Graphics
  • Part 5: Integration
  • Part 6: Input
  • Part 7: Sprites
  • Part 8: Interrupts
  • Part 9: Memory Banking
  • Part 10: Timers

Please note: This article has been updated to remove an incorrect interrupt handling procedure. --12th Nov, 2010

In the previous part, the foundations for simulating a game were laid, with the introduction of sprites. However, one aspect was missing from the emulator: the vertical blanking interrupt. In this part, interrupts as a whole will be introduced, and the blanking interrupt in particular will be implemented; once this has been done, the emulator will run Tetris.

{1418644362964ms} [GPU] Initialising screen.
{1418644363000ms} [GPU] Reset.
{1418644363001ms} [MMU] Reset.
{1418644363001ms} [Z80] Reset.

Reset | Run
Figure 1: jsGB implementation with vertical blanking interrupt

Imagine that you have a computer with a network card, and some software that processes data from the network. From the perspective of the computer, data only comes in every so often, so you need some way for the software to know that new data has arrived. There are two ways for this to happen:

  • Polling: The software asks the network card every so often whether new data has arrived. This is a simplistic way of doing things, but has disadvantages:
    • The software doesn't know about new data until its periodic check, which means a delay between the data arriving at the computer and it being handled by the software;
    • Time has to be taken out on a periodic basis for the checks to be made, taking time away from other work even if no data has arrived;
    • If the polling process handles one piece of new data each time it checks, but data is arriving at a faster rate, a backlog of data is created in the network card, and there's potential for some data to be lost;
    • If there is no other work to be done, the software still has to check for data, which keeps the computer running at full speed with no work to do.
  • Interrupts: The network card informs the software that new data has arrived. This is a more complicated way of receiving data, with more steps involved, but it alleviates all the disadvantages of polling:
    • New data can be processed as soon as it arrives, with no delay between arrival and the data being handled;
    • The software need only take time to handle data when there's definitely data to be handled, and the processing routines can be called as often as necessary to clear any backlogs;
    • If there is no other work to be done, the computer can enter a low-power mode until the network card awakens it for new data.

Interrupts and interrupt handlers

It's obvious that the concept of interrupts is a useful one, but there are both hardware and software requirements for interrupts to work. In hardware terms, the CPU has to temporarily stop execution of what it's doing when an interrupt arrives, and instead begin execution of an interrupt handler (sometimes referred to as an Interrupt Service Routine). In the above scenario, a wire is run between the network card and the CPU, allowing the card to inform the CPU when data has arrived.

Hardware implementation of interrupts Figure 2: Hardware implementation of interrupts

The CPU will check its interrupt inputs at the end of every instruction. If an interrupt signal has been given by some attached peripheral like the network card, steps are taken by the CPU to start the interrupt handler: the CPU will save the location where it left off normal execution, register the fact that the interrupt happened, and jump across to the handler.

CPU interrupt handling Figure 3: CPU interrupt handling procedure

In the GameBoy, there are five different interrupt wires, feeding in from the various peripherals. Each one has its own ISR at a different address in memory; the list of interrupts is as follows.

Interrupt | ISR address (hex)
Vertical blank | 0040
LCD status triggers | 0048
Timer overflow | 0050
Serial link | 0058
Joypad press | 0060
Table 1: Interrupts in the GameBoy

In the case of the vertical blank, a wire is threaded into the bottom of the LCD; as soon as the GPU has finished scanning all the LCD lines and runs into the bottom of the screen, the interrupt fires and the CPU jumps to 0040, executing the blanking ISR.

Implementation: Interrupt flags

Most CPUs contain a "master flag" for interrupts: they will only be handled by the CPU if this flag is enabled. The Z80 in the GameBoy is no exception, but there are additional registers that deal with the individual interrupts available in the GameBoy. These are memory registers, so they are handled by the memory management unit:

Register Location Notes Details
Interrupt
enable 		FFFF When bits are set,
the corresponding
interrupt can
be triggered
Bit | When 0 | When 1
0 | Vblank off | Vblank on
1 | LCD stat off | LCD stat on
2 | Timer off | Timer on
3 | Serial off | Serial on
4 | Joypad off | Joypad on
Interrupt
flags 		FF0F When bits are set,
an interrupt
has happened 		Bits in the same
order as FFFF
Table 2: Interrupt flags in the MMU

Since these are memory registers, their implementation is something for the MMU:

MMU.js: Interrupt flags

MMU = {
    _ie: 0,
    _if: 0,

    rb: function(addr)
    {
    switch(addr & 0xF000)
    {
        ...
        case 0xF000:
            switch(addr & 0x0F00)
        {
            ...
            // Zero-page
            case 0xF00:
                if(addr == 0xFFFF)
            {
                return MMU._ie;
            }
                else if(addr >= 0xFF80)
            {
                return MMU._zram[addr & 0x7F];
            }
            else
            {
                // I/O control handling
                switch(addr & 0x00F0)
                {
                    case 0x00:
                    if(addr == 0xFF0F) return MMU._if;
                    break;
                    ...
                }
                return 0;
            }
        }
    }
    },
    ...
};

The Z80's "master enable" switch is, in a similar manner, something for the Z80 implementation. The CPU provides opcodes for software to flick the master enable into either On or Off position, so these will also need to be implemented:

Z80.js: Interrupt master enable

Z80 = {
    _r: {
        ime: 0,
        ...
    },

    reset: function()
    {
        ...
    Z80._r.ime = 1;
    },

    // Disable IME
    DI: function()
    {
        Z80._r.ime = 0;
    Z80._r.m = 1;
    Z80._r.t = 4;
    },

    // Enable IME
    EI: function()
    {
        Z80._r.ime = 1;
    Z80._r.m = 1;
    Z80._r.t = 4;
    }
};

Implementation: Interrupt handling

With the interrupt flags in place, the main execution loop can be redeveloped, to fall more in line with the execution path from figure 3. After execution, the interrupt flags need checking to see whether an enabled interrupt has occurred; if it has, its handler can be called.

Z80.js: Vblank interrupt handler

Z80 = {
    _ops: {
    ...

        // Start vblank handler (0040h)
        RST40: function()
        {
            // Disable further interrupts
            Z80._r.ime = 0;

            // Save current SP on the stack
            Z80._r.sp -= 2;
            MMU.ww(Z80._r.sp, Z80._r.pc);

            // Jump to handler
            Z80._r.pc = 0x0040;
        Z80._r.m = 3;
            Z80._r.t = 12;
        },

        // Return from interrupt (called by handler)
        RETI: function()
        {
        // Restore interrupts
        Z80._r.ime = 1;

        // Jump to the address on the stack
        Z80._r.pc = MMU.rw(Z80._r.sp);
        Z80._r.sp += 2;

        Z80._r.m = 3;
        Z80._r.t = 12;
        }
    }
};

while(true)
{
    // Run execute for this instruction
    var op = MMU.rc(Z80._r.pc++);
    Z80._map[op]();
    Z80._r.pc &= 65535;
    Z80._clock.m += Z80._r.m;
    Z80._clock.t += Z80._r.t;
    Z80._r.m = 0;
    Z80._r.t = 0;

    // If IME is on, and some interrupts are enabled in IE, and
    // an interrupt flag is set, handle the interrupt
    if(Z80._r.ime && MMU._ie && MMU._if)
    {
        // Mask off ints that aren't enabled
        var ifired = MMU._ie & MMU._if;

    if(ifired & 0x01)
    {
        MMU._if &= (255 - 0x01);
        Z80._ops.RST40();
    }
    }

    Z80._clock.m += Z80._r.m;
    Z80._clock.t += Z80._r.t;
}

Next time: Bigger games

As shown in Figure 1, the emulator has reached a reasonable stage: it's able to emulate a released game in at least some form. It does, however, have the problem of game size. Tetris is a 32kB ROM, and fits perfectly into the "ROM" space in the memory map. Games tend to have larger ROMs than this, and the cartridge follows a process of mapping portions of the ROM into memory. Next time, I'll look at the simplest available form of ROM mapping for the GameBoy, and its implementation on a 64kB game ROM.

Imran Nazar <tf@imrannazar.com>, Nov 2010.

Article dated: 5th Nov 2010

Operated by Imran Nazar Ltd, registered in the UK (#07698370). Content copyright Imran Nazar, 2005-2014.
Design and imagery copyright Imran Nazar, 2008-2011; "Parchment" used by license from sxc.
  • Articles
  • Programming
  • Fiction
  • Get in touch
Get the RSS feed


The Lush Engine Tutorials - Gameboy colour - Palettes

Wayback Machine
мар | АПР | ИЮН
Previous capture | 16 | Next capture
1999 | 2000 | 2001
23 captures
16 апр 00 - 29 дек 11
sparklines
Close Help

The Lush Engine

Introduction
General Assembler
Mono Gameboy
Gameboy Colour
Introduction
Palettes
Reference

Gameboy Colour Palettes

Title :Gameboy Colour Palettes
Overview :An explanation of how the colour palettes work on the Gameboy Colour.
Audience :Gameboy Colour programmers.
Requirements :An understanding of binary number systems and a general understanding of how to program the Gameboy.
Objectives :Make the reader comfortable with how the colour palettes work.

Section 1 - Introduction

To work with the colour palettes, you must understand how the BITs are arranged within a BYTE. If you are unsure, quickly read the number systems tutorial in the general assembler section.

Section 2 - Overview

The Gameboy Colour can display 56 different colours on screen at once from a total of 32,768 different colours. With clever code, you can display more than that, but it takes some doing, and that kind of trick is beyond the scope of this tutorial.

The magic number of 56 is due to the fact that there are eight separate palettes for the background (the window uses the same palettes), each of which has four colours (8 x 4 = 32), and there are also eight separate palettes for the objects (sprites) and although each palette has four colour entries, colour 0 is always transparent, leaving three useable colour entries (8 x 3 = 24).

The total of 32,768 for the number of different colours is reached due to the fact that each colour is made up of 32 shades of red, green, and blue, with the shades ranging from 0 to 31 (0 being none of the colour, 31 being the highest amount of the colour). 32 x 32 x 32 = 32,768.

Section 3 - Colour Palette Data

In Binary, the number range 0-31 can be represented by five BITs. This means that a single palette entry requires fifteen BITs to specify which colour is being displayed (five BITs for the red part, five for the blue part, and five for the green part). Fifteen BITs fit nicely into two BYTEs with one BIT to spare, so two BYTEs are used for each palette entry.

The two BYTEs are referred to as Colour Palette High and Colour Palette Low. The table below shows the two BYTEs, and what each BIT is used for.

Data Byte | H | i | g | h |  |  |  |  | L | o | w |  |  |  |  |
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
Data | X | B | B | B | B | B | G | G | G | G | G | R | R | R | R | R

Data Key
X : Not Used
B : Blue data
G : Green data
R : Red data

As the table above shows, the green data is split between the two bytes. This isn't a problem, you just need to remember that the high data BYTE holds the high BITs of the green data and the low data BYTE holds the low BITs of the green data.

Section 4 - Preparing the data

Getting the two data BYTEs ready for writing to a palette entry can be thought of as difficult. However, it isn't really.

So long as you already have three colour values ranging from 0-31, the preparation of the colour data BYTEs is fairly straightforward.

As a point of interest, if you have colour values ranging from 0-255 for the three colours, and you want to convert them to a 0-31 range, an easy method is to simply shift the data right three times (each shift right halves the data, losing any fractions, so three shifts right is halved, then quartered, then eighthed).

The command in assembler to shift the data right is SRL. So, assuming that you bring the colour value into the accumulator, the commands would be:

 SRL	A	; halves the data
 SRL	A	; halves it again
 SRL	A	; halves it one final time
Anyway, back to the data preparation. Let's assume that the red value is held in E, the green value is held in H, and the blue value is held in L. We will be storing the high data BYTE in E, and the low data BYTE in D.

For this example, we will create the low data BYTE first, and then the high data BYTE.

To start off with, we take the red data value and place it in the low data BYTE store. The code is as follows;

 LD	A,E
 AND	31
 LD	D,A
The AND command is used to mask out any unwanted data, just in case we have a colour value that is greater than 31.

That's the red part finished. Now the green. We need the right hand three BITs of the green data merged with the red data (placed in BITs 7, 6, and 5 of the low data BYTE).

The easiest way to do this is to shift the green data left five times, which pushes the left five BITs off into oblivion, and leaves the remaining three BITs in the correct positions. The code is as follows;

 LD	A,H
 SLA	A
 SLA	A
 SLA	A
 SLA	A
 SLA	A
Now we need to merge this with the red data already in the low data BYTE. A simple OR operation will do this for us, as follows;
 OR	D
 LD	D,A
That's the low data BYTE done, now the high BYTE. We'll start off by finishing off the green data. We want the left hand two BITs to form the right hand two BITs of the high data BYTE. To start off with, we mask out all the unwanted data, just in case we have a value higher than 31. The code is as follows;
 LD	A,H
 AND	31
Now we can shift the data to the right three times, throwing the data we don't want off the edge and keeping the two BITs we want in the right place. The code is as follows;
 SRL	A
 SRL	A
 SRL	A
This gives us the start of the high BYTE, so we'll stick it in E. The code is as follows;
 LD	E,A
The last colour to deal with now is blue. This value just needs masking and then shifting left two BITs. The code is as follows;
 LD	A,L
 AND	31
 SLA	A
 SLA	A
Now we have the final colour value, we just merge it with the high data BYTE. The code is as follows;
 OR	E

Section 5 - Writing the data

Writing data to a palette is done by using the following Gameboy registers: the write specification register and the write data register.

What you do is set up the write specification register to tell the gameboy what data you are sending, and then place the data in the write data register.

The BITs of the write specification register are used for the following things.

BIT | Used for
7 | If when writing the actual data, this BIT is set to 1, the next palette is automatically selected after writing the data.
6 | Not Used.
5,4,3 | These three BITs specify the palette number (0-7).
2,1 | These two BITs specify the specific palette entry that is being written (0-3).
0 | Specifies which part of the data is being written. If this BIT is set to 0, the low data BYTE is being written, if it is set to 1, the high data BYTE is being written.

A few examples of write specification entries follow:

Entry | Explanation
00001010 | BIT 7: Not set BIT 6: Not set BITs 5,4,3 : Value of 1, so palette 1 is being set BITs 2,1 : Value of 1, so palette entry 1 is being set BIT 0 : Not set, so the low data BYTE is being written
00011001 | BIT 7: Not set BIT 6: Not set BITs 5,4,3 : Value of 3, so palette 3 is being set BITs 2,1 : Value of 0, so palette entry 0 is being set BIT 0 : Set, so the high data BYTE is being written
00100100 | BIT 7: Not set BIT 6: Not set BITs 5,4,3 : Value of 4, so palette 4 is being set BITs 2,1 : Value of 2, so palette entry 2 is being set BIT 0 : Not set, so the low data BYTE is being written

You will notice that there is no way within the write specification register to state which type of palette is being written (background of object). This is because there are two write specification registers and two write data registers, one set for background palettes and one for object palettes.

The addresses of these registers are shown in the table below:

Register | Address
Background Write Specification (BCPS) | FF68
Background Write Data (BCPD) | FF69
Object Write Specification (OCPS) | FF6A
Object Write Data (OCPD) | FF6B

The four letter acronyms in brackets in the table above are the shorthand names for the registers used by Nintendo. Their full names are:

Acronym | Full Name
BCPS | Background Colour Palette Specification
BCPD | Background Colour Palette Data
OCPS | Object Colour Palette Specification
OCPD | Object Colour Palette Data

An example follows to show how the two data BYTEs are written for a single colour of a background palette. The data bytes are stored in D and E (D being the low data BYTE and E being the high data BYTE as in the example in the previous section).

 LD	HL,$FF68	; set up a pointer to the BCPS
 LD	C,%00001010	; Colour 1 of Palette 1
 LD	A,C		; Load A with the write specification
            ; data
 LD	[HLI],A		; place the data in the specification
            ; register and move the pointer to the
            ; BCPD
 LD	A,D		; get the low data BYTE
 LD	[HL],A		; send the low data BYTE to the register
 LD	HL,$FF68	; set the pointer back to the BCPS
 LD	A,C		; Load A with the write specification
            ; data
 INC	A		; Add 1 to the data, therefore setting
            ; BIT 0 which means we are now writing
            ; the high data BYTE
 LD	[HLI],A		; place the data in the specification
            ; register
            ; and move the pointer to the BCPD
 LD	A,E		; get the high data BYTE
 LD	[HL],A		; send the high data BYTE to the register

That's basically all there is to it. The example code is not the best code in the world, but it does show, with simple code, how it all works.

End of Tutorial

Buttons created using the interactive button maker at Coolarchive


GBDoK

GBDoK (v1.2)

(by Manfred Linzner (aka Pink/Abyss) and Jason (aka Icehawk) )

Table of Contents:

0. Introduction
0.1 Overview
0.2 History

1. Graphics
1.0 Important Notes
1.1 Background
1.2 Color Gameboy Support
1.3 Display
1.4 Display Modes
1.5 Sprites Window
1.6 Window

2. Miscellaneous
2.1 Interrupts
2.2 Joypad

3. Memory
3.1 Banks
3.2 How to include binary data-banks to your ROM image

4. Color Gameboy Programming Info
4.1 How to set your ROM to CGB-Mode
4.2 How to write real CGB comaptible Code
5. References
5.1 Webpages
5.2 IRC Channels

0.0 INTRODUCTION

0.1 Overview

This document is mainly for upcoming (color)Gameboy coders
who use Pascal Felber´s excellent GBDK package.

A basic knowledge of the C programming language will greatly
help you understand how to program for the gameboy using GBDK.

GBDK has almost no documentation about it´s internal functions
regarding graphics, interrupts and other important things.
This document tries to give you an quick overview about all
GBDK-functions and explains some ColorGameboy specific details.

0.2 History

13.05.99 Version 1.2
- Updated links.
- Updated text version to by syncronized with the HTML version.
- Added a few lines here and there due to personal experience.

13.05.99 Version 1.1
- Converted the whole document to HTML
- Severall bug-fixes and additions

28.01.99 Version 1.00
- Added information about including binary files as additional ROM BANKS.
- Fixed section 4.2 (ATTRIBUTES must be set first!)
- Added alot of descriptions to the routines described.
- A little bit of bug fixes and added routines.

22.01.99 Version 0.9
- Corrected some bugs
- Added BANK-Switching info
- Added history

12.01.99 Version 0.8
- First public release of GBDoK

1. Graphics

1.0 Important Notes

The Gameboy Uses an LCD display with a resolution of
160x144. The supported sprites are 8x8 and 8x16. There
is no way to use 32x32 sprites, so don't ask. The
gameboy supports a max of 40 sprites with 10 on one
horizontal axis at one time.

Before writing any data to the videomem you have to disable
the LCD-Display (display_off();).

On a real gameboy you are not able to upload data to the
videomem while the LCD is processing.
Gameboy emulators may not suffer from this problem.

If your programm needs to write to the videomem while displaying
something you have to be in the Vertical-Blank.It´s also possible
to write data to the videomem while being in the Horizontal-Blank
but this needs a special interrupt handling.

Also have in mind that most emulaotrs will fill all
RAM/ROM with zeros. A real gameboy won´t do that. At the end of
all programs, it is a good habit to fill at least the visible
screen data to all 0s.

All of the information provided is compiled from various
sources and own experiences. This document is for educational
purposes and ´homebrew´ GB-development.

GameBoy and CGB is copyrighted by Nintendo Co., LTD.

1.1 Background

void hide_bkg(); // Disable background

Calling hide_bkg(); turns all the background tiles off. The sprites are still visible. This command does not work with no$gmb.

void show_bkg(); // Enable Background

Calling show_bkg()/SHOW_BKG; turns all the background tiles. This is used to
turn them on after using the hide_bkg();

* Note : On the Gameboy Color, the background tiles start automatically,
however, on the gameboy and gameboy pocket, they initially start
off, so call a SHOW_BKG; after you load your background data.

void scroll_bkg(int x, int y); // Scroll background x/y >=0 <=255

void set_bkg_data(int first_tile, int nb_tiles, unsigned char *data);
/* -128 <= first_tile <= 127 * -128 <= first_tile+nb_tiles <= 127 * nb_tiles >= 1 */

set_bkg_data is used to load the background tiles and palette information into
memory. first_tile is the first tile that you want to load data into, usually 0.
nb_tiles is the number of tiles you want to load into memory. *data is a pointer
to the unsigned char that the tiles are stored in.

void set_bkg_tiles(int x, int y, int w, int h, unsigned char *tilelist);
/* 0 <= x <= 31 * 0 <= y <= 31 * 1 <= w <= 32-x * 1 <= h <= 32-y */
set_bkg_tiles puts the tile data onto the screen. You must load the tiles into
memory using set_bkg_data first. x is the starting location in tiles (pixels / 8)
to load the data into. y is the starting location in tiles (pixels / 8) to load the
data into. w is the width of the data you want to load in tiles. h is the height
in tiles of the data you want to put on the screen. *tilelist is a pointer to
an unsigned char that has a tile map of what you want to be displayed.

1.2 Color Gameboy Support

* Note: Before you can set the color attributes for a tile
you have to switch to the 2nd video-memory bank (VBK_REG=1).
The CGB can now address upto 512 tiles
(by use of BIT 3 from the attribute byte)

The attribute byte looks like this:

BIT 0-2: Palettenumber (0-7)
BIT 3 : Character Bank select
BIT 5 : Flip Tile Horizontal
BIT 6 : Flip Tile Vertical
BIT 7 : Background Priority

VBK_REG = 1; /* Use this before setting tile-attributes */
VBK_REG = 0; /* Use this before setting tile-offsets */
VBK_REG sets the Video Bank Register. When set to zero, it loads the
tile data into the background. When set to one, it loads the palette
data into the background. You're supposed to set the palette data
before you set the tile data.

void set_bkg_palette(UBYTE first_palette,UBYTE nb_palettes,UWORD *rgb_data);

set_bkg_palette loads the palette data into memory so the gameboy color
can access it. first_palette is the first palette to be loaded. nb_palettes
is the number of palettes to load. *rgb_data is a pointer to where the
palette data is located.
* Note : Never forget to set all 8 palettes after another
It´s not safe to set individual palettes
* Note/Plug : Jason's Colors.h has the most common colors predefined for use
with gbdk projects.

void set_sprite_palette(UBYTE first_palette,UBYTE nb_palettes,UWORD *rgb_data);

set_sprite_palette loads the sprite palette into memory so the gameboy color
can access it. first_palette is the first palette to be loaded. nb_palettes
is the number of palettes to load. *rgb_data is a pointer to where the
palette data is located.
* Note : Never forget to set all 8 palettes after another
It´s not safe to set individual palettes
* Note/Plug : Jason's Colors.h has the most common colors predefined for use
with gbdk projects.

/* GB type (GB, PGB, CGB) */
/* Read this byte to determine the type of Gameboy*/

extern UBYTE _cpu;

#define DMG_TYPE 0x01 /* Original GB or Super GB */
#define MGB_TYPE 0xFF /* Pocket GB or Super GB 2 */
#define CGB_TYPE 0x11 /* Color GB */

So if you want to be backwards compatible to the normal gameboy use this:

if(_cpu == 0x11) {
color-related material
}

where 0x11 can also be replaced with CGB_TYPE.

1.3 Display

void display_on();
display_on() turns the display on.

void display_off();
display_off() turns the display off.

1.4 Display Modes

void mode(int m);

**mode defines**

M_TEXT_OUT
M_DRAWING
M_TEXT_INOUT

These modes are used for the GBDK built in libraries. M_TEXT_OUT allows puts
and other text-oriented tools to be used. M_DRAWING allows GBDK's plot
and other routines (such as directy.h downloadable from Jason's site)
to be used. M_TEXT_INOUT allows for puts and gets to be used.

1.5 Sprites

void show_sprites(); /* Enable Sprites */

show_sprites() displays sprites properly configured on the visible screen.
* Note : Newer GBDK versions support SHOW_SPRITES;

void hide_sprites(); /* Disable Sprites */

hide_sprites() makes all sprites not visible.

void sprites8x8(); /* Set Spritesize to 8x8 */

Sets the sprites to be eight pixels wide by eight pixels tall.
* Note : Newer GBDK versions support SPRITES_8x8;

void sprites8x16(); /* Set Spritesize to 8x16 */

Sets the sprites to be eight pixels wide by sixteen pixels tall.
* Note : Newer GBDK versions support SPRITES_8x16;

void set_sprite_data(int first_tile, int nb_tiles, unsigned char *data);

/* 0 <= first_tile <= 255 * 0 <= first_tile+nb_tiles <= 255 * nb_tiles >= 1 */
set_sprite_data loads data into the sprite ram area. By doing this, sprites
are able to contain the patterns you have designed. first_tile is the first
tile from the data to be loaded. nb_tiles is the number of tiles that you
want to be loaded. *data is a pointer to an unsigned char that contains the
tile data you want to be loaded.

void set_sprite_tile(int nb, int tile); /* 0 <= nb <= 39 * 0 <= tile <= 255 */

set_sprite_tile configures sprites so that they can be visible by loading
the sprite tile into the selected sprite. nb refers to which of the 40 visible
sprites you would like to load data into. tile is an integer refering to which
tile from the loaded data set in set_sprite_data you want to be accessed by
the sprite.

void set_sprite_prop(int nb, int prop); /* 0 <= nb <= 39 */

** sprite propperties (use with set_sprite_prop) **
S_PALETTE
S_FLIPX
S_FLIPY
S_PRIORITY
set_sprite_prop sets properties for each sprite. S_PALETTE (gameboy color
only) contains the palette information. S_FLIPX allows the x-axis to be
flipped/mirrored. S_FLIPY allows the y-axis to be flipped/mirrored. S_PRIORITY
contains data referring to if this tile should be on top of other tiles or not.

void move_sprite(int nb, int x, int y);

/* 0 <= nb <= 39 * 0 <= x <= 255 * 0 <= y <= 255 */
move_sprite moves the specified sprite to another x-y location. nb is the number of
the sprite (0-39) that you would like to move. x is the new x-location for the sprite.
y is the new y-location for the sprite.

1.6 Window

void show_window();
show_window() displays the window.

void hide_window();
hide_window() hides the window.

void move_win(UBYTE x,UBYTE y);
move_win(x,y) moves the window to another location. x is the new x location for the window.
y is the new y location for the window.

void scroll_win(BYTE x,BYTE y); /*Move Window relative to current position*/
scroll_win scrolls the window to another location. x is how many pixels horizontally you
would like to scroll the window. y is how many pixels vertically you would like to
scroll the window.

2. Miscellaneous

2.1 Interrupts

Note: Use disable_interrupts() before setting new interrupts.

void enable_interrupts();
enable_interrupts() sets the interrupts to active. Using this allows routines
set for interrupts to be called.

void disable_interrupts();
disable_interrupts() disables the interrupts. Many routines use disabled interrupts to
perform their tasks.

void add_VBL(int_handler h);
While interrupts are disabled, add_VBL(h) allows routines to be run when the
vblank interrupt flag is called. h is the routine to perform.

void add_LCD(int_handler h);
While interrupts are disabled, add_VBL(h) allows routines to be run when the
lcd interrupt flag is called. h is the routine to perform.

void add_TIM(int_handler h);
While interrupts are disabled, add_VBL(h) allows routines to be run when the
timer interrupt flag is called. h is the routine to perform.

void add_SIO(int_handler h);
While interrupts are disabled, add_VBL(h) allows routines to be run when the
serial input/output interrupt flag is called. h is the routine to perform.

void add_JOY(int_handler h);
While interrupts are disabled, add_VBL(h) allows routines to be run when the
joystick interrupt flag is called. h is the routine to perform.

set_interrupts(UBYTE flags);
Interrupt-Flags
VBL_IFLAG
LCD_IFLAG
TIM_IFLAG
SIO_IFLAG
JOY_IFLAG
set_interrupts(flags) specifies which interrupts to call.
* Note : (Use "|" to combine them)
* Mote : VBL_IFLAG should always be set.

2.2 Joypad

**Joypad defines**

J_START = Start button
J_SELECT = Select button
J_B = B
J_A = A
J_DOWN = Down arrow on joypad
J_UP = Up arrow on joypad
J_LEFT = Left arrow on joypad
J_RIGHT = Right arrow on joypad

int joypad();
joypad() returns a byte containing which buttons from the joypad defines are pressed

int waitpad(int mask);
waitpad(mask) stops executing the program until the button(s) specified in mask are pressed.

void waitpadup();
waitpadup() stops executing the program until all buttons on the pad are released.

example:

int i,x,y;

i = joypad();
if(i & J_START) pause();
if(i & J_UP) y--;
if(i & J_DOWN) y++;
if(i & J_LEFT) x--;
if(i & J_RIGHT) x++;

3. Memory

3.1 Banks

GBDK doesn´t supports Memory-BANKS automatically.
You have to link them with the compiler. GBDK i.e. can´t
split huge data into several roms. You have to
handle the BANKS yourself with direct memory adressing.

The best way of handling BANKs is to put all your
program-code into BANK(0) and your data-code into BANK (1-31).

Using the MBC1 memory bank controller
-------------------------------------
#define SWITCH_ROM_MBC1(b) \*(unsigned char *)0x2000 = (b)
SWITCH_ROM_MBC1(b) switches the current changable 16k ROM to the bank specified by b.

#define SWITCH_RAM_MBC1(b) \*(unsigned char *)0x4000 = (b)
SWITCH_RAM_MBC1(b) switches the RAM bank to the bank specified by b.

#define ENABLE_RAM_MBC1 \*(unsigned char *)0x0000 = 0x0A
ENABLE_RAM_MBC1 enables support for external ram.

#define DISABLE_RAM_MBC1 \*(unsigned char *)0x0000 = 0x00
DISABLE_RAM_MBC1 disables support for external ram.

Using the MBC5 memory bank controller
-------------------------------------

Note! Using MBC5 and bankswitching seems to have a slight error in the code.

#define SWITCH_ROM_MBC5(b)
\*(unsigned char *)0x2000 = (b)&0xFF; \*(unsigned char *)0x3000 = (b)>>8
SWITCH_ROM_MBC5(b) switches the current changable 16k ROM to the bank specified by b.

#define SWITCH_RAM_MBC5(b) \*(unsigned char *)0x4000 = (b)
SWITCH_RAM_MBC5(b) switches the RAM bank to the bank specified by b.

#define ENABLE_RAM_MBC5 \*(unsigned char *)0x0000 = 0x0A
ENABLE_RAM_MBC1 enables support for external ram.

#define DISABLE_RAM_MBC5 \*(unsigned char *)0x0000 = 0x00
DISABLE_RAM_MBC1 disables support for external ram.

void hiramcpy(UBYTE dst,const void *src,UBYTE n);
hiramcpy copies procedures from ROM to RAM.

3.2 How to include binary data-banks to your ROM image

If you want to include binary code into your program
you can use the following procedure.

- Compile a standard 32KB ROM Image
- Set BYTE 0x147 to 1 (ROM+MBC1)
- Set BYTE 0x148 to your used romsize
- Add your 16KB Binaryfile to that image
(i.e. on MS-DOS systems simply use COPY /b main.gb + bank01.gb final.gb)

0 - 256Kbit = 32KByte = 2 banks
1 - 512Kbit = 64KByte = 4 banks
2 - 1Mbit = 128KByte = 8 banks
3 - 2Mbit = 256KByte = 16 banks
4 - 4Mbit = 512KByte = 32 banks
5 - 8Mbit = 1MByte = 64 banks
6 - 16Mbit = 2MByte = 128 banks

52 - 9Mbit = 1.1MByte = 72 banks
53 - 10Mbit = 1.2MByte = 80 banks
54 - 12Mbit = 1.5MByte = 96 banks

- Finally use RGBFIX95 to pad your file and fix all the other stuff.

For accessing this (extern) data from C you can use this method:

UBYTE *data,externdata;
data=(UBYTE*)0x4000;

SWITCH_ROM_MBC1(2) // Switch to ROM Bank 2
externdata=*data; // Here you already access BANK 2
SWITCH_ROM_MBC1(1) // Switch back to ROM Bank 1

4. Color Gameboy Programming Info:

4.1 How to set your ROM to CGB-Mode

- Write 0x80 to rombyte-offset 0x143 (=CGB mode)
( you can do this at link-time by setting this linkerswitch: Wl-yp0x143=0x80)

Without setting this byte most emulators will treat your program wrong (and of course the real CGB too).

0xC0 is supposed to be used for GBC-only roms, however most emulators currently do not support, this, so you can use the if(_cpu == 0x11) to make sure the game runs in color-only mode.

4.2 How to write real CGB comaptible Code

For being compatible to the real Gameboy you should use this order when coding:

- Disable LCD, Disable Interrupts (see 1.3 and 2.1)
- Identify if the Gameboy you are currently running on is a CGB (see 1.2)
- Transfer color palettes (see 1.2)
- Transfer VRAM (video-memory) (see 1.1)
- Transfer attributes to Bank1 (see 1.1 and 1.2)
- Transfer tilesoffsets to Bank0 (BF CHR Code Transfer) (see 1.1)
- Enable LCD Display (see 1.3)
- Go on with your normal code....

5. References

5.1 Webpages

Gameboy Developer's Kit
Pascal Felber
Needed to compile your C files.
http://www.gbdev.org/GBDK/

SHIN´EN Entertainment
Manfred Linzner
Makers of the GHX Soundengine (a commercial gameboy soundengine).
Find GBDoK here.
http://www.shinen.com/GBDoK/GBDoK.html

Gameboy Development News
Jason
Daily updated site telling about the day's development. Also contains ASM and C examples, utilities and misc. shit.
Find GBDoK here.
http://www.gbdev.org/news/

Gameboy Mailing List
The maintainer of this list is Kalle Pihlajasaari.
Mailing list... don't e-mail with ROM requests or stuff answered on webpages/this doc.
http://www.ip.co.za/people/kalle/gb/list.htm

Gameboy Webring
Genetic Fantasia
List of several gameboy development webpages
http://www.geneticfantasia.com/html/webring.html

No$Gmb
Martin Korth
Best all-around gameboy emulator and debugger. Shareware, though.
http://www.work.de/nocash

DBOY
BouKiChi
Freeware Gameboy Color/SGB emulator based off of an early VGB.
http://members.xoom.com/foo01/

Harry Mulder's Gameboy Development
Harry Mulder
Author of Gameboy Tile Deisgner and Gameboy Map Builder.
http://www.casema.net/~hpmulder/

Gamelist's Mailing List Archive
Gamelist
Rarely updated archive of the mailing list. Visit here before mailing the list.
http://www.geocities.com/SiliconValley/Platform/6906/

Bung Enterprises
Bung
Makers of flash carts and other goodies to test your gameboy programs on the GB/CGB.
http://www.bung.com.hk/

IRC Channels

#gbdev on EFNET
Contains many serious gameboy developers with the occasional 31337 hax0r.


Nick Pelling - Fast multiply on the Game Boy?!?!

Fast multiplication on the Game Boy!

You say: what are you talking about, Nick? The Game Boy (or even the Game Boy Color) wasn't designed to do anything fast?

To which I reply: when Dave Leitch & I were programming our "3D Pocket Pool" game ("3D Pool Allstars" in the US, for obvious reasons), we needed fast maths routines - so I had to come up with some. It didn't matter whether it was possible or not. :-) So here's what I devised...

Firstly, I don't think I'm breaking my Nintendo NDA by saying that the GB/GBC is powered by a low-power Z80 clone with the complex instructions taken out, but with other useful ops put in their place (any emulator site would tell you this). However, my solution didn't end up using any of the new instructions, so I don't feel too bad about posting my code openly. That's only relevant so I don't get emails saying "Hey Nick, why didn't you use LD HL,DE?"... it's because the GB doesn't have it. :-)

Anyway... overall, my approach to the problem was to figure out what the bare minimum set of instructions for any given multiply would be, and then try to achieve that. Also: because speed was so critical our title, my design aim was to fit it into the lowest (permanently banked in) ROM bank, so 16K was the upper size constraint... any solution I could construct in that space was fair game. :-)

In the end, though, I have to say that I created a monster of a routine. If you're expecting a petite 50-line masterpiece, look away now.

The code multiplies an 8-bit unsigned char (A) with a 16-bit unsigned short (DE), and here's how it begins:

Stage 1: build (from A) an offset (in HL) into a 256-element table, where each element holds up to 8 bytes of code. To avoid unnecessary shifts, the H and L values are simply masked from the A-value - so this isn't a simple "multiply by 8", it's also changing the order of the 256 elements, in a way which the table itself will have to be inversely ordered around. But it saves several run-time cycles for no actual cost, so I like this trick a lot. :-)

ld h,a
and $f8
ld l,a
xor h

Stage 2: add the start of the table into H, and set B = A = 0 (which many of the routines will rely on):

add a,>multiplyjumptable
ld h,a
xor a
ld b,a

Stage 3: jump directly to the appropriate code fragment - get on with it!

jp (hl)

All each of the 256 code fragments then needs to do is to multiply DE by a single fixed value: though I optimised most of these routines quite closely, there were (IIRC) a handful that could have been further tweaked - but I'd had quite enough of this accursed routine by that stage. :-)

Finally: it was absolutely essential that each element got padded up to 8 bytes, hence all the NOPx macro calls - for those routines that were longer than 8 bytes, the code jumps into various lumps of shared code, which I constructed by hand.

If you're writing a GB/GBC game and would like to use this gargantuan miracle of perseverance, please feel free to email me for a copy of the (verified) source code. Copyright remains mine, but I'm sure we can sort something out... :-)

%macro NOP1
{
    nop
}

%macro NOP2
{
    nop
    nop
}
%macro NOP3
{
    nop
    nop
    nop
}

%macro NOP4
{
    nop
    nop
    nop
    nop
}

%macro NOP5
{
    nop
    nop
    nop
    nop
    nop
}

%macro NOP6
{
    nop
    nop
    nop
    nop
    nop
    nop
}

%macro JUMP0
{
    ld h,a
    ;ld l,a
;L is already zero!
;[this is extremely tricksy!]
    ret
    NOP6
}

%macro JUMP0_1
{
    ld h,d
    ld l,e ;HL = DE
    ret
    NOP5
}

%macro JUMP0_2
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    ret
    NOP3
}

%macro JUMP0_3
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,de
    adc b
    ret
    nop
}
%macro JUMP0_4
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,hl
    rla
    ret
    nop
}

%macro JUMP0_5
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla ;AHL <<= 1
    add hl,hl
    jp .exit05
}

%macro JUMP0_6
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla ;AHL <<= 1
    add hl,de
    jp .exit06
}

%macro JUMP0_7
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla	  ;AHL <<= 1
    add hl,de
    jp .exit07
}
%macro JUMP0_8
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,hl
    jp .exit08

}

%macro JUMP0_9
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,hl
    jp .exit09
}

%macro JUMP0_10
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,hl
    jp .exit010
}

%macro JUMP0_11
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,hl
    jp .exit011
}
%macro JUMP0_12
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,de
    jp .exit012
}

%macro JUMP0_13
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,de
    jp .exit013
}

%macro JUMP0_14
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,de
    jp .exit014
}

%macro JUMP0_15
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla
    add hl,de
    jp .exit015
}

%macro JUMP0
{
    JUMP0_@1
}

%macro JUMP1
{
    ld h,d
    ld l,e ;HL = DE
    jp multby@1
    NOP3
}

%macro JUMP2
{
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla 	;AHL <<= 1
    jp multby@1
    nop
}

%macro JUMP3
{
;;;;if n * 0x3F...
  %if (@1)=15
  {
    ld a,d
    ld h,e
    rra
    rr h
    jp .exit63
  }
  %if (@1)<15
  {
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla ;AHL <<= 1
    add hl,de
    jp multby@1_3
  }
}

%macro JUMP4
{
;;;;if n * 0x40...
 %if (@1)=0
 {
    ld a,d
    ld h,e
    rra
    rr h
    jp .exit64
 }
;;;;if n * 0x41...
 %if (@1)=1
 {
    ld a,d
    ld h,e
    rra
    rr h
    jp .exit65
 }
 %if (@1)>1
 {
    ld h,d
    ld l,e ;HL = DE
    add hl,hl
    rla ;AHL <<= 1
    add hl,hl
    jp multby@1_4
 }
}

%macro JUMP5
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,hl
    jp multby@1_5
}

%macro JUMP6
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,de
    jp multby@1_6
}

%macro JUMP7
{
;;;;if n * 0x7F...
  %if (@1)=15
  {
    ld a,d
    ld h,e
    rra
    rr h
    jp .exit127
  }
  %if (@1)<15
  {
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,de
    jp multby@1_7
  }
}
%macro JUMP8
{
;;;;;if n * 0x80...
 %if (@1)=0
 {
    ld a,d
    ld h,e
    rra
    rr h
    jp .exit128
 }
;;;;if n * 0x81...
 %if (@1)=1
 {
    ld a,d
    ld h,e
    rra
    rr h
    jp .exit129
 }
;;;;if n * 0x82...
 %if (@1)=2
 {
    ld a,d
    ld h,e
    rra
    rr h
    jp .exit130
 }
 %if (@1)>2
 {
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,hl
    jp multby@1_8
 }
}

%macro JUMP9
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,hl
    jp multby@1_9
}

%macro JUMP10
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,hl
    jp multby@1_10
}

%macro JUMP11
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,hl
    jp multby@1_11
}
%macro JUMP12
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,de
    jp multby@1_12
}

%macro JUMP13
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,de
    jp multby@1_13
}

%macro JUMP14
{
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,de
    jp multby@1_14
}

%macro JUMP15
{
;;;;if n * 255...
 %if (@1)=15
 {
    ;xor a ;A = 0 already!
    sub e
    ld l,a
    ld a,e
    sbc d
    ld h,a
    ld a,d
    sbc b
    ret ;AHL = DEB - BDE
 }
;;;;if n * 254...
 %if (@1)=14
 {
    ld c,d
    ld h,e
    sla e
    jp .exit254
    nop
 }
 %if (@1)<14
 {
    ld h,d
    ld l,e
    add hl,hl
    rla
    add hl,de
    jp multby@1_15
 }
}
%macro JUMPSET
{
    JUMP0 @1
    JUMP0 @2
    JUMP1 @1
    JUMP1 @2
    JUMP2 @1
    JUMP2 @2
    JUMP3 @1
    JUMP3 @2
    JUMP4 @1
    JUMP4 @2
    JUMP5 @1
    JUMP5 @2
    JUMP6 @1
    JUMP6 @2
    JUMP7 @1
    JUMP7 @2
    JUMP8 @1
    JUMP8 @2
    JUMP9 @1
    JUMP9 @2
    JUMP10 @1
    JUMP10 @2
    JUMP11 @1
    JUMP11 @2
    JUMP12 @1
    JUMP12 @2
    JUMP13 @1
    JUMP13 @2
    JUMP14 @1
    JUMP14 @2
    JUMP15 @1
    JUMP15 @2
}
    A256 ;CODE GENERATION STARTS, ALIGN FOLLOWING TO PAGE BOUNDARY
multiplyjumptable public
    JUMPSET 0,8
    JUMPSET 1,9
    JUMPSET 2,10
    JUMPSET 3,11
    JUMPSET 4,12
    JUMPSET 5,13
    JUMPSET 6,14
    JUMPSET 7,15
.exit012
    adc b
    add hl,hl
.exit08
    rla
    add hl,hl
    rla
    ret
.exit013
    adc b
    add hl,hl
.exit09
    rla
    add hl,hl
    rla
    add hl,de
    adc b
    ret
.exit014
    adc b
    add hl,hl
.exit010
    rla
    add hl,de
.exit06
    adc b
    add hl,hl
    rla
    ret
.exit015
    adc b
    add hl,hl
.exit011
    rla
    add hl,de
.exit07
    adc b
    add hl,hl
.exit05
    rla
    add hl,de
    adc b
    ret
.exit63
    ld c,a
    ld a,b
    rra ;CHA = DE * 128
    rr c
    rr h
    rra ;CHA = DE * 64
    sub e
    ld l,a
    ld a,h
    sbc d
    ld h,a
    ld a,c
    sbc b ;AHL = CHA - BDE
    ret
.exit64
    ld l,b
    rr l
    rra
    rr h
    rr l
    ret
.exit65
    ld l,b
    rr l
    rra
    rr h
    rr l
    add hl,de
    adc b
    ret
.exit127
    ld c,a
    ld a,b
    rra ;CHA = DE * 128
    sub e
    ld l,a
    ld a,h
    sbc d
    ld h,a
    ld a,c
    sbc b ;AHL = CHA - BDE
    ret
.exit128
    ld l,b
    rr l
    ret
.exit129
    ld l,b
    rr l
    add hl,de
    adc b
    ret
.exit130
    ld l,b
    rr l
    add hl,de
    adc b
    add hl,de
    adc b
    ret
.exit254
    rl d
    rl b ;CHA = DE * 256, BDE = DE * 2
    ;xor a ;A = 0 already!
    sub e
    ld l,a
    ld a,h
    sbc d
    ld h,a
    ld a,c
    sbc b
    ret ;AHL = DEB - (DE * 2)
;----------------------------------------------------------------------------
; FUNCTION: MulU16x8
; DOES...
; Multiplies a 16 bit value by an 8 bit one (both unsigned).
; IN: DE = the 16 bit value
; A = the 8 bit one
; OUT: AHL = the product
; Everything else trashed
;----------------------------------------------------------------------------
MulU16x8 proc public
    ld h,a
    and $f8
    ld l,a
    xor h
    add a,>multiplyjumptable
    ld h,a
    xor a
    ld b,a
    jp (hl)
MulU16x8 endproc
;---------------------------
;---------------------------

%macro INTRO
{
@1_14
    adc b
    add hl,hl
@1_10
    rla
    add hl,de
@1_6
    adc b
    add hl,hl
@1_4
    rla
    jr @1
@1_12
    adc b
    add hl,hl
@1_8
    rla
    add hl,hl
    rla
    jr @1
@1_13
    adc b
    add hl,hl
@1_9
    rla
    jr @1_x
@1_15
    adc b
    add hl,hl
@1_11
    rla
    add hl,de
@1_7
    adc b
@1_x
    add hl,hl
@1_5
    rla
    add hl,de
@1_3
    adc b
@1
    @2	;call the shiftN macro @1_0
}
%macro SHIFT1
{
    add hl,hl
    rla
}

%macro SHIFT2
{
    SHIFT1
    SHIFT1
}

%macro SHIFT3
{
    SHIFT2
    SHIFT1
}

%macro SHIFT4
{
    SHIFT2
    SHIFT2
}
%macro MYRET ;edit this macro to change the return register convention
{ ;RETURN VALUES ARE NOW IN AHL SO...
    ;ld c,a
;we're expecting value to be returned in CHL, not AHL
    ret
}
    INTRO multby1,SHIFT4
    add hl,de
    adc b
    MYRET

    INTRO multby2,SHIFT3
    add hl,de
    adc b
    SHIFT1
    MYRET

    INTRO multby3,SHIFT3
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    MYRET

    INTRO multby4,SHIFT2
    add hl,de
    adc b
    SHIFT2
    MYRET
    INTRO multby5,SHIFT2
    add hl,de
    adc b
    SHIFT2
    add hl,de
    adc b
    MYRET

    INTRO multby6,SHIFT2
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT1
    MYRET

    INTRO multby7,SHIFT2
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    MYRET

    INTRO multby8,SHIFT1
    add hl,de
    adc b
    SHIFT3
    MYRET
    INTRO multby9,SHIFT1
    add hl,de
    adc b
    SHIFT3
    add hl,de
    adc b
    MYRET

    INTRO multby10,SHIFT1
    add hl,de
    adc b
    SHIFT2
    add hl,de
    adc b
    SHIFT1
    MYRET

    INTRO multby11,SHIFT1
    add hl,de
    adc b
    SHIFT2
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    MYRET

    INTRO multby12,SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT2
    MYRET
    INTRO multby13,SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT2
    add hl,de
    adc b
    MYRET

    INTRO multby14,SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT1
    MYRET

    INTRO multby15,SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    SHIFT1
    add hl,de
    adc b
    MYRET
?


GameBoy Sound Table

Note | MIDI | GB | Hertz | Note | MIDI | GB | Hertz
C 0 | 0 | - | 8.176 | E 5 | 64 | 1650 | 329.63
C# 0 | 1 | - | 8.662 | F 5 | 65 | 1673 | 349.23
D 0 | 2 | - | 9.177 | F# 5 | 66 | 1694 | 369.99
D# 0 | 3 | - | 9.723 | G 5 | 67 | 1714 | 391.99
E 0 | 4 | - | 10.301 | G# 5 | 68 | 1732 | 415.31
F 0 | 5 | - | 10.913 | A 5 | 69 | 1750 | 440.00
F# 0 | 6 | - | 11.562 | A# 5 | 70 | 1767 | 466.16
G 0 | 7 | - | 12.250 | B 5 | 71 | 1783 | 493.88
G# 0 | 8 | - | 12.978 | C 6 | 72 | 1798 | 523.25
A 0 | 9 | - | 13.750 | C# 6 | 73 | 1812 | 554.37
A# 0 | 10 | - | 14.568 | D 6 | 74 | 1825 | 587.33
B 0 | 11 | - | 15.434 | D# 6 | 75 | 1837 | 622.25
C 1 | 12 | - | 16.352 | E 6 | 76 | 1849 | 659.26
C# 1 | 13 | - | 17.324 | F 6 | 77 | 1860 | 698.46
D 1 | 14 | - | 18.354 | F# 6 | 78 | 1871 | 739.99
D# 1 | 15 | - | 19.445 | G 6 | 79 | 1881 | 783.99
E 1 | 16 | - | 20.601 | G# 6 | 80 | 1890 | 830.61
F 1 | 17 | - | 21.826 | A 6 | 81 | 1899 | 880.00
F# 1 | 18 | - | 23.124 | A# 6 | 82 | 1907 | 932.32
G 1 | 19 | - | 24.499 | B 6 | 83 | 1915 | 987.77
G# 1 | 20 | - | 25.956 | C 7 | 84 | 1923 | 1046.5
A 1 | 21 | - | 27.50 | C# 7 | 85 | 1930 | 1108.7
A# 1 | 22 | - | 29.135 | D 7 | 86 | 1936 | 1174.7
B 1 | 23 | - | 30.867 | D# 7 | 87 | 1943 | 1244.5
C 2 | 24 | - | 32.703 | E 7 | 88 | 1949 | 1318.5
C# 2 | 25 | - | 34.648 | F 7 | 89 | 1954 | 1396.9
D 2 | 26 | - | 36.708 | F# 7 | 90 | 1959 | 1480.0
D# 2 | 27 | - | 38.890 | G 7 | 91 | 1964 | 1568.0
E 2 | 28 | - | 41.203 | G# 7 | 92 | 1969 | 1661.2
F 2 | 29 | - | 43.653 | A 7 | 93 | 1974 | 1760.0
F# 2 | 30 | - | 46.249 | A# 7 | 94 | 1978 | 1864.7
G 2 | 31 | - | 48.999 | B 7 | 95 | 1982 | 1975.5
G# 2 | 32 | - | 51.913 | C 8 | 96 | 1985 | 2093.0
A 2 | 33 | - | 55.000 | C# 8 | 97 | 1988 | 2217.5
A# 2 | 34 | - | 58.270 | D 8 | 98 | 1992 | 2349.3
B 2 | 35 | - | 61.735 | D# 8 | 99 | 1995 | 2489.0
C 3 | 36 | 44 | 65.406 | E 8 | 100 | 1998 | 2637.0
C# 3 | 37 | 156 | 69.295 | F 8 | 101 | 2001 | 2793.8
D 3 | 38 | 262 | 73.416 | F# 8 | 102 | 2004 | 2960.0
D# 3 | 39 | 363 | 77.781 | G 8 | 103 | 2006 | 3136.0
E 3 | 40 | 457 | 82.406 | G# 8 | 104 | 2009 | 3322.4
F 3 | 41 | 547 | 87.307 | A 8 | 105 | 2011 | 3520.0
F# 3 | 42 | 631 | 92.499 | A# 8 | 106 | 2013 | 3729.3
G 3 | 43 | 710 | 97.998 | B 8 | 107 | 2015 | 3951.1
G# 3 | 44 | 786 | 103.82 | C 9 | 108 | - | 4186.0
A 3 | 45 | 854 | 110.00 | C# 9 | 109 | - | 4434.9
A# 3 | 46 | 923 | 116.54 | D 9 | 110 | - | 4698.6
B 3 | 47 | 986 | 123.47 | D# 9 | 111 | - | 4978.0
C 4 | 48 | 1046 | 130.81 | E 9 | 112 | - | 5274.0
C# 4 | 49 | 1102 | 138.59 | F 9 | 113 | - | 5587.7
D 4 | 50 | 1155 | 146.83 | F# 9 | 114 | - | 5919.9
D# 4 | 51 | 1205 | 155.56 | G 9 | 115 | - | 6271.9
E 4 | 52 | 1253 | 164.81 | G# 9 | 116 | - | 6644.9
F 4 | 53 | 1297 | 174.61 | A 9 | 117 | - | 7040.0
F# 4 | 54 | 1339 | 184.99 | A# 9 | 118 | - | 7458.6
G 4 | 55 | 1379 | 195.99 | B 9 | 119 | - | 7902.1
G# 4 | 56 | 1417 | 207.65 | C 10 | 120 | - | 8372.0
A 4 | 57 | 1452 | 220.00 | C# 10 | 121 | - | 8869.8
A# 4 | 58 | 1486 | 233.08 | D 10 | 122 | - | 9397.3
B 4 | 59 | 1517 | 246.94 | D# 10 | 123 | - | 9956.1
C 5 | 60 | 1546 | 261.63 | E 10 | 124 | - | 10548.1
C# 5 | 61 | 1575 | 277.18 | F 10 | 125 | - | 11175.3
D 5 | 62 | 1602 | 293.66 | F# 10 | 126 | - | 11839.8
D# 5 | 63 | 1627 | 311.13 | G 10 | 127 | - | 12543.9
Middle C = 261.63Hz = C5

NOTE:

  The GB values above are the closest match to the shown

 perfect Hertz frequencies. The higher the frequencies

 the GB values attempt to recreate the more the percentage

 of error increases.


Реклама от Google
Реклама от Google
Объявление занимает всю страницу
Не показывать это объявление
Готово! Отмена
Почему вам не понравилась эта реклама?
Благодарим за отзыв! Назад
Мы проверим это объявление и сделаем все возможное для повышения качества рекламы.
Чтобы рекламные объявления соответствовали вашим интересам, укажите свои предпочтения.
Благодарим за отзыв! Отмена
Мы проверим объявления на этом сайте на основании вашего отзыва.
Чтобы рекламные объявления соответствовали вашим интересам, укажите свои предпочтения.
Закрытие объявления: %1$d


  • Загрузите Google Chrome
    Безопасный просмотр сайтов. Google Chrome сообщает об опасных сайтах
    google.ru/chrome/browser
Реклама от Google
Реклама от Google
Объявление занимает всю страницу
Не показывать это объявление
Готово! Отмена
Почему вам не понравилась эта реклама?
Благодарим за отзыв! Назад
Мы проверим это объявление и сделаем все возможное для повышения качества рекламы.
Чтобы рекламные объявления соответствовали вашим интересам, укажите свои предпочтения.
Благодарим за отзыв! Отмена
Мы проверим объявления на этом сайте на основании вашего отзыва.
Чтобы рекламные объявления соответствовали вашим интересам, укажите свои предпочтения.
Закрытие объявления: %1$d


  • Загрузите Google Chrome
    Ищите проще и быстрее с новым браузером Google Chrome!
    google.ru/chrome/browser
Реклама от Google
Реклама от Google
Объявление занимает всю страницу
Не показывать это объявление
Готово! Отмена
Почему вам не понравилась эта реклама?
Благодарим за отзыв! Назад
Мы проверим это объявление и сделаем все возможное для повышения качества рекламы.
Чтобы рекламные объявления соответствовали вашим интересам, укажите свои предпочтения.
Благодарим за отзыв! Отмена
Мы проверим объявления на этом сайте на основании вашего отзыва.
Чтобы рекламные объявления соответствовали вашим интересам, укажите свои предпочтения.
Закрытие объявления: %1$d


  • Снег не стал ждать зиму.
    Не жди и ты! Начни учиться НЛП. До конца осени запишись Бесплатно.
    fastrost.ru/Тренинг+НЛП


404 - Not Found


Object not found!

Wayback Machine
мар | АПР | СЕН
Previous capture | 10 | Next capture
2010 | 2011 | 2012
62 captures
10 апр 11 - 22 дек 12
sparklines
Close Help

Object not found!

The requested URL was not found on this server. If you entered the URL manually please check your spelling and try again.
If you think this is a server error, please contact the webmaster

Error 404

www.sbirmc.ac.uk
Sun 10 Apr 2011 17:31:15 IST
Apache/2.0.40 (Red Hat Linux)


404 - PAGE NOT FOUND

ERROR 404 - PAGE NOT FOUND

Why am I seeing this page?

404 means the file is not found. If you have already uploaded the file then the name may be misspelled or it is in a different folder.

Other Possible Causes

You may get a 404 error for images because you have Hot Link Protection turned on and the domain is not on the list of authorized domains.

If you go to your temporary url (http://ip/~username/) and get this error, there maybe a problem with the rule set stored in an .htaccess file. You can try renaming that file to .htaccess-backup and refreshing the site to see if that resolves the issue.

It is also possible that you have inadvertently deleted your document root or the your account may need to be recreated. Either way, please contact HostGator immediately via phone or live chat so we can diagnose the problem.

Are you using WordPress? See the Section on 404 errors after clicking a link in WordPress.

How to find the correct spelling and folder

Missing or Broken Files

When you get a 404 error be sure to check the URL that you are attempting to use in your browser.This tells the server what resource it should attempt to request.

http://example.com/example/Example/help.html

In this example the file must be in public_html/example/Example/

Notice that the CaSe is important in this example. On platforms that enforce case-sensitivity example and Example are not the same locations.

For addon domains, the file must be in public_html/addondomain.com/example/Example/ and the names are case-sensitive.

Broken Image

When you have a missing image on your site you may see a box on your page with with a red X where the image is missing. Right click on the X and choose Properties. The properties will tell you the path and file name that cannot be found.

This varies by browser, if you do not see a box on your page with a red X try right clicking on the page, then select View Page Info, and goto the Media Tab.

http://example.com/images/banner.PNG

In this example the image file must be in public_html/images/

Notice that the CaSe is important in this example. On platforms that enforce case-sensitivity PNG and png are not the same locations.

404 Errors After Clicking WordPress Links

When working with WordPress, 404 Page Not Found errors can often occur when a new theme has been activated or when the rewrite rules in the .htaccess file have been altered.

When you encounter a 404 error in WordPress, you have two options for correcting it.

Option 1: Correct the Permalinks

  1. Log in to WordPress.
  2. From the left-hand navigation menu in WordPress, click Settings > Permalinks (Note the current setting. If you are using a custom structure, copy or save the custom structure somewhere.)
  3. Select Default.
  4. Click Save Settings.
  5. Change the settings back to the previous configuration (before you selected Default). Put the custom structure back if you had one.
  6. Click Save Settings.

This will reset the permalinks and fix the issue in many cases. If this doesn't work, you may need to edit your .htaccess file directly.

Option 2: Modify the .htaccess File

Add the following snippet of code to the top of your .htaccess file:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# End WordPress

If your blog is showing the wrong domain name in links, redirecting to another site, or is missing images and style, these are all usually related to the same problem: you have the wrong domain name configured in your WordPress blog.

How to modify your .htaccess file

The .htaccess file contains directives (instructions) that tell the server how to behave in certain scenarios and directly affect how your website functions.

Redirects and rewriting URLs are two very common directives found in a .htaccess file, and many scripts such as WordPress, Drupal, Joomla and Magento add directives to the .htaccess so those scripts can function.

It is possible that you may need to edit the .htaccess file at some point, for various reasons.This section covers how to edit the file in cPanel, but not what may need to be changed.(Y


Dropbox - Ошибка

Выберите язык

  • Bahasa Indonesia
  • Bahasa Malaysia
  • Dansk
  • Deutsch
  • English
  • Español (España)
  • Español (Latinoamérica)
  • Français
  • Italiano
  • Nederlands
  • Norsk (bokmål) [Beta]
  • Polski
  • Português (Brasil)
  • Pусский
  • Svenska
  • Українська [Beta]
  • ไทย
  • 中文（简体）
  • 中文（繁體）
  • 日本語
  • 한국어
  • Войти
    Функция единого входа включена
    или Зайти с логином и паролем Dropbox
    Мы отправили вам код по номеру телефона, заканчивающемуся на .
    Введите код, сформированный приложением проверки подлинности на вашем мобильном устройстве.

    Зарегистрируйтесь в Dropbox.

Нам не удается найти то, что вы ищете.

Поделиться