• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Completed Generator

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
423
Create new data objects in RAM with <generator>!

This function can be used to generate, format, and maintain allocations for abstract data containers in Melee’s scene-persistent memory heap. Each container may be given a number of header bytes, a number of n-byte array elements, and hidden padding for structural and descriptive metadata:


The element array can be used to build basic data structures, while the header padding can be used to makes some abstraction from the resulting set. This may ease the process of parsing, modifying, searching, and sorting large, dynamic collections of data.

A number of options in the argument interface make it easy to automatically format the metadata padding hidden inside of each element to create a variety of useful data structures:


Allocations made in the memory heap in this fashion will automatically deallocate on each new scene change.

An optional "pointback" protocol may be used to pair generated containers with pointer variables that can be kept in static memory. This will cause the function to only generate a new allocation when it detects an expired allocation caused by a scene change; making it possible to initialize data without littering functions like StartMelee with dozens of hooks:


UnclePunch UnclePunch T tauKhan SinsOfApathy SinsOfApathy DRGN DRGN

---

<generator>

Get the Function Module here:
Code:
-==-

Generator
Allows for the generation of memory containers that persist for the duration of a Melee scene.
More info at - https://smashboards.com/threads/generator.480890/
[Punkline]
<generator> ALL
# generate and or format an abstract data container, with optional metadata

# arguments:
  # r3 = Options:
  #     FFFF 0000 = Metadata Autoformat Options (see autoformat key, below)
  #     0000 FF00 = Option Flags
  #     0000 00FF = Extra Metadata Words (for each element)
  # r4 = Header Args:
  #     FF00 0000 = Extra Metadata Words (for header)
  #     00FF FFFF = Header Size (byte count)
  # r5 = Element Size (byte count)
  # r6 = Element count
  # r7 = special argument:
  #      - Used as a pointback destination      if using bPBack option
  #      - Used to reference an existing alloc  if using bPreAlloc option
  # r7...r12 = may also be used to create arguments for various Autoformat specifications in r3

# returns:
  # r3...r6, cr1...cr7, ctr = unchanged from given arguments
  # r7  = Header Metadata Size
  # r8  = Element Metadata Size
  # r9  = Total Size
  # r10 = Address of HEADER
  # r11 = Address of FIRST container Element
  # r12 = Address of LAST container Element
  # cr0 eq == FALSE if returning newly generated/formatted allocation
  # cr0 eq == TRUE if using pointback protocol, and old alloc is still valid

# The r3 value is broken up into 13 fields:

# r3 Autoformat Options:
# - Up to 4 format IDs can be specified to create automatic metadata.
# - Each non-0 ID will add 4 bytes of metadata to the size of each element.
  # F000 0000 = Format ID for Element offset -0x4
  # 0F00 0000 = Format ID for Element offset -0x8
  # 00F0 0000 = Format ID for Element offset -0xC
  # 000F 0000 = Format ID for Element offset -0x10
  # 0000 00FF = Additional words of blank padding for each element

# Autoformat Key:
# - These IDs are tied to small callback routines that can be used to format a container.
# - IDs B...F can be used to pass custom callback routines for instantiating complex data types.
  # 0 = (terminator)
  # 1 = link to previous sibling
  # 2 = link to next sibling
  # 3 = link to allocation header
  # 4 = link to first sibling
  # 5 = link to last sibling
  # 6 = enumerate unique key
  # 7 = write value from r7
  # 8 = write value from r8
  # 9 = write value from r9
  # A = write value from r10
  # B = execute callback in r9
  # C = execute callback in r10
  # D = execute callback in r11
  # E = execute callback in r12
  # F = execute private process (argument = LR + 4)

# r3 Boolean Options:
# - note, the value of these boolean CR IDs are leftshifted <<8 and put into saved cr2, cr3
# -- the masks in the comments show the raw r3 fields, and do not match the resulting CR IDs
  #.set bSelfLink, 8  # 0000 8000 = Allow pointing to self in autoformat
  #.set bSkipH,    9  # 0000 4000 = Skip header input options (r4 = 0)
  #.set bSkipE,    10 # 0000 2000 = Skip Element input options (r5, r6 = 0)
  #.set bPBack,    11 # 0000 1000 = Use Pointback Protocol (r7 = pointback)
  #.set bNoZero,   12 # 0000 0800 = Skip zeroing out allocations on format step
  #.set bCircular, 13 # 0000 0400 = Make Sibling relationships circular, instead of terminated
  #.set bPreAlloc, 14 # 0000 0200 = Use given alloc instead of generating (r7 = prealloc)
  #.set bMetaDesc, 15 # 0000 0100 = Write allocation description in header metadata

# mask format, for bools in r3
  #.set mSelfLink, 0x8000
  #.set mSkipH,    0x4000
  #.set mSkipE,    0x2000
  #.set mPBack,    0x1000
  #.set mNoZero,   0x0800
  #.set mCircular, 0x0400
  #.set mPreAlloc, 0x0200
  #.set mMetaDesc, 0x0100

70601000 2C870000 7C0802A6 9421F000 90011004 91810F64 91610F60 41820054 41A4000C 39400001 480002B0 81870000 548055BA 7D606051 546CE6B4 7D6C5850 40800030
bl <MenuController_Base>
800C0000 818BFFFC 7C076000 5400443E 818BFFF8 7C806000 4C423202 4082000C 81470000 4800026C 5460421E 7D800026 7C038120 BE010F80 91810F3C 7E0902A6 92010F38 39810F40 7C6C05AA 40A90008 38800000 40AA000C 38A00000 38C00000 5497023E 7CB82B78 549A55BA 7CD93378 547B15BA
lis r4, <<generator>>@h
38000005
ori r4, r4, <<generator>>@l
7C0903A6 38840210 5463001E 3A210F30 5463203E 54601E79 2C800058 4C841102 41A40008 3B7BFFFC 3B7B0004 7CC40214 94D10004 4022FFDC 40AB0008 3B5A0008 40AF0008 3B5A0020 7C18DA14 7F9901D6 7C17D214 7F9C0214 7F83E379 41A1000C 39400003 48000198 40AE0014 7CE33B78 41A40010 39400002 48000184
bl <hsdAllocMemPiece>
7FA3D215 3A60FFFF 7C1DBA14 3A800000 7C83E214 3AA00000 7FF82050 7FDB0214 7FD6F378 41A0000C 39400004 48000150 418C0010 38800000 7F85E378
bl 0x80003130
7CDAE850 40AB0024
bl <MenuController_Base>
800C0000 5403443E 80810F50 38C60008 9066FFF8 9086FFFC 93A40000 40AF0008 7EE605AA 7E58DA14 7C12C9D7 408100F4 3A730001 7C93C800 7EB4AB79 7ED0B378 7ED5B378 7ED69214 408400D8 7C95F800 40A60008 3AC00000 40AD0014 40800008 7FF4FB78 40860008 7FD6F378 3A210F30 84110004 7C0903A6 4E800420 4BFFFFB4 60000000 7E83A378 48000080 7EC3B378 48000078 7FA3EB78 48000080 7FC3F378 48000068 7FE3FB78 48000060 7E639B78 48000068 80610F50 48000060 80610F54 48000058 80610F58 48000050 80610F5C 48000048 80610F58 48000024 80610F5C 4800001C 80610F60 48000014 80610F64 4800000C 80611004 38630004 7C6803A6 4E800021 4BFFFF6C 41880010 7C03A800 40A20008 38600000 9470FFFC 4BFFFF54 BF410F50 39810F40 7C6C04AA 4C421182 81610F38 81810F3C 7D6903A6 7D87F120 BA010F80 81610F60 81810F64 80011004 38211000 7C0803A6 4E800020

# portable handle for vanilla HSD allocation function
# r3 = desired size
<hsdAllocMemPiece> NTSC 1.02
b 0x80381FA8
<hsdAllocMemPiece> NTSC 1.01
b 0x803812C8
<hsdAllocMemPiece> NTSC 1.00
b 0x803800F4
<hsdAllocMemPiece> PAL 1.00
b 0x80381EAC

# portable handle for fetching address of Menu Controller Scene IDs
<MenuController_Base> NTSC 1.02
3D808048 398C9D30 4E800020
<MenuController_Base> NTSC 1.01
3D808048 398C9050 4E800020
<MenuController_Base> NTSC 1.00
3D808047 398C7D68 4E800020
<MenuController_Base> PAL 1.00
3D808047 398CAB38 4E800020
Code:
-==-
!
ASM - Generator
Allows for the generation of memory containers that persist for the duration of a Melee scene.
More info at - https://smashboards.com/threads/generator.480890/
[Punkline]
<generator> ALL
# generate and or format an abstract data container, with optional metadata

# arguments:
  # r3 = Options:
  #     FFFF 0000 = Metadata Autoformat Options (see autoformat key, below)
  #     0000 FF00 = Option Flags
  #     0000 00FF = Extra Metadata Words (for each element)
  # r4 = Header Args:
  #     FF00 0000 = Extra Metadata Words (for header)
  #     00FF FFFF = Header Size (byte count)
  # r5 = Element Size (byte count)
  # r6 = Element count
  # r7 = special argument:
  #      - Used as a pointback destination      if using bPBack option
  #      - Used to reference an existing alloc  if using bPreAlloc option
  # r7...r12 = may also be used to create arguments for various Autoformat specifications in r3

# returns:
  # r3...r6, cr1...cr7, ctr = unchanged from given arguments
  # r7  = Header Metadata Size
  # r8  = Element Metadata Size
  # r9  = Total Size
  # r10 = Address of HEADER
  # r11 = Address of FIRST container Element
  # r12 = Address of LAST container Element
  # cr0 eq == FALSE if returning newly generated/formatted allocation
  # cr0 eq == TRUE if using pointback protocol, and old alloc is still valid

# The r3 value is broken up into 13 fields:

# r3 Autoformat Options:
# - Up to 4 format IDs can be specified to create automatic metadata.
# - Each non-0 ID will add 4 bytes of metadata to the size of each element.
  # F000 0000 = Format ID for Element offset -0x4
  # 0F00 0000 = Format ID for Element offset -0x8
  # 00F0 0000 = Format ID for Element offset -0xC
  # 000F 0000 = Format ID for Element offset -0x10
  # 0000 00FF = Additional words of blank padding for each element

# Autoformat Key:
# - These IDs are tied to small callback routines that can be used to format a container.
# - IDs B...F can be used to pass custom callback routines for instantiating complex data types.
  # 0 = (terminator)
  # 1 = link to previous sibling
  # 2 = link to next sibling
  # 3 = link to allocation header
  # 4 = link to first sibling
  # 5 = link to last sibling
  # 6 = enumerate unique key
  # 7 = write value from r7
  # 8 = write value from r8
  # 9 = write value from r9
  # A = write value from r10
  # B = execute callback in r9
  # C = execute callback in r10
  # D = execute callback in r11
  # E = execute callback in r12
  # F = execute private process (argument = LR + 4)

# r3 Boolean Options:
# - note, the value of these boolean CR IDs are leftshifted <<8 and put into saved cr2, cr3
# -- the masks in the comments show the raw r3 fields, and do not match the resulting CR IDs
  .set bSelfLink, 8  # 0000 8000 = Allow pointing to self in autoformat
  .set bSkipH,    9  # 0000 4000 = Skip header input options (r4 = 0)
  .set bSkipE,    10 # 0000 2000 = Skip Element input options (r5, r6 = 0)
  .set bPBack,    11 # 0000 1000 = Use Pointback Protocol (r7 = pointback)
  .set bNoZero,   12 # 0000 0800 = Skip zeroing out allocations on format step
  .set bCircular, 13 # 0000 0400 = Make Sibling relationships circular, instead of terminated
  .set bPreAlloc, 14 # 0000 0200 = Use given alloc instead of generating (r7 = prealloc)
  .set bMetaDesc, 15 # 0000 0100 = Write allocation description in header metadata

# mask format, for bools in r3
  .set mSelfLink, 0x8000
  .set mSkipH,    0x4000
  .set mSkipE,    0x2000
  .set mPBack,    0x1000
  .set mNoZero,   0x0800
  .set mCircular, 0x0400
  .set mPreAlloc, 0x0200
  .set mMetaDesc, 0x0100

# Registers:
  .set rStr,    12

  .set rMeta,   16  # address of last metadata variable for rThis
  .set rCBList, 17  # address of current bctr routine address for formatter loop
  .set rESize,  18  # size of EData + EMeta
  .set rKey,    19  # key value for rThis
  .set rPrev,   20  # address of previous element
  .set rThis,   21  # address of this element
  .set rNext,   22  # address of next element
  .set rHData,  23  # size of data region in header (extracted from r4 0x00FFFFFF mask)
  .set rEData,  24  # size of data region in each element
  .set rECount, 25  # number of elements in array
  .set rHMeta,  26  # size of metadata padding before header data region
  .set rEMeta,  27  # size of metadata padding before each element data region
  .set rTSize,  28  # total size, including all metadata
  .set rHead,   29  # address of header
  .set rFirst,  30  # address of first element
  .set rLast,   31  # address of last element

# Stack offsets:
  .set xStorage,       0xF80  # register storage,  do not modify
  .set xStr,           0xF40  # return string,  modify with care
  .set xCR,            0xF3C  # returned cr1...cr7 values
  .set xCTR,           0xF38  # returned ctr value
  .set xCallbackList,  0xF30  # callback list -- directs nested loop, relies on terminator

# Constants:
  .set errBadPBackArgument,   1 # these error messages are returned in place of r10
  .set errBadPAllocArgument,  2
  .set errBadAllocSize,       3
  .set errBadAllocReturn,     4

_code_start:
andi. r0, r3, mPBack
cmpwi cr1, r7, 0
# cr1 will be used to check the validity of r7 argument value in 2 optional input protocols
# cr0 makes a check from the bools before they've been loaded into cr

mflr r0
stwu sp, -0x1000(sp)
stw  r0, 0x1004(sp)
stw r12, xStr+0x24(sp)
stw r11, xStr+0x20(sp)
beq- _setup
# to make the pointback protocol very fast, we are postponing setup until after validation

# full argument interface leaves no room for working registers before storage step,
# so r11 and r12 are made available for use as GPRs during pre-setup checks

  _pointback_check:
  blt+ cr1, _valid_pointback_argument
    li r10, errBadPBackArgument
    b _fast_return

  # if using pointback protocol and r7 is not a valid address,
  # then return error code
  # else, check if the destination contains a valid pointback value in its metadata

  _valid_pointback_argument:
  lwz    r12, 0x0(r7)
  rlwinm r0, r4, 10, 0xFF<<2
  sub.   r11, r12, r0
  rlwinm r12, r3, 28, 0x20
  sub    r11, r11, r12
  # find address of pointback variable in header metadata by unpacking value from r4
  # - the bit in extracted mPreAlloc is multiplied by the size of its added metadata size
  # r11 = total header metadata size (minus the pointback variable size)

  bge- _setup
  # if pointer value isn't a valid address, then it hasn't been initialized yet
  # else, pointback check indicates that this alloc is already initialized

    bl <MenuController_Base>
    lwz r0, 0x0(r12)
    # scene ID will be used to help prevent false positives in verification

    lwz r12, -0x4(r11)
    cmpw r7, r12
    rlwinm r0, r0, 8, 0xFFFF
    lwz r12, -0x8(r11)
    cmpw cr1, r0, r12
    crand eq, eq, eq+4
    bne- _setup
      lwz r10, 0x0(r7)
      b _fast_return
    # if pointback value == given pointer destination then skip generation/formatting
    # else, we need to regenerate this allocation by proceeding with _setup

_setup:
rlwinm r0, r3, 8, 0x00FF0000
mfcr r12
mtcrf 0x38, r0
stmw r16, xStorage(sp)
stw r12, xCR(sp)
mfctr r16
stw r16, xCTR(sp)
# saved registers are now available, CR/CTR are backed up, and bools are loaded into cr
# the process is now fully informed of the option inputs, and can begin handling things

_save_args:
addi rStr, sp, xStr
stswi r3, rStr, 0x20
# store r3...r10 along with previously stored r11, r12 string

_check_skipH:
bf+ bSkipH, _check_skipE
  li r4, 0
  # sets both Header Metadata and Header Data sizes to null if skipping header inputs

_check_skipE:
bf+ bSkipE, _setup_params
  li r5, 0
  li r6, 0
  # sets both Element Data size and Element Count to null if skipping element inputs

# the SkipH and SkipE options can be used to ignore some of the argument inputs
# the xStr that is returned is unaffected, meaning args r4...r6 will be perserved

_setup_params:
rlwinm rHData, r4, 0, 0xFFFFFF
mr rEData, r5
rlwinm rHMeta, r4, 10, 0xFF<<2
mr rECount, r6
rlwinm rEMeta, r3, 2, 0xFF<<2
# these params will remain as saved params during the post-generation formatting loop

_setup_preloop:
lis r4, <<generator>>@h
li r0, 5
ori r4, r4, <<generator>>@l
mtctr r0
addi r4, r4, _fork-_code_start
rlwinm r3, r3, 0, 0xFFFF0000
addi rCBList, sp, xCallbackList
# ready for loop to set up autoformat callbacks and count extra metadata size for elements

_for_each_autoformat_ID:
  rotlwi r3, r3, 4
  rlwinm. r0, r3, 3, 0xF<<3
  cmpwi cr1, r0, 0xB<<3
  crandc lt+4, lt+4, eq
  blt+ cr1, _add_autoformat_EMeta
    subi rEMeta, rEMeta, 4
    # if using a callback format or terminator, negate metadata padding
    # - this allows callbacks to specify their own padding size manually in the arguments

  _add_autoformat_EMeta:
  addi rEMeta, rEMeta, 4
  add r6, r4, r0
  stwu r6, 0x4(rCBList)
  bdnzf- eq, _for_each_autoformat_ID
  # exit loop if ran out of nibbles or valid autoformat IDs
  # - a '0' index points to a callback that terminates the formatting sequence
  # - if 4 nibbles are in use, a 5th iteration will write out a terminator at the end

_check_pback_meta:
bf+ bPBack, _check_metadesc_meta
  addi rHMeta, rHMeta, 8
_check_metadesc_meta:
bf+ bMetaDesc, _calculate_size
  addi rHMeta, rHMeta, 0x20
  # these options automatically add data to the header metadata region

_calculate_size:
add r0, rEData, rEMeta
mullw rTSize, rECount, r0
add r0, rHData, rHMeta
add rTSize, rTSize, r0
mr. r3, rTSize
bgt+ _prealloc_check
  li r10, errBadAllocSize
  b _return
  # if total size doesn't add up to a positive number, then return error

_prealloc_check:
bf+ bPreAlloc, _generate
  mr r3, r7
  blt+ cr1, _alloc_in_r3
  # if using pre-allocation option, use given r7 address instead of generating new alloc

    li r10, errBadPAllocArgument
    b _return
    # if using a pre-allocation option, return error if address is invalid

_generate:
bl <hsdAllocMemPiece>
# generates allocation

_alloc_in_r3:
add. rHead, r3, rHMeta
li rKey, -1
add r0, rHead, rHData
li rPrev, 0
add r4, r3, rTSize
li rThis, 0
sub rLast, r4, rEData
add rFirst, rEMeta, r0
mr rNext, rFirst
# all params have been set and are ready for formatting loop

blt+ _check_zero
  li r10, errBadAllocReturn
  b _return
  # if allocation address is not a valid address, then return error

_check_zero:
bt- bNoZero, _setup_meta
  li r4, 0
  mr r5, rTSize
  bl 0x80003130
  # __fill_mem - zero out space with a system func
  # (func address is the same between all version of Melee -- even PAL)

_setup_meta:

sub r6, rHead, rHMeta
bf+ bPBack, _check_metadesc_write
  bl <MenuController_Base>
  lwz r0, 0x0(r12)
  rlwinm r3, r0, 8, 0xFFFF
  lwz r4, xStr+0x10(sp)
  addi r6, r6, 8
  stw r3, -0x8(r6)
  stw r4, -0x4(r6)
  stw rHead, 0x0(r4)
  # if using pointback protocol, write pointback address in header metadata

_check_metadesc_write:
bf+ bMetaDesc, _setup_postloop
  stswi r23, r6, 0x20
  # if bMetaDesc, store metadata description in the header metadata

_setup_postloop:
add rESize, rEData, rEMeta
mullw. r0, rESize, rECount
ble- _exit_postloop
# skip postloop if no elements are present

_for_each_element:
  addi rKey, rKey, 1
  cmpw cr1, rKey, rECount
  mr. rPrev, rThis
  mr rMeta, rNext
  mr rThis, rNext
  add rNext, rNext, rESize
  bge- cr1, _exit_postloop
  cmpw cr1, rThis, rLast
  # if rKey >= element count, then exit formatting loop
  # else, check for last element

  bne+ cr1, _check_circular
    li rNext, 0
    # if on final element, set next = null

  _check_circular:
  bf+ bCircular, _formatting_event

    _check_circular_first:
    bge- cr0, _check_circular_last
      mr rPrev, rLast
      # set previous from rFirst to rLast, if circular sibling option is true

    _check_circular_last:
    bne- cr1, _formatting_event
      mr rNext, rFirst
      # set next from rLast to rFirst, if circular sibling option is true

  _formatting_event:
  addi rCBList, sp, xCallbackList

  _for_each_callback:
    lwzu r0, 0x4(rCBList)
    mtctr r0
    bctr  # uses fork

  _fork:
  # the following is an array of indexed instructions that branch into routines
  # - this fork has 16 permutations, and uses no conditional branches

  _fork_0: # terminate nested loop
  b _for_each_element
  nop
  # nop keeps this aligned with the other indexed instructions

  _fork_1: # prev
  mr r3, rPrev
  b _fork_write_link
  _fork_2: # next
  mr r3, rNext
  b _fork_write_link
  _fork_3: # head
  mr r3, rHead
  b _fork_write
  _fork_4: # first
  mr r3, rFirst
  b _fork_write_link
  _fork_5: # last
  mr r3, rLast
  b _fork_write_link

  _fork_6: # enumerate
  mr r3, rKey
  b _fork_write
  # value will be equal to element ID, creating a unique ordered key

  _fork_7: # write r7 value
  lwz r3, xStr+0x10(sp)
  b _fork_write
  _fork_8: # write r8 value
  lwz r3, xStr+0x14(sp)
  b _fork_write
  _fork_9: # write r9 value
  lwz r3, xStr+0x18(sp)
  b _fork_write
  _fork_A: # write r10 value
  lwz r3, xStr+0x1C(sp)
  b _fork_write
  # values may be defined in arguments r7...r10 when calling <generator>

  _fork_B: # call r9 callback
  lwz r3, xStr+0x18(sp)
  b _fork_execute
  _fork_C: # call r10 callback
  lwz r3, xStr+0x1C(sp)
  b _fork_execute
  _fork_D: # call r11 callback
  lwz r3, xStr+0x20(sp)
  b _fork_execute
  _fork_E: # call r12 callback
  lwz r3, xStr+0x24(sp)
  b _fork_execute
  # callbacks may be defined in arguments r9...r12 when calling <generator>

  _fork_F: # call private callback
  lwz r3, 0x1004(sp)
  addi r3, r3, 4
  # private callback address is relative to caller's return address:
  # bl <generator>
  # b _code_continue
  # # <- start of private callback...
  # _code_continue:

    _fork_execute:
    mtlr r3
    blrl
    b _for_each_callback
    # execute argument and continue nested loop

    _fork_write_link:
    bt- bSelfLink, _fork_write
      cmpw r3, rThis
      bne+ _fork_write
        li r3, 0
        # nullify write value if equal to self and not allowing self references

    _fork_write:
    stwu r3, -0x4(rMeta)
    b _for_each_callback
    # store argument, update rMeta address, and continue nested loop

_exit_postloop:
stmw r26, xStr+0x10(sp)
# commit to exit string variables stored in latter part of saved registers
# argument values have been destroyed, as they are no longer used

addi rStr, sp, xStr
lswi r3, rStr, 0x20
crclr eq
# load return string
# clear cr0 eq to signal that a new allocation/format was successful

_return:
lwz r11, xCTR(sp)
lwz r12, xCR(sp)
mtctr r11
mtcrf 0x7F, r12
lmw r16, xStorage(sp)
# return values have been set:
# r3...r6, cr1...cr7, ctr = unchanged from given arguments
# r7  = Header Metadata Size
# r8  = Element Metadata Size
# r9  = Total Size
# r10 = Address of HEADER
# r11 = Address of FIRST container Element
# r12 = Address of LAST container Element
# cr0 eq == FALSE if returning newly generated/formatted allocation
# cr0 eq == TRUE  if using pointback protocol, and old alloc is still valid

_fast_return:
lwz r11, xStr+0x20(sp)
lwz r12, xStr+0x24(sp)
lwz r0, 0x1004(sp)
addi sp, sp, 0x1000
mtlr r0
blr

# portable handle for vanilla HSD allocation function
# r3 = desired size
<hsdAllocMemPiece> NTSC 1.02
b 0x80381FA8
<hsdAllocMemPiece> NTSC 1.01
b 0x803812C8
<hsdAllocMemPiece> NTSC 1.00
b 0x803800F4
<hsdAllocMemPiece> PAL 1.00
b 0x80381EAC

# portable handle for fetching address of Menu Controller Scene IDs
<MenuController_Base> NTSC 1.02
lis r12,-32696
subi r12,r12,25296
blr
<MenuController_Base> NTSC 1.01
lis r12,-32696
subi r12,r12,28592
blr
<MenuController_Base> NTSC 1.00
lis r12,-32697
addi r12,r12,32104
blr
<MenuController_Base> PAL 1.00
lis r12,-32697
subi r12,r12,21704
blr

Mastercode enables static function for use with Gecko codes:
The Gecko Mastercode installs a static function for <generator> at 804DD960 so that it may be used by other gecko codes as part of a .gct file.
uses mytoc block 334

$Generator [Punkline]
042EF654 C8228000
C24DD960 00000063
70601000 2C870000
7C0802A6 9421F000
90011004 91810F64
91610F60 41820058
41A4000C 39400001
480002D0 81870000
548055BA 7D606051
546CE6B4 7D6C5850
40800034 3D808048
398C9D30 800C0000
818BFFFC 7C076000
5400443E 818BFFF8
7C806000 4C423202
4082000C 81470000
48000288 5460421E
7D800026 7C038120
BE010F80 91810F3C
7E0902A6 92010F38
39810F40 7C6C05AA
40A90008 38800000
40AA000C 38A00000
38C00000 5497023E
7CB82B78 549A55BA
7CD93378 547B15BA
4800016D 7C8802A6
38000005 7C0903A6
5463001E 3A210F30
5463203E 54601E79
2C800058 4C841102
41A40008 3B7BFFFC
3B7B0004 7CC40214
94D10004 4022FFDC
40AB0008 3B5A0008
40AF0008 3B5A0020
7C18DA14 7F9901D6
7C17D214 7F9C0214
7F83E379 41A1000C
39400003 480001B8
40AE0014 7CE33B78
41A4001C 39400002
480001A4 3C008038
60001FA8 7C0803A6
4E800021 7FA3D215
3A60FFFF 7C1DBA14
3A800000 7C83E214
3AA00000 7FF82050
7FDB0214 7FD6F378
41A0000C 39400004
48000164 418C001C
38800000 7F85E378
3C008000 60003130
7C0803A6 4E800021
7CDAE850 40AB0028
3D808048 398C9D30
800C0000 5403443E
80810F50 38C60008
9066FFF8 9086FFFC
93A40000 40AF0008
7EE605AA 7E58DA14
7C12C9D7 408100F8
3A730001 7C93C800
7EB4AB79 7ED0B378
7ED5B378 7ED69214
408400DC 7C95F800
40A60008 3AC00000
40AD0014 40800008
7FF4FB78 40860008
7FD6F378 3A210F30
84110004 7C0903A6
4E800420 4E800021
4BFFFFB0 60000000
7E83A378 48000080
7EC3B378 48000078
7FA3EB78 48000080
7FC3F378 48000068
7FE3FB78 48000060
7E639B78 48000068
80610F50 48000060
80610F54 48000058
80610F58 48000050
80610F5C 48000048
80610F58 48000024
80610F5C 4800001C
80610F60 48000014
80610F64 4800000C
80611004 38630004
7C6803A6 4E800021
4BFFFF68 41880010
7C03A800 40A20008
38600000 9470FFFC
4BFFFF50 BF410F50
39810F40 7C6C04AA
4C421182 81610F38
81810F3C 7D6903A6
7D87F120 BA010F80
81610F60 81810F64
80011004 38211000
7C0803A6 4E800020
60000000 00000000

Symbols may be included in MCM root directory:
Paste the following into a file called “generator.s” and place it in your MCM root directory.
You may then .include these symbols in your codes, or just manually include them by pasting them locally:
Code:
# you can load these symbols by placing this file in your MCM root directory,
#  and writing the line  .include "generator.s"  in the context of any code.


.set generator,  0x804DD960 # Static Address, for Gecko

# mask format, for bools in r3
  .set mSelfLink, 0x8000  # Allow pointing to self in autoformat
  .set mSkipH,    0x4000  # Skip header input options (r4 = 0)
  .set mSkipE,    0x2000  # Skip Element input options (r5, r6 = 0)
  .set mPBack,    0x1000  # Use Pointback Protocol (r7 = pointback)
  .set mNoZero,   0x0800  # Skip zeroing out allocations on format step
  .set mCircular, 0x0400  # Make Sibling relationships circular, instead of terminated
  .set mPreAlloc, 0x0200  # Use given alloc instead of generating (r7 = prealloc)
  .set mMetaDesc, 0x0100  # Write allocation description in header metadata

# Registers:
  .set rMeta,   16  # address of last metadata variable for rThis
  .set rCBList, 17  # address of current bctr routine address for formatter loop
  .set rESize,  18  # size of EData + EMeta
  .set rKey,    19  # key value for rThis
  .set rPrev,   20  # address of previous element
  .set rThis,   21  # address of this element
  .set rNext,   22  # address of next element
  .set rHData,  23  # size of data region in header (extracted from r4 0x00FFFFFF mask)
  .set rEData,  24  # size of data region in each element
  .set rECount, 25  # number of elements in array
  .set rHMeta,  26  # size of metadata padding before header data region
  .set rEMeta,  27  # size of metadata padding before each element data region
  .set rTSize,  28  # total size, including all metadata
  .set rHead,   29  # address of header
  .set rFirst,  30  # address of first element
  .set rLast,   31  # address of last element

# Stack offsets:
  .set xStorage,       0xF80  # register storage,  do not modify
  .set xStr,           0xF40  # return string,  modify with care
  .set xCR,            0xF3C  # returned cr1...cr7 values
  .set xCTR,           0xF38  # returned ctr value
  .set xCallbackList,  0xF30  # callback list -- directs nested loop, relies on terminator

# arguments:
  # r3 = Options:
  #     FFFF 0000 = Metadata Autoformat Options (see autoformat key, below)
  #     0000 FF00 = Option Flags
  #     0000 00FF = Extra Metadata Words (for each element)
  # r4 = Header Args:
  #     FF00 0000 = Extra Metadata Words (for header)
  #     00FF FFFF = Header Size (byte count)
  # r5 = Element Size (byte count)
  # r6 = Element count
  # r7 = special argument:
  #      - Used as a pointback destination      if using bPBack option
  #      - Used to reference an existing alloc  if using bPreAlloc option
  # r7...r12 = may also be used to create arguments for various Autoformat specifications in r3

# returns:
  # r3...r6, cr1...cr7, ctr = unchanged from given arguments
  # r7  = Header Metadata Size
  # r8  = Element Metadata Size
  # r9  = Total Size
  # r10 = Address of HEADER
  # r11 = Address of FIRST container Element
  # r12 = Address of LAST container Element
  # cr0 eq == FALSE if returning newly generated/formatted allocation
  # cr0 eq == TRUE if using pointback protocol, and old alloc is still valid

# The r3 value is broken up into 13 fields:

# r3 Autoformat Options:
# - Up to 4 format IDs can be specified to create automatic metadata.
# - Each non-0 ID will add 4 bytes of metadata to the size of each element.
  # F000 0000 = Format ID for Element offset -0x4
  # 0F00 0000 = Format ID for Element offset -0x8
  # 00F0 0000 = Format ID for Element offset -0xC
  # 000F 0000 = Format ID for Element offset -0x10
  # 0000 00FF = Additional words of blank padding for each element

# Autoformat Key:
# - These IDs are tied to small callback routines that can be used to format a container.
# - IDs B...F can be used to pass custom callback routines for instantiating complex data types.
  # 0 = (terminator)
  # 1 = link to previous sibling
  # 2 = link to next sibling
  # 3 = link to allocation header
  # 4 = link to first sibling
  # 5 = link to last sibling
  # 6 = enumerate unique key
  # 7 = write value from r7
  # 8 = write value from r8
  # 9 = write value from r9
  # A = write value from r10
  # B = execute callback in r9
  # C = execute callback in r10
  # D = execute callback in r11
  # E = execute callback in r12
  # F = execute private process (argument = LR + 4)

# r3 Boolean Options:
# - note, the value of these boolean CR IDs are leftshifted <<8 and put into saved cr2, cr3
# -- the masks in the comments show the raw r3 fields, and do not match the resulting CR IDs
  .set bSelfLink, 8   # 0000 8000 = Allow pointing to self in autoformat
  .set bSkipH,    9   # 0000 4000 = Skip header input options (r4 = 0)
  .set bSkipE,    10  # 0000 2000 = Skip Element input options (r5, r6 = 0)
  .set bPBack,    11  # 0000 1000 = Use Pointback Protocol (r7 = pointback)
  .set bNoZero,   12  # 0000 0800 = Skip zeroing out allocations on format step
  .set bCircular, 13  # 0000 0400 = Make Sibling relationships circular, instead of terminated
  .set bPreAlloc, 14  # 0000 0200 = Use given alloc instead of generating (r7 = prealloc)
  .set bMetaDesc, 15  # 0000 0100 = Write allocation description in header metadata


---


How to Use:

The DOL Mod and ASM Source install a standalone function called <generator>.
So long as the function module is in the Mods Library, ANY other code can use this function! You don’t need to bundle it with other codes, and MCM will automatically install it as required.

The Gecko Mastercode installs a static function for <generator> at 804DD960 so that it may be used by other gecko codes as part of a .gct file.


Code Examples that Use <generator>
Coming soon...



Quickstart Guides:
The argument interface uses a set of options in r3 to determine the behavior of the <generator> function.
See generator.s for symbol definitions.

Here are some examples of how the <generator> function can be called:
By setting r3 = 0, you can use the default container format with no options.
This has you define a header size, an element size, and an element count:

li r3, 0
li r4, 0x20
# HData (Header Data Size)
li r5, 0x10 # EData (Element Data Size)
li r6, 3 # ECount (Element Count)
bl <generator>
# r10 = address of header
# r11 = address of first element
# r12 = address of last element


Each time the above call is made, a new allocation is generated and returned.

---


If you do not want to create an array in your container, you can set EData or ECount to 0.
You may also disable the Element arguments altogether with the mSkipE option:

li r3, mSkipE
li r4, 0x20
# HData
bl <generator>
# r10 = address of header

---


If you do not want to create a header in your container, you can set HData to 0.
You may also disable the Header arguments with the mSkipH option:

li r3, mSkipH
li r5, 0x10
# EData
li r6, 3 # ECount
bl <generator>
# r11 = address of first element
# r12 = address of last element


---


All containers generated with <generator> will remain stable for the duration of a scene.
After that, they will become volatile, and may become corrupted over time as demands are made on the heap.
When a code needs to use a generated data container, it will probably reference it via pointer.
This becomes complicated when considering the fact that data allocations are made volatile after scene transitions.

The process of updating and maintaining a pointer to a generated container can be completely automated with the mPBack option,
and a given pointer variable in the r7 argument:

li r3, mSkipE | mPBack
li r4, 0x80
# HData
mr r7, r31 # address of pointer variable
bl <generator>
# r10 = address of header
# r31 = updated pointer
# cr0 eq = allocation status


Using this optional protocol will cause <generator> to only create a new allocation if the given pointer variable in r7 does not point to a valid allocation
-- in which case cr0 eq will be FALSE.

The pointback protocol uses a 2-way pointer and a scene ID to verify whether or not an allocation head has been made volatile or not.
The protocol costs 8 extra bytes of metadata in the header of each allocation that uses it.

This can be used to prevent memory leaks without requiring any external logic on the caller’s side,
and also creates a pipeline for initializing default data in new allocations by utilizing the condition register as a return argument.


You can use the returned cr0 “eq” value to determine whether or not to initialize new data before it is accessed for the first time:

li r3, mSkipE | mPBack
li r4, 0x80
mr r7, r31
bl <generator>

beq+ _initialized

_initialization_routine:
li r0, -1
stw r0, 0x0(r10)
# when a new allocation is generated, write 0xFFFFFFFF to offset 0x0

_initialized:
# r10 = address of data (old or new)
The 4 high nibbles in argument r3 can be used to specify special “autoformat” options that will automatically format a container in a number of ways, using metadata.

Metadata is “hidden” by being stored in offsets that are negative relative to the base of each element (or the header). Padding is automatically added to support metadata generated in this way.

lis r3, 0x1236 # string of 4 autoformat IDs
ori r3, r3, mPback | mCircular # options
li r4, 0x10 # HData
li r5, 0x40 # EData
li r6, 12 # ECount
mr r7, r31 # PBack variable
bl <generator>
beq+ _initialized


_initialization_routine:
stw r11, 0x0(r10)
# initial selection = first element

_initialized:
lwz r11, 0x0(r10)
# r10 = address of header superstruct

# r11 = address of selected doubly-linked element in circular list


With the autoformat string of 0x1236, we specify the following metadata for each element:
-0x10 = 6 -- enumerate with unique key
-0x0C = 3 -- point to header
-0x08 = 2 -- point to next element
-0x04 = 1 -- point to previous element
0x0 = (base of element data region)


When using the option mCircular, sibling links “next” and “previous” will wrap around instead of using null terminators.

See the full argument interface towards the bottom of this post for more details about autoformat IDs and their uses.

---


The mPreAlloc option can be used to give <generator> an input data region to format instead of generating a new one in the memory heap. This can be used to format temporary structures in the runtime stack frame for procedures that require a known amount of space complexity. It can also be used to nest automatically generated structures within a primary allocation.

The option mSkipZero can be used in these cases to skip the zeroing step of formatting. This will preserve any unformatted data in the given region. When not using mSkipZero, the given data region will be cleaned (filled with 0) before being formatted with metadata.

lis r3, 0x2000
ori r3, r3, mPreAlloc | mSkipH | mSkipZero
li r5, 0x4
li r6, 200
addi r7, sp, 0x20
bl <generator>

# r11 = temporary linked list

# container is automatically destroyed in process epilog, when stack collapses


When generating nested containers, the header size of the primary allocation can be padded to accommodate other containers.

lis r3, 0x2000
ori r3, r3, mPBack
li r4, 0x600 # HData
li r5, 0xC # EData
li r6, 32 # ECount
mr r7, r31 # PBack variable
bl <generator>
beq+ _initialized


_initialization_routine:
stw r11, 0x0(r10)
# store 1st list pointer at 0x0
xori r3, r3, mPBack | mPreAlloc | mCircular
# XOR will toggle previously ORed options, turning off mPBack
subi r4, r4, 0x100 # HData -= 2nd list size
li r5, 0x4 # EData
mr r7, r10 # PreAlloc base address
bl <generator>
stw r11, 0x4(r10)
# store 2nd container pointer at 0x4
subi r4, r4, 0x480 # HData -= 3rd list size
li r5, 0x20 # EData
mr r7, r10 # PreAlloc base address
bl <generator>
stw r11, 0x8(r10)
# store 3rd list pointer at 0x8

_initialized:
# r10 = 3 list pointers in 0x20-byte header
Autoformat options B...F allow the caller to specify argument callbacks for the formatting process to execute in place of any prefab routines. The callback inherits the saved register context used by the formatting loop, creating an opportunity for the callback to make informed modifications to the structures being parsed without having to load any parameters.

lis r3, 0xE000 # autoformat E -- callback in r12
li r4, 0x10
li r5, 0x20
li r6, 200
lis r12, <<my_custom_autoformat>>@h
ori r12, r12, <<my_custom_autoformat>>@l
bl <generator>

# <my_custom_autoformat> is used to format each element


The following autoformat types will execute callbacks from specific argument registers:
B -- callback in r9
C -- callback in r10
D -- callback in r11
E -- callback in r12



In MCM 4.1+, you may hardcode the address of standalone functions by referencing their function names in double-brackets:

<<my_custom_autoformat>>

---


Autoformat type F is a special callback option that exploits the nature of the link register to provide a “private” callback.

A private callback is unique, and defined locally by the caller of <generator>.
This relies on the call returning to a branch instruction that skips over the input callback routine starting at lr + 4, like so:

lis r3, 0xF000 # autoformat -- private argument = lr + 4
li r4, 0x10
li r5, 0x20
li r6, 200
bl <generator>
b _continue

_my_custom_autoformat:
# write autoformat callback here, locally

_continue:
# return from <generator> skips over callback code

---


A given callback will be executed once for each element.
When designing a callback, you gain contextual access to the following register and stack interface:

Code:
# Registers:
.set rMeta,   16  # address of last metadata variable for rThis
.set rCBList, 17  # address of current bctr routine address for formatter loop
.set rESize,  18  # size of EData + EMeta
.set rKey,    19  # key value for rThis
.set rPrev,   20  # address of previous element
.set rThis,   21  # address of this element
.set rNext,   22  # address of next element
.set rHData,  23  # size of data region in header (extracted from r4 0x00FFFFFF mask)
.set rEData,  24  # size of data region in each element
.set rECount, 25  # number of elements in array
.set rHMeta,  26  # size of metadata padding before header data region
.set rEMeta,  27  # size of metadata padding before each element data region
.set rTSize,  28  # total size, including all metadata
.set rHead,   29  # address of header
.set rFirst,  30  # address of first element
.set rLast,   31  # address of last element

# Stack offsets:
.set xStorage,       0xF80  # register storage,  do not modify
.set xStr,           0xF40  # return string,  modify with care
.set xCR,            0xF3C  # returned cr1...cr7 values
.set xCTR,           0xF38  # returned ctr value
.set xCallbackList,  0xF30  # callback list -- directs nested loop, relies on terminator

When a callback needs to write some metadata to an element, it can do so with stwu like so:
stwu r0, -0x4(rMeta)

If a callback is to write some metadata like this, the caller of <generator> must provide some metadata padding in r3.
You may have a callback write multiple metadata values with consecutive stwu instructions.

Code:
Stack offset “xStr” may be used to access input arguments. It is laid out like so:
# from sp, xStr:
0xF40 = argument r3
0xF44 = argument r4
0xF48 = argument r5
0xF4C = argument r6
0xF50 = argument r7
0xF54 = argument r8
0xF58 = argument r9
0xF5C = argument r10
0xF60 = argument r11
0xF64 = argument r12

Additionally, cr4 is nullified before the formatting loop begins, so callbacks may rely on bits 16...19 to create logical states.

If you have the callback write to arguments r3...r6 in xStr, the value will be returned to the caller of <generator>.
This may be useful for hacking the returns to provide some kind of information to the caller.


---


Arguments:

r3 = Options:
FFFF 0000 = Metadata Autoformat Options (see autoformat key, below)
0000 FF00 = Boolean Flag Options
0000 00FF = Extra Metadata Words (for each element)
r4 = Optional Header Arguments:
FF00 0000 = Extra Metadata Words (for header)
00FF FFFF = Header Size (byte count)
r5 = Optional Element Size (byte count)
r6 = Optional Element count
r7 = Optional Special Argument:
- Used as a pointback destination, if using mPBack option
- Used to reference an existing allocation, if using mPreAlloc option
r7...r12 = Optional Autoformat Inputs


Returns:
r3...r6, cr1...cr7, ctr = unchanged from given arguments
r7 = Header Metadata Size
r8 = Element Metadata Size
r9 = Total Size
r10 = Address of HEADER
r11 = Address of FIRST container Element
r12 = Address of LAST container Element
cr0 eq == FALSE if returning newly generated allocation
cr0 eq == TRUE if using pointback protocol, and old alloc is still valid



The options in r3 may be used to hide parts of the argument interface by toggling optional arguments.
Other options may be used to set up how metadata is automatically formatted:


Autoformat Options:
- r3 can specify up to 4 format IDs to automatically create metadata variables.

r3 = Options:
F000 0000 = Format ID for Element offset -0x4
0F00 0000 = Format ID for Element offset -0x8
00F0 0000 = Format ID for Element offset -0xC
000F 0000 = Format ID for Element offset -0x10
0000 00FF = Additional words of blank padding for each element


Autoformat Key:
- You may select up to 4 of these IDs for the Autoformat Options.

0 = (terminator)
1 = link to previous sibling
2 = link to next sibling
3 = link to allocation header
4 = link to first sibling
5 = link to last sibling
6 = enumerate unique key
7 = write value from r7
8 = write value from r8
9 = write value from r9
A = write value from r10
B = execute callback in r9
C = execute callback in r10
D = execute callback in r11
E = execute callback in r12
F = execute private process (argument = LR + 4)


Boolean Flag Options:
- Names and descriptions describe case of TRUE

r3 = Options:
0000 8000 = mSelfLink -- Allow pointing to self in autoformat
0000 4000 = mSkipH -- Skip header input options (r4 = 0)
0000 2000 = mSkipE -- Skip Element input options (r5, r6 = 0)
0000 1000 = mPBack -- Use Pointback Protocol (r7 = pointer variable)
0000 0800 = mNoZero -- Skip zeroing out allocations on format step
0000 0400 = mCircular -- Make Sibling relationships circular, instead of terminated
0000 0200 = mPreAlloc -- Use given alloc instead of generating (r7 = address of existing alloc)
0000 0100 = mMetaDesc -- Write allocation description in header metadata
 
Last edited:

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
673
this is some crazy powerful stuff dude, i'll be looking for ways to use this in the future!
 

dreamhouse

Smash Cadet
Joined
Apr 25, 2015
Messages
27
Location
West Tennessee
Serious question, how far away are we from being able to code a completely barebones Tetris clone you could play on the pause screen? In theory at least.

Are there any necessary programming fundamentals missing? I'm just learning asm, but it really feels like you guys have made crazy progress recently.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
423
Late reply, sorry. But:
Serious question, how far away are we from being able to code a completely barebones Tetris clone you could play on the pause screen? In theory at least.

Are there any necessary programming fundamentals missing? I'm just learning asm, but it really feels like you guys have made crazy progress recently.
-- I'd say we're already there.

With "GObjs" we can make little event factories that create "GObj Processes" that run every process frame for updating a GObj, or "GX callbacks" which run every drawn frame, and are capable of drawing vertices through the GX pipe. They let you attach joints to it, for displaying models; and they also let you maintain a data pointer in a way that allows for a deallocation callback, so you can destroy the GObj. This lets a GObj keep data attributes like variables and other callback methods.

Currently it's a little bit of a pain to set them up correctly, but I'll have some tools available soon that make it a little easier to make GObjs for playing with. I'll also be trying to make sure this generator function can create data tables that can automatically deallocate with a destroyed GObj.
 

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
673
Serious question, how far away are we from being able to code a completely barebones Tetris clone you could play on the pause screen? In theory at least.

Are there any necessary programming fundamentals missing? I'm just learning asm, but it really feels like you guys have made crazy progress recently.
what have you done? you know punkline never walks away from a challenge
 
Last edited:
Top Bottom