ALTTP Custom Title Screen

Intro

This is a tutorial for how to add a custom title screen for the SNES game The Legend of Zelda: A Link to the Past. This tutorial will require you to know how Hexidecimal numbers work and assumes that you know the programs yy-chr and ZCompress so keep that in mind.

The original title screen only uses 59 unique tiles for the background which can make it quite difficult to make a custom background. There are other methods to make more tiles available but those methods generally won't allow you to have enough tiles to have a unique tile in each square in the grid.

For a title screen that is the same dimensions as the original, you would need 576 tiles (18*32) for every tile to be unique and not re-used at all. In my case, I am reusing 16ish tiles but the tutorial should allow you to have all 576 unique tiles. If you want to have a bigger background than the vanilla you can, however, you will have to reuse tiles as adding more tiles than 576 will probably result in glitches. It is probably possible to have more tiles but that would require much more clever use of ASM and I'm not going to go over how you could do that here.

The Graphics:

In order to change the title screen, several changes need to be made. Due to the limitations of the SNES and how it processes graphics, only 16 colors can be displayed on one 8x8 tile in a tile map at a time. ALTTP uses 4 of these 16-color palettes to display the background by fading into each color from black.

Before and After Fade in:

Our image has to be made with these limitations in mind. I ended up only using 3 of the 4 color palettes available, one for logs and leaves that also have part of the background sky in them, one for tiles with leaves, dirt, and parts of the flowers, and one last one that would have logs, leaves, and dirt in the same tile. For example, if you were to look through each 8x8 tile you wouldn't find any tile that would have part of a flower in the same tile that has a piece of sky. since I have an extra palette, I could add another that would look like something like this:

But note that this would also mean that we don't have any space for the log or dirt colors in this palette.

Also something to note, when making your image, add the empty black space above and below the image so that a blank tile will appear in your tileset as seen below. The whole image itself should be 256x224 pixels

Original Title Screen:

Original Image:

It will then prompt you with this:

The Tilemap:

Now we have to make a tilemap for our image and change our graphics to a form the snes can use. A tilemap is essentially a set of instructions that the SNES uses to know where to put each tile, and whether the tile needs to be flipped or not. So we will now need to rebuild our image using a program called TileMap Studio.

First we need to import our image, to do this, under tools, go to Image to tiles and change the settings to be so: (image to the left)

Note: You need to change the "Start at ID:" address to $0C0 as it will allow the Assembly to point to the correct graphics later.

Now you should be seeing something like this to the right: The colors may seem messed up but don't worry, we will fix that later. It may also seem like there are fewer tiles than are actually in the image but this is because Tilemap Stuidio removes any duplicate tiles or flipped tiles as to save space.

You may have to rebuild the image, but it should set it up for you, however, under the tile tab, you will have to set up the palettes for each tile. This will tell the snes which palette to use to display each of the tiles. use palettes 2-5 as those are the 4 that will fade in.

Lastly, Export your tilemap by using the print tilemap button at the top.

When importing your image to Tilemap Studio, it should also have exported an image resembeling the image to the left. We need to take this image and put it into a program called SNESGFX. This will convert our graphics to a form the snes can use. Here are the settings you need to use:

Note: under the settings tab for arange colors, you will need to set this to manual so that we can re-arange our colors to the order that we want:

Then click save, you should now have a 2 .bin files, one that you just made in SNESGFX and one that we exported from Tilemap Studio.

The Assembly:

I won't explain every detail of the Assembly here. If you want to know how it works in more detail, I've left comments on there that should give you an idea.

However just some key things to note:

For the Assembly to read your tilemap and graphics, both files need to be in the same folder as the .asm file with the names "Tilemap.bin" and "Tileset.bin" for your tilemap and your graphics respectively.

The colors on the right-hand side of the color palettes are replaced with the assembly. This is because the game uses the palette for the "ZELDA" title and the subtitle (Hyrule Magic Area colors 1 Palette 3) twice in this scene. So if we change the background we will also inadvertently change the title colors. In order to change the colors used, you will need to change the values found on lines 101 - 119 (under ApplyNewColors), for example,

The line LDA #$1525 : STA $7EC350 will take the color 0x1525 and write it into slot 0x73C300 or the first color on the right hand side in the first color palette. the colors on the left side i inserted in Hyrule Magic under World colors 1 Palette 5. LDA #$1525 : STA $7EC352 will write to the second color, and so on.

If you would like a further explanation on how to convert a color to the snes format, check out this tutorial.

          
    org $0CC341 ;initialize some gfx settings at the beginning so that it displays correctly after the 
        JSL Main1 ;history cut scene plays And inserts the gfx to the vram and inserts a custom palette
    
    org $0CC27E ; disables BG 3 during the opening sequence as it is not used
        db #$11
    
    org $0CC51D ; disables BG 3 during the opening sequence as it is not used
        db #$01
    
    org $0CC2AE ; applies the tilemap after the original is placed in 
        JSL Main2
    
    org $0CEE39 ; at the beginning of the history cut scene reset the bg address
        JSL Reset1 
    
    org $0CCDE2 ; once the select screen appears, reset the bg address
        JSL Reset2
    
    org $218000
    
    ; ==============================================================================
    
    Main1:
    {
        JSL $00E384 ;replaces the Graphics_LoadCommonSprLong that was replaced
    
        LDX #$52 : STX $210B ; Changes where BG 1 pulls its graphics from
    
        LDA.b #$11 : STA $1C ;disables BG3
        LDA.b #$01 : STA $1D
        LDA.b #$11 : STA $212C
        LDA.b #$01 : STA $212D
    
        JSR ApplyGraphics ; inserts the new gfx into the vram
        JSR ApplyNewColors ; inserts the new colors into the cram
    
        SEP #$30
    
        RTL
    }
    
    ; ==============================================================================
    
    Main2:
    {
        JSL $0CC404 ; $64404* that was replaced 
    
        LDA $35 : CMP.b #$01 : BEQ .notfirst ;$35 is free ram that I am using to see whether this is the first time this is being run or not
    
        LDA.b #$01 : STA $35 ; if it is set $35 to 1 so we don't apply the tilemap again. otherwise, this causes part of the triforce to flicker
    
        JSR ApplyTileMap ; inserts the new tilemap into the vram
    
        .notfirst
    
        SEP #$30
    
        RTL
    }
    
    ; ==============================================================================
    
    Reset1:
    {
        JSL $1BEDF9 ;replaces the bit that was replaced Palette_ArmorAndGloves
    
        LDA.b #$1F : STA $1C ;re-enables BG3
        LDA.b #$1F : STA $1D
        LDA.b #$1F : STA $212C
        LDA.b #$1F : STA $212D
    
        LDX #$22 : STX $210B ;resets tile address
    
        RTL
    }
    
    ; ==============================================================================
    
    Reset2:
    {
        JSL $00E19B ;replaces the InitTileSets that was replaced
    
        LDA.b #$1F : STA $1C ;re-enables BG3
        LDA.b #$1F : STA $1D
        LDA.b #$1F : STA $212C
        LDA.b #$1F : STA $212D
    
        LDX #$22 : STX $210B ;resets tile address
    
        RTL
    }
    ; ==============================================================================
    
    ApplyNewColors:
    {
        REP #$20 ; Set A in 16bit mode
    
        ; note, this uses addresses like 7EC300 and not 7EC500 because the game 
        ; will fade the colors into 7EC500 based on the colors found in 7EC300
    
        LDA #$1525 : STA $7EC352 ;first row
        LDA #$1947 : STA $7EC354
        LDA #$1DA7 : STA $7EC356
        LDA #$2E2B : STA $7EC358
        LDA #$4B32 : STA $7EC35A  
    
        LDA #$12A6 : STA $7EC370 ;second row
        LDA #$092D : STA $7EC372
        LDA #$156E : STA $7EC374
        LDA #$19B1 : STA $7EC376
        LDA #$2214 : STA $7EC378 
        LDA #$7F61 : STA $7EC37A 
        LDA #$7AA0 : STA $7EC37C 
        LDA #$031B : STA $7EC37E 
    
        LDA #$092D : STA $7EC392 ;third row
        LDA #$156E : STA $7EC394
        LDA #$19B1 : STA $7EC396
        LDA #$2214 : STA $7EC398 
    
        SEP #$20 ;Set A in 8bit mode
    
        RTS
    }
    
    ; ==============================================================================
    
    ApplyGraphics:
    {
        REP #$20 ; A = 16, XY = 8
        LDX #$80 : STX $2100 ;turn the screen off (required)
        LDX #$80 : STX $2115 ;Set the video port register every time we write it increase by 1
        LDA #$5C00 : STA $2116 ; Destination of the DMA $A000 in vram <- this need to be divided by 2
        LDA #$1801 : STA $4300 ; DMA Transfer Mode and destination register "001 => 2 registers write once (2 bytes: p, p+1          )"
        LDA.w #YourBitmap : STA $4302 ;This is the source address where you want gfx from ROM
        LDX.b #YourBitmap>>16 : STX $4304
        LDA #$4600 : STA $4305 ; size of the transfer 3 sheets of $800 each
        LDX #$01 : STX $420B ;Do the DMA 
        LDX #$0F : STX $2100 ;turn the screen back on
    
        RTS
    
        YourBitmap:
        incbin Tileset.bin
    }
    
    ; ==============================================================================
    
    ApplyTileMap:
    {
        REP #$20 ; A = 16, XY = 8
        LDX #$80 : STX $2100 ;turn the screen off (required)
        LDX #$52 : STX $210B ;52
    
        LDX #$80 : STX $2115 ; Set the video port register every time we write it, increase by 1
        LDA #$0000 : STA $2116 ; Destination of the DMA $0000 in vram <- this need to be divided by 2 
        LDA #$1801 : STA $4300 ; DMA Transfer Mode and destination register "001 => 2 registers write once (2 bytes: p, p+1          )"
        LDA.w #YourTileMap : STA $4302 ; This is the source address where you want gfx from ROM
        LDX.b #YourTileMap>>16 : STX $4304
        LDA #$0800 : STA $4305 ; size of the transfer 3 sheets of $800 each
        LDX #$01 : STX $420B ;Do the DMA 
        LDX #$0F : STX $2100 ;turn the screen back on
    
        RTS
    
        YourTileMap:
        incbin Tilemap.bin
    }
    
    ; ==============================================================================

        

Other things to note:

The Assembly uses the space at the very bottom of the SNES's VRAM, if the image is the same size as mine (256x224) and has the same amount of black space at the top and bottom, it should all fit into this space, if it is bigger, it will write into the space where your tilemap would be and you'll see some glitched graphics. Using a bigger image will most likely require much more work to pull off and a lot of changes to the Assembly.

Since BG3 uses a tilemap that normally would be in the space where we inserted our graphics, the Assembly disables it durring the title sequence, and then later re-enables it when the history cut-scene plays or when the player goes to the select screen.

To apply this patch, I used a program called asar but there are other ways to apply it. To use asar, run the program within a cmd or a terminal and then follow the instructions it gives you by typing in the name of the .asm file and name of the rom.

Sub-Title and Sword:

Finally, the sub-title and the sword were edited using yy-chr a graphics editor commonly used to edit SNES and other retro console graphics and zcompress a program specificly designed to extract the graphics from ALTTP and then re-insert them. Further tutorials for how to use them can be found on the Zeldix Forums.

If you have any other questions, you can send me a message on discord: Jared_Brian_#7167 or you can find more help on the Zeldix Forums.

Credit to Zarby89 for helping me with much of the ASM for this project.

Logo for the hack