Working with ZOSE

From ZeldaHacking Wiki
Jump to navigation Jump to search

Eventually to achieve some custom effects or puzzles you will need to use ZOSE, while not too complicated some introduction is required. Read this page to get a basic idea about ZOSE and what can be done with it.

Be sure to also check ZOSE.html for the opcode list, which is bundled with ZOSE.

Introduction and Overview

ZOSE is a program which is used to create custom scripts in Oracle of Ages ROM hacks. It works by preforming an underlying change to add necessary assembly and replacing the 1 interaction. It is like a low level programming language which follows one instruction after another and comes with its own special instructions and syntax which must be used properly to create a functioning script for you to use. In total you can have a maximum of 255 scripts used in the ROM.

Preparations for Use

In order to use ZOSE to be used you must first prepare the ROM which you are planning to use. The following steps are required to get started:

1) You must open you ROM in ZOLE at least once. This expands the file size of the ROM from 1MB to 2MB which is used for various aspects when hacking Oracle of Ages as well as creating more space which you can use for scripts.

2) You must open your ROM in ZOSE and click "Yes" to the prompt which it asks you so it can preform the necessary changes to the file. This prompt is asking if you are willing to give up the use of the Great Moblin event in order to make use of custom scripts. If you do not wish to remove this event you can select "No" and it will not preform any changes but you will not be able to make use of custom scripts.

After preparations are completed you can start making scripts. The interaction ID for your scripts start at 7200 until 72FF.

Warnings and Quirks

There exist a few things which one must keep in mind when making scripts and using ZOSE. The following is a list of them:

  • 1) If you don't write script correctly, it tells you there was a compile error when you try to compile it. This is because either your script has an unknown commands, improper arguments, missing arguments or with invalid values. The incorrect command will be highlighted in red so you can easily find where it is.
  • 2) The Decompile Interaction only works with certain interactions. Mainly those in the 72XX and 20XX families. Others may work but are not guarantied. This function is a Gameboy instruction emulator which decodes the instructions back into a human readable script. HOWEVER! This feature has not been up kept over the courses of various updates so it may be unreliable so use at your own risk.
  • 3) There exists limited space in bank C. A lot has been moved but a lot was also needed for the interaction 72 pointers. When looking for space for scripts starting address, go either before 33FDA or as far back as possible!
  • 4) You cannot have more than one script in a room that uses the 3-byte-jump command. This is because of the fact that when a jump 3-byte is executed, 256 bytes are copied into RAM for the game to execute the script. When the second script loads, it will overwrite the first script in RAM and cause it to malfunction. This being said, you can have multiple 3-byte jumps in one script.
    • As a bonus caveat, this means you can only have one 72XX interaction at a time. This is because all 72XX interactions begin with what is equivalent to a 3-byte jump (copying the script to RAM).
  • 5) The 72XX interaction when placed acts like an 8x8 invisible solid tile wherever it is placed. It is recommended that it either be placed at 0,0 or somewhere the player cant access.
  • 6) You cannot use a script which uses a jump 3-byte in underwater areas as they WILL NOT WORK. This is because the wave effect uses the same memory space which the 256 bytes would be copied to and the waves will continuously overwrite that space.
  • 7) Most script commands don't take effect until AFTER a room has finished transitioning.
  • 8) When jumping to a 3-byte address the chunks from there on after only compile in 256 byte chunks and anything after that will NOT work. This is again because of the limit of how its loaded into memory. It is advised to find more free space and implement another 3-byte jump to there to continue. ZOSE will notify you if the space required if greater than 256 bytes.

Examples

Basic Examples

The following is the header for every script which will be written in ZOSE (Not including comments in brackets):

WriteLocation XXXX (An address outside of bank C with enough free space, can be two or 3 bytes)
SetInteraction72 XX (The second half of your interaction id, always start with 00 and go up from there by 1 per script)

The above section is the declaration of the start of a script.
The first line which contains WriteLocation XXXX states where the starting address of the script will be written to.
The second line which contains SetIntraction72 XX states which interaction ID will be tied to this script when placed in ZOLE.

After this top header you will start to write the body of your script to execute what you want to preform.

Eg 1:

WriteLocation 100002 (Address location 100002 is free space and the script will be stored at that STARTING address.)
SetInteraction72 00 (The hex value 00 is the 2nd part of the id for a type 2 interaction. Eg: 7200)

CheckItemFlag (Check the item flag for the current room. If NOT set it will continue with script else it will stop.)
SpawnItem 28 0C (Spawns an item at Interactions YX location with the ID 280C. (A once collectable 30 rupee))

The above simple script will check if the item flag for that room is set; if it is set then it will stop the script, but if not then the script will continue to the next line and spawn an item with the ID 280C which is a 30 rupee collectable item.
When the item is collected the item flag will be set and any further attempts to collect the item by reentering the room again will be stopped by the ChekItemFlagon the line above the SpawnItem 28 0C.


Intermediate Examples

A big part of scripts involve checking/writing values from/to memory. They will either hold the execution of the script until the value at the memory address matches that value or jump to an address in the ROM if a memory address is equal to a specific value.

Eg 1:

writelocation 100008 (Address location 100008 is the next free space after the previous script.)
setinteraction72 01 (We now have one script ID already used so now we need to use another. In this case the next numerical ID is 7201.)

checkmemory CCA0 7 (Reads memory value from RAM address CCA0 and waits until it is a value of 7.)
setmemory CCA0 40 (Sets a memory value of 40 to RAM address CCA0.)

The above script will check memory address CCA0 which is the memory address which keeps track of the bit positions of the button interactions 90X & 98X and which are pushed/not pushed. If it has a value of 7 such that buttons with the ID 900, 901 & 902 are all pushed it will then write a value of hexadecimal 40 to memory address CCA0. (Effectively setting bit position 6 and clearing bit positions 0,1&2)
This is done so that a door with ID's 1E04-1E07 and an X value of 6 will open when the value is written to memory. This roundabout way is because doors use the X value to check the BIT POSITION of the switch which will activate the door and not so much the current value in memory of address CCA0. (This is useful for cases where you wish to have more than two switches to open a door.)

WARNING!: Always make sure you know what memory address you are reading/writing to by referencing the ZOSE2.html which comes with it when using CheckMemory XXXX Y and SetMemory XXXX Y.
Reading from wrong locations will halt scripts indefinitely or cause erratic behavior depending on the values at that location.
Writing to wrong locations can cause corruptions of the RAM as well as unknown operation depending on what was overwritten with the worst case being a crash.


Eg 2:

writelocation 100012 (Address location 100012 is the next free space after the previous script.)
setinteraction72 02 (You get the idea, increment ID by 1 for next script.)

maketorcheslightable (Calls an assembly script to allow lighting and monitoring of torches. Equivalent to: asm15 4f4b)
checkmemory CC8F 2 (Reads memory value from RAM address CC8F and waits until it is a value of 2.)
checkitemflag (Checks item flag in current room.)
playsound 4D (Plays a sound effect of specified value. (Puzzle solved SFX))
createpuff (Creates a puff at the interaction's location.)
setdelay6 (Delays the script from progressing.)
settile F1 (Sets the tile F1 at the interaction's location. (Closed chest tile))
setstate FF (Writes a memory value of FF to the interaction for its state.)

The above script will call the assembly needed to preform the lighting and memory management of torches in the room, check memory location CC8F for a value of 2 (the memory location of currently lit torches which goes up by 1 every time one is light), check if the item flag is set, play the puzzle solved sound effect, create a puff effect at the current interaction's XY position, delay the script for 30 frames, set a closed chest tile at the interaction's location and set the state of the interaction to done.

There are a few other commands which give more of a audio visual cue to the player such as:
PlaySound XX, CreatePuff, SetDelayX, and some others which can be found in the ZOSE2.html reference.

You can set tiles in a room to change layout of the room by using the SetTile XX command but keep in mind that they will only take effect in the current room when they are encountered in the script but also only update when entering a room AFTER the room transition is completed which can look weird as the changes will pop into existence after the transition is done. (There is a way around this but requires some understanding of how dungeons keep track of permanent changes to tiles)

Advanced Examples

The last major part of scripts are jumps. They can be used to jump directly to other parts of ROM memory or jump based on if a memory location is equal to a specified value.

Eg 1:

WriteLocation 100023
SetInteraction72 03

SetDelay8
SetTileAt 55 F7
SetDelay8
SetTileAt 55 A0
Jump3Byte 100023

The above script waits for 60 frames, changes the tile at YX position 55 to a pit tile, waits another 60 frames, changes the tile back to a floor tile and then jumps back to the start of the address of the script to loop the script.
It uses a Jump3Byte XXXXXX command which will unconditionally jump to the address specified when it is executed.

Eg 2:

WriteLocation 10002F
SetInteraction72 04

CheckItemFlag
CheckMemory CCA0 1
SetMusic 2D
CreatePuff
PlaySound 98
SetDelay6
SetTileAt 5D F1 
SetCoords 58 38
CreatePuff
PlaySound 98
SetTileAt 38 A0
SetCoords 00 00
SetDelayC
SetDelayC
SetDelayC
Jump3ByteMC CCA0 1 10005D 
PlaySound F0
PlaySound 4D
SetMusic 14

WriteLocation 10005D
PlaySound F0
PlaySound 5A
SetTileAt 7E B0
SetMusic 14

The above script will check if item flag is set and if so stop the script, else it will continue. It then waits until memory location CCA0 has a value of 1 (Switch with ID 900 has been pressed), set the music for this room to the miniboss music, create a puff at its current location, play the puff SFX, delay the script for 30 frames, set tile at YX location 5D to a closed chest tile, set the coordinates of this interaction to location YY XX of 58 38, create a puff at the interaction's current location, play the puff SFX, set the tile at YX location 38, set the coordinates of this interaction to YY XX of 00 00, delay the script for 240 frames 3 times, if the memory location CCA0 has a value of 1 then jump to ROM address 10005D where it will stop the music, play the "Wrong" SFX, set the tile at YX location 7E to a wall tile and set the music back to dungeon 2's music, else it will stop the music, play the "Right" SFX and set the music back to dungeon 2'd music.

There are two types of jumps which exist: 2 byte jumps and 3 byte jumps.
The 2 byte jump allows you to jump up to 256 bytes from the start of the script.
The 3 byte jump allows you to jump anywhere in the ROM.

WARNING!: There exists two key things you must keep in mind when jumping:
1) When using a 3 byte jump you can only load 256 bytes after you jump to that location, so try to code your scripts to be in 256 byte chunks or less.
2) Trying to jump to an address before or 256 bytes after your last 3 byte jump using a 2 byte jump you WILL crash the game as it will not be able to handle it.

Other Examples

General Advice and Pointers

Jump Commands

This page mentions "2-byte jump this" and "3-byte jump that", but what does that mean?

Scripts can operate in two ways:

  • a) Within bank C; 2-byte jumps can jump anywhere in bank C.
  • b) Within RAM copied from any bank; 3-byte jumps copy a script to RAM and run it there.

In practice, custom scripts use option b due to the limited space in bank C. The tradeoff is that only one script can use this space in RAM at a time.

2-byte jumps can still be used with option b; they can jump to within 256 bytes after the start of the script in RAM (in other words, within 256 bytes of where the last 3-byte jump occurred).

ZOSE-exclusive opcodes*
Opcode Jump type
Jump3Byte** 3-byte
JumpRoomFlag 3-byte
JumpTileCheck 3-byte
Jump3ByteMC 3-byte
JumpRoomFlagO 3-byte

* These opcodes are patched in with ZOSE and are not part of the original game. The disassembly doesn't support them out-of-the-box.

** The "loadscript" opcode is identical to the "jump3byte" opcode as of ZOSE v0.7.00. Consider "Jump3Byte" to be deprecated.

Built-in opcodes
Opcode Jump type
LoadScript 3-byte
Jump2Byte 2-byte
JumpTable_MemoryAddress 2-byte
JumpIfRoomFlagSet 2-byte
JumpIfC6xxSet 2-byte
JumpIfGlobalFlagSet 2-byte
JumpIfTextOptionEq 2-byte
JumpAlways 2-byte
JumpTable_InteractionByte 2-byte
JumpIfMemorySet 2-byte
JumpIfTradeItemEq 2-byte
JumpIfNoEnemies 2-byte
JumpIfLinkVariableNeq 2-byte
JumpIfMemoryEq 2-byte
JumpIfInteractionByteEq 2-byte
JumpIfItemObtained 2-byte