The other day, for whatever reason, I wanted to know how fast the Nintendo logo scrolls down the screen on a DMG. The boot ROM has been dumped forever and this is a simple matter to investigate in BGB. In BGB select Gameboy as the system type in the system tab, check “bootroms enabled”, and click ok. Then reset the CPU. Now, assuming the boot ROM is in the specified location, you can debug it.
I added a breakpoint (debug, breakpoints) with condition (FF44)=90. This breakpoint triggers on every VBlank, ie when register FF44, LY, the LCD line counter, reaches value $90. This is a more certain way of triggering on VBlank than the interrupt vector for cases when the regular interrupt may not be triggered in time due to interrupts being disabled, or (as in this case) when the code doesn’t use interrupts at all.
By pressing F9 repeatedly and seeing how much the logo would move every time, it seemed that the logo moved 1 pixel every 2 frames. Or not. It actually alternates between waiting 2 and 3 frames between every progression. The code responsible for this is the following, at position $0060 in the boot ROM:
00:0060 ld e,$02 00:0062 ld c,$0C 00:0064 ldh a,[$FF44] 00:0066 cp a,$90 00:0068 jr nz,$0064 00:006A dec c 00:006B jr nz,$0064 00:006D dec e 00:006E jr nz,$0062
This is the wait for VBlank routine. We have three layers of loops here. Outermost, we have a countdown using the E register, to wait for two frames. The innermost loop (0064-0068) wait until LY=$90, which just like with the breakpoint above signifies the start of the VBlank period. Both straightforward mechanisms.
The interesting part is the dec c/jr nz,$0064 mechanism. On the surface, it seems to be added to prevent the outer loop from exiting too early. On the second iteration of the outer loop, LY would still be $90, and the loop would immediately fall through instead of waiting for another frame. The C loop iterates 12 ($C) times, but interestingly jumps through the inner loop as well. This means that LY may or may not stop being $90 before the middle loop ends, depending on the exact time that the loop is entered. In other words, sometimes the code gets stuck in the inner loop for an extra frame when C=1, and then the last dec C is executed, and the outer loop exits.
The animation jitters between 2 and 3 frames consumed between each pixel move, so the animation takes 25% longer time than a naïve reading of the code would suggest. (5 frames per 2 pixels vs 4 frames per 2 pixels.) Now the question is if this was intentional to make the animation go slightly slower, or just a bug. If it was just a bug, it would have been easy to fix by making the dec C loop jump back to 006A (the dec C instruction) and setting a higher initial value for C.
Bug or intentional slowdown of the animation? Would be interesting to know.
Gradual Decline - A quick Gameboy glitch ROM by nitro2k01
This ROM explores the idea of what would happen if you wrote random data to
random IO registers and random positions in video RAM. If you like audiovisual
glitches, this is for you. The ROM generates randomized graphics, and bleeps,
bloops and static. Sometimes it will even produce tones that are vaguely
melodic, even if dissonant.
How to use
Burn to a flash cartridge and run. It’s a unique experience each time you run
it. Even though this will not perceivably change the output of the ROM, you
may press buttons to seed external entropy into the random number generator.
Game Fighter posts:
- Dumping the boot ROM
Inside every Gameboy there’s a small boot ROM, which scrolls the Nintendo logo down from the top of the screen, and plays the iconic po-ling sound. This 256 byte boot ROM also checks that said Nintendo logo is present in the ROM chip of the cartridge, and validates the checksum of the header. Up until the 1992 legal case Sega v. Accolade it was believed that this check gave Nintendo the legal authority to control which games were allowed to run on the console, as the “Nintendo” logo was copyrighted by Nintendo, but the the ruling gave a strong precedent that using this small piece of copyrighted data to achieve interoperability was allowable.
The boot ROM locks itself out from being read before leaving control of the CPU to the program running on the cartridge, maybe more due to necessity than preventing read-out of the ROM, since the bottom memory area near the CPU entry point also covers the interrupt vectors, which the game needs access to. But as a result, this boot ROM remained an elusive secret, which was assumed would never see the light of day.
It was not until 2003 that Neviksti extracted this ROM from a DMG, decapping the CPU chip, looking at the CPU die with a scanning electron microscope and painstakingly reading out the bits visually, all 2048 (8*256) of them.
In 2009, costis extracted the SGB boot ROM by externally overclocking the CPU right at the time when the boot ROM shuts itself out, which causes the write to the lock-out register to be ignored, and then it could be copied to the outside world. The GBC boot ROM was also dumped this way by costis soon after.
Just this year (2014) BennVenn made the process child’s play with a silly but effective method. All you need to perform the attack is a piece of wire! You solder one end of the wire to one of the crystal oscillation circuit, and scrape the other end of the wire against any grounded surface in the Gameboy, such as copper shielding plate. This causes the CPU to glitch to jump to a random location. If you’re lucky, this allows you take control of the code execution and, again, dump the boot ROM.
But, what about pirate Gameboy clones? For example this Game Fighter, which is a horizontal Gameboy clone. Also see my detailed teardown and analysis of this beauty. It also has a boot ROM, though slightly different in function. The Game Fighter, instead of scrolling the logo down the screen, starts the game immediately. I also knew from before that it checked that the logo was correct, but not the game’s ROM header checksum.
So I wrote a small program, did the glitching procedure and voila! I had the boot ROM. You can download it here:
If you really wanted, you could use this ROM with BGB and for example single step through it in BGB’s debugger. To do this, go into BGB’s settings, go to the system tab, then check the bootroms enabled option, and point the DMG boot ROM field to the ROM file.
Since the Game Fighter boots immediately, unlike an original ‘boy, I made the ROM have an invalid logo in its header so it would lock up and give me time to try to glitch the execution. The procedure was the same as on an original Gameboy: Find the one the pins of the crystal driver that is the input, solder a wire to it, (pictured above) and brush the other side to a part of the board that is ground. For the Game Fighter, I found that I had better luck getting it to work if I first shorted it to ground (for example the negative battery terminal) on power on, then brushing it against the surface as I let it go.
I will post the program I used for dumping the boot ROM later, along with a more detailed description of how it works.
Here’s the boot ROM disassembled, which holds an interesting secret! (Disassembly of the original DMG boot ROM for comparison.)
ld sp,$FFFE ; 0000 Set up the stack pointer. ; Clear VRAM. xor a ; 0003 ld hl,$9FFF ; 0004 Addr_0007: ldd [hl],a ; 0007 bit 7,h ; 0008 jr nz,Addr_0007 ; 000A ; Set up sound. ld hl,$FF26 ; 000C ld c,$11 ; 000F ld a,$80 ; 0011 ldd [hl],a ; 0013 [$FF26] = $80 Turn on sound ld [$ff00+c],a ; 0014 [$FF11] = $80 Channel 1 wave duty inc c ; 0015 ld a,$F3 ; 0016 ld [$ff00+c],a ; 0018 [$FF12] = $F3 Channel 1 envelope ldd [hl],a ; 0019 [$FF25] = $F3 Channel routing inc c ; 001A ld a,$C1 ; 001B ld [$ff00+c],a ; 001D [$FF13] = $C1 Channel 1 low frequency byte ld a,$77 ; 001E ld [hl],a ; 0020 [$FF24] = $77 Master volume ; Set up graphics. ld a,$FC ; 0021 ldh [$FF47],a ; 0023 [$FF47] = $FC BG palette ld a,$91 ; 0025 ldh [$FF40],a ; 0027 [$FF40] = $91 Turn on LCD ; Compare the second half of the logo in the header against ; the second half of the Nintendo logo stored in the boot ROM. ld de,Addr_0043 ; 0029 call Addr_0073 ; 002C cp a,$34 ; 002F Will return $34 if successful. jr nz,Addr_0036 ; 0031 If not, jump to a second compare operation. jp Addr_00FC ; 0033 ; Compare the second half of the logo in the header against ; the second half of the mystery logo stored in the boot ROM. Addr_0036: ld de,Addr_005B ; 0036 call Addr_0073 ; 0039 cp a,$34 ; 003C Will return $34 if successful. Addr_003E: jr nz,Addr_003E ; 003E If not, get stuck in an endless loop. jp Addr_00FC ; 0040 ; Second half of the Nintendo logo, $18 bytes Addr_0043: db $DC, $CC, $6E, $E6, $DD, $DD, $D9, $99 db $BB, $BB, $67, $63, $6E, $0E, $EC, $CC db $DD, $DC, $99, $9F, $BB, $B9, $33, $3E ; Second half of mysterious RIS or KIS logo, $18 bytes Addr_005B: db $00, $00, $00, $00, $76, $66, $C6, $31 db $00, $19, $66, $FF, $01, $88, $38, $C7 db $C6, $C8, $00, $00, $00, $00, $00, $00 ; Subroutine: Compare the cartridge header’s logo against ; a given memory location Addr_0073: ld hl,$011C ; 0073 Start comparing halfway into the logo Addr_0076: ld a,[de] ; 0076 inc de ; 0077 cp [hl] ; 0078 jr nz,Addr_0082 ; 0079 inc hl ; 007B ld a,l ; 007C cp a,$34 ; 007D $xx34 = The first byte after the header logo jr nz,Addr_0076 ; 007F ret ; 0081 Addr_0082: ld a,$85 ; 0082 Compare failed! ret ; 0084 ; $77 filler bytes Addr_0085: db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff db $ff, $ff, $ff, $ff, $ff, $ff, $ff ; Disable the boot ROM and hand over control to the game cartridge. Addr_00FC: ld a,$01 ; 00FC Write to the ROM disable register ldh [$FF50],a ; 00FE
It’s mostly like a stripped down version of the original Nintendo bootstrap. It turns on the sound circuit even though it does not play any sound. Perhaps this is for compatibility with some game that expects it to be turned on on start-up. The boot ROM also clears the video RAM, sets the background palette and finally turns on the LCD circuit.
The boot ROM interestingly enough only stores a reference copy of the second half of the Nintendo logo, even though there’s plenty of unused space. So the first half of the logo could be incorrect and the program would still start. Incidentally, Gameboy Color has a similar bug where only the first half of the logo is checked, which has prompted some pirate publishers to get creative and make logo combinations like HotKid, Niutoude and Yiutoudz using the bottom half of the logo that they can control freely. Read more on Neofuji. It wouldn’t matter in this case anyway, as the logo isn’t shown on the screen, only checked.
But what’s weirder is what the boot ROM does if the Nintendo logo check fails. It then does a second check, against an alternative logo that perhaps says KIS or RIS, simulated to the right for illustrative purposes. Again, the boot ROM only stores half the logo in this case as well, so there’s no way of getting the full logo without finding a matching game that contains the full logo in it header.
It may be checking specifically for a certain game by the same publisher. But why would the game have an alternative logo, when this logo is not shown on the Game Fighter, and would hang a genuine Gameboy? The only logical conclusion I can come to is that the game that this check was made for does a logo swap. Some examples of logo swaps (apart form the half logo hacks) can be found in the Neofuji post linked above. A logo swap works by switching in memory containing your own logo when the boot ROM copies the logo the into VRAM, and then switching back to the real logo when the check is done. This would normally fail on the Game Fighter, since it checks the logo much earlier than a real Gameboy.
But in the end, we may never know. If you have any clue which pirate publisher KIS/RIS/??? is please let me know!
Thanks to BennVenn for coming up with this method of accessing the boot ROM!
mGB, trash80’s Gameboy MIDI synth for use with Arduinoboy has support for selecting various waveforms for use with channel 3. If you look at the data loaded into wave RAM and search for it in the ROM you find nothing. Is the data generated algorithmically, perhaps? Let’s look into it.
I may actually have the mGB source code lying around somewhere, but this is easy to figure out using BGB. First add an access breakpoint (debug, access breakpoints) to FF30 (the lowest address in the channel 3 wave buffer) and hit run. If mGB was already started, change the waveform or reset the CPU to make the write happen again.
If you read the code, it seems like the waveform index is loaded from somewhere, and is then added to C14D, an address in RAM. If you go to that address in the data panel in the bottom of the window you can confirm that there are indeed waveforms there. Let’s place a write breakpoint to the address in RAM, maybe disable the old one, and rinse and repeat.
The code that creates the waveforms in RAM consists of a long row of instructions on the form
ld de,C14D ld a,01 ld (de),a ld de,C14E ld a,23 ld (de),a
This is likely the GBDK C compiler’s output from something like…
…and so on. Or this may just be an example of the compiler doing something really stupid when you define a const array. The performance and size of such code compared to an optimized alternative is not exactly flattering, but that doesn’t matter much in this case.
In other words, the actual data starts at address 3C9C and is spaced out so every 6th byte is waveform data. Above, the bytes for 0th waveform (starting at the hex editor’s cursor) are marked in a light, not-quite-cyan, blue. Click for a bigger image.
Can we do it better? Let’s try to patch the ROM to store the data in a linear fashion and then copy it into the position in RAM where it is later used. Or even better – as the wave data is not likely to be modified after the being copied into RAM – make the program read them directly from ROM instead of from RAM.
First start mGB and grab the wave data from RAM, for use later. First a back of the envelope calculation: there are 16 waveforms, each of size 16 bytes, leading to a total of 256 bytes. When mGB is at the main screen, select file, memory dump in the debugger. Choose a place to save the dumped file. We know from above that the data is stored starting at C14D, so enter that as the starting address. As size, enter 100, hex for 256.
Then go to 3C98 in the disassembly, which is the first instruction of the row of instructions that are used to fill up the wave data in RAM. The similar instructions before that one are used to initialize other memory. Don’t touch those.
We know form above that there are 256 bytes of wave data. We also know that it takes 6 bytes worth of instructions to copy one byte to memory. This means we can now calculate the end of this code area, or rather, the first address containing other code as 3C98+(100*6)=4298 (all hex, mind you!) You can now go to this address in the disassembly to confirm it’s correct. Press ctrl+G and enter 4298.
The group before that position reads
ld de,C24C ld a,03 ld (de),a
C24C is 255 bytes after C14D, and the next byte being written to, C254, is non-consecutive. This means 4298 seems like the correct address to jump to.
Now go back to 3C98 and create a jump to 4298. You can do this by simply placing the cursor at the correct place and starting to type jp 4298. When you type the first character, an assembler winow pops up to let you continue writing the instruction. Pressing enter or clicking ok will assemble the opcode. If all goes well you will now see it in the disassembly.
So, now to plan for where to put the 256 (100 hex) block of data. The area 3C9B-4297 is now “dead”, consisting of code that will never be executed. We could now place the data directly at 3C9B and 100 (hex) bytes forward. Or we could make our lives easier when editing and put the data at something easy to remember 4000 and 100 (hex) bytes forward. Any area that fits within the now dead memory area is ok, which is true for 4000-40FF.
Now press ctrl+G and go to 28B3, which is the place where the base address for the waveform lookup is loaded into the HL register. Start typing ld hl,4000 and press enter.
Time to save the file. Normally I would recommend to do file, fix checksums at this point, but since the file will be further edited, the checksum will change later anyway. So select file, save ROM as and save the file, preferably under a new name. Use whatever method your hex editor provides (or doesn’t provide) to overwrite 100 (hex) bytes from 4000-40FF. In my trusty ol’ xvi32, I had to first delete 100 bytes, then select file, insert and select the file previously saved containing the waveforms.
You can select an address to go to by pressing ctrl+G. You can select characters by selecting edit, select
A better hex editor may have a better method of overwriting an area with data form another file.
Confirm that the newly inserted waveform data is located at 4000 and not anywhere else. Press ctrl+page down to go to the last address. Confirm that the file is the right size by checking that the last address is FFFF or 65535 (depending on whether you are on the hex or character side of the display.) Save.
Load in BGB and confirm that the ROM doesn’t crash. To get the right warm fuzzy feeling inside, select file, fix checksums and then file, save ROM as, again.
For your convenience, you can download the modified ROM here.
I’ve got mail! What’s inside? A cartridge of Furrtek’s second, and so far, latest game Airaki. The envelope (as well as the title field in the ROM header) is marked with the number 1, suggesting I’m receiving the first cartridge of this batch. The cartridge comes in a resealable “drug bag”, and there’s also an instruction sheet explaining the game mechanics.
The game comes in a generic “game” shell, a type often used by pirates. The cartridge has a label with our muscular furry hero holding his massive sword in an erect position. *cough*
A few years ago, I bought a PSU for my Dell D600 on eBay which turned out to be a really crappy Chinese pirate PSU which suffered badly from interference. When I wanted to fix the interference by connecting the plug to a different, and genuine Dell PSU, I stumbled upon a problem. All modern Dell PSU’s have a ID feature to confirm that the PSU is genuine, using a 3rd pin center pin connected to an EEPROM. If this chip does not match, the laptop won’t allow the battery to be charged. While a less scrupulous 3rd party manufacturer can nag one of those ID chips from a broken genuine Dell PSU and pass the test with a any poor quality PSU, I had to transplant this ID chip somehow, to make the PSU work.
I recently got another Dell laptop with a broken PSU, which I could have if I could fix the PSU. Like last time, I decided to splice the cable with a board containing the chip.
This board was made from veroboard, and ws a bit bulky, so I decided to design and order a new one. This posted is an illustrated guide to how this board is used.
Pocket Music for Gameboy Color refuses to run on GBA. It shows a screen similar to when you run the game on a monochrome Gameboy, saying “this game is intended only for use with a Gameboy Color.”
Maybe they decided to do this because the sample playback sounds like crap on GBA. This patch fixes both those problems. It bypasses the GBA check so you can use Pocket Music on GBA, and it uses my “antispike” fix (also seen in LSDj) to almost completely remove the whine from the sample playback.
Use an IPS patcher to apply the patch to the Pocket Music GBC ROM.
Gameboy project week 9: The EMS cartridges: Something old and something new. Something black and something blue. And how sloppy cartridge design affects you.March 4th, 2013
Top left: EMS 32M cartridge, rev 2. Top right: EMS 32M cartridge, rev 1.
Bottom: EMS 64M USB cartridge.
The HKEMS company is probably the oldest manufacturer of Gameboy flash cartridges still in business. Among the original era companies, the most famous was probably Bung, but it eventually collapsed under the legal threats from big N. EMS survived, however, and continues to develop its line of products, including their 64 Mbit GB cartridge.
There’s a thread in the Gameboy dev subforum of NESDev about trouble using the cartridge with certain games. In that thread, I said in good faith that EMS cartridges have good emulation of MBC1’s quirks, but also that I needed to investigate it further. Which is what I’ve done this week. And, I was wrong, at least regarding the newer cartridges.
First a bit of background: The Gameboy CPU is an 8 bit CPU with a 16 address bus. This allows the CPU to address up to 65536 bytes of memory. In that space, it needs to fit all it’s ROM, RAM and control registers. If you want to address more memory than that, you cannot do it directly, but need to employ a method called bank switching. This is done by a chip called an MBC (memory bank controller) which is situated on the cartridge. The normal memory layout seen by the GB CPU is one 16 kiB area of ROM called bank 0 which never changes, and one selectable 16 kiB area to which any 16 kiB window of the ROM (with a couple of exceptions) can be switched in. In bank 0, a game or program can store code and data it wants to have easily accessible.
However, for a flash cartridge this might present a problem if you want the user to be able to choose between different games/programs stored in the flash memory. This is where multi ROM capabilities come into play. Just like the banking lets you select a small window of memory that is visible in the Gameboy memory map, multi ROM lets you select an area of the flash chip which then appears to the Gameboy as a Gameboy cartridge.
From the hardware point of view, there’s an interface that lets you select which portion of the flash chip should be visible to the Gameboy CPU by performing a sequence of writes. If well designed, the multi ROM commands should not conflict with any other commands normally used by any type of MBC, or by writes which exist in a few games especially designed to mess up multi ROM and similar extended functionality and crash the program (as anti piracy feature.)
On the software side of things there’s a menu ROM which scans the flash memory for valid ROM images, presents a menu, and after a user selection performs the special menu sequence to switch the ROM, and finally jumps to the ROM entry point.
I sat down to investigate exactly what the menu ROM does, and what effect each command has on the cartridge. You can see the full report HERE. But here are some points I discovered:
The USB 64M cartridge (as well as the second revision of the blue, pre-USB 32M cartridges) have reduced functionality compared to the original revision of cartridge. This means that some ROMs will have to be patched in order to run on the cartridge in the future as well. Bummer!
The default menu ROM leaves SRAM open (unprotected, [$0000]<-$0A) without good reason. This could potentially trash one bank of SRAM, especially for games that don't use SRAM because they won't enable the SRAM protectopn again.
From a quick glance, it seems like the EMS 64M USB utility finally aligns the ROMs correctly to size boundaries when flashing multiple ROMs through the application. However, the menu still has trouble finding some ROMs in an experiment I did.
The EMS application also pads the files with $00 (all bits zero) instead of $ff (all bits one) which will contribute a microscopic amount to wearing out the flash faster. Will it actually matter, nah, but it’s a sign of not paying attention to detail.
As a proof of concept, I’ve released LittleFM 0.5.1 with integrated EMS multi ROM support. As it is a proof of concept, it does not come with a special patcher, check that ROMs are correctly aligned or handle save data at all. However, to show that I paid attention at least slightly to detail, it does not show repeated entries when used in an emulator, and also gives you an error message if the switching failed, unlike most similar menus.
You have to prepare a ROM yourself, by first patching an LSDj ROM with LittleFM as usual, then concatenating whatever ROMs you want at the end, respecting the alignment of course.
copy /b lsdj-4_7_0-lfm.gb + rom1.gb + rom2.gb out.gb cat lsdj-4_7_0-lfm.gb rom1.gb rom2.gb > out.gb
Then burn out.gb as the only ROM onto your EMS cartridge, USB or non-USBB. Press start to bring up the EMS multi ROM menu.
Since it doesn’t do save management or anything fancy it’s kind of pointless except maybe for musicians who want noise maker ROMs alongside LSDj on a cart. A future version will be able to store save data in a (compressed) LSDj-like file system, and also come as a standalone menu replacement without LSDj. But that’s for the future.
Gameboy project week 8: The white Nintendo Power official flash cartridge, a tale of reverse engineering, sweat and tearsFebruary 25th, 2013
This week’s project concerns this cartridge, the official Nintendo Power flash cartridge. It used to actually be sold by Nintendo for 2500 yen and you could by games from a special kiosk starting at 800 yen at the Lawson department store. (About US$27 and US$9 respectively at today’s exchange rate.) The kiosks are long gone, but the cartridges remain in the wild. (However one page in Japanese from the time remains. It mentions how the launch is delayed because of the earthquake in Taiwan, the massive 921 earthquake)
Since these are flash cartridges they are in theory writeable, which would make them useful for, for example Little Sound Dj or other homebrew software. However, writing to them has proven difficult. The currently only known way of writing to them is with a software called FMGBx by Mootan. The program works but requires flasher hardware that is difficult to get and and in all cases are using an LPT port interface. Options include Bungs GB Xchange, a modified GBA flasher and two homebrew solutions using discrete 74hc logic. Clearly, there’s room for improvement.
As per usual, this week’s project is fashionably late. This week’s project is an image viewer ROM which you can use to show custom graphics on the screen. This is for example useful for modders who want to display a logo on the screen when doing a photoshoot. I’ve been making these ROMs for people on request, but even so, I have been limited to using an old DOS utility (yes, DOS, not just command line) called PCX2GB. However, this application is slow and buggy, and with the advent of 64-bit operating systems, DOS applications can no longer run natively. So for every one of those ROMs I’ve made, I’ve had to start DOSBox, then include the files that PCX2GB output into the project and recompile the ROM.
This project sets out to change that. The project consists of two parts. A Gameboy ROM which can be patched with one or more images, and Python script that converts the image to the Gameboy tile format and does the appropriate patching. The result is a Gameboy ROM you can use to show the images.
The patcher is a Python script, which means you need to install Python to run it. You should install the 32-bit version of Python 2.7 — the one marked x86 instead of x86-64. Sorry to those who prefer 3.x.
Additionally, you need PIL, or Python Image Library which is used to load the image files.
How to use the patcher
To use the patcher you need to prepare as many images as you want, which are exactly 160*144 pixels and color reduced to contain a palette of exactly 4 colors/shades of gray. The actual color depth of the file can be anything, as long as the image contains exactly 4 unique colors. The image can be in any format that PIL can load, which is almost any format. Avoid using JPEG images however. Use PNG, GIF or BMP.
When you have correctly installed Python 2.7 and PIL, you can use the patcher by giving the following command, with sample output:
python pygbconv.py out.gb image1.png image2.png image3.png ===Processing image1.png Optimized image to 127 tiles. ===Processing image2.png Could not optimize image to below 256 tiles. Image stored as 360 tile image. ===Processing image3.png Warning! Image has only 2 unique colors (instead of exactly 4.) Color table will amended. Optimized image to 5 tiles. ===Processing image4.png Could not optimize image to below 256 tiles. Image stored as 360 tile image. ===Processing image5.png Could not optimize image to below 256 tiles. Image stored as 360 tile image. 1
The first agument, is the name of the GB file to be produced. This is then followed by any number of image file names (up to potentially 256 images.) The output tries to be informative about what the patcher is doing.
imagerom.gbbase is the actual viewer code. This is used as the base ROM for the finished product, thus the different file extension. If you run this standalone (which there is no point in doing) you will see an X shape and hear two dissonant tones, as a warning message.
The Gameboy software
The Gameboy software is dead simple. Press left or right to scroll through the pictures or press A to invert the palette
Note on the inner workings of the patcher and ROM
The Gamboy graphics hardware is working with tiles, which are addressable 8*8 pixel blocks. If an image has repeated instances of the same 8*8 pixel area, those can be addressed as the same tile. The patcher tries, if possible, to “compress” an image this by removing redundant tiles. However, if an image contains more than 256 unique tiles, it instead displays the image with no repeated tiles, for a total of 360 tiles being displayed on the visible area of the DMG screen.
The download also comes with a cople of image that you can play around with until you make your own image.