ywy box of fun

land maker investigation

Yolkai
Yolkai
a summary of y's investigation with Taito's 1998 arcade game Land Maker (ランドメーカー), the last title for the f3 system hardware.
🎎 unless otherwise noted, rom addresses are for version 2.01 (landmakrj). see the 2.01J -> 2.02O program shifts table for translation to 2.02 addresses.

unused content etc.

check menu

the graphic check menu is already covered well by https://sudden-desu.net/entry/unused-menus-in-land-maker under "Check Menu" by the way, "SCP" is a typo. should be SCR, referring to tilemaps. interestingly, the format of the graphic tables used by the menu are different from other f3 games: land maker interleaves the block definition address and filename (array of structs), while e.g. puchi carat, arabian magic, arkanoid returns, recalhorn, puzzle bobble 4, and bubble bobble ii have separate arrays for the addresses and filenames (struct of arrays). (the array of associated palette pointers is still separate in land maker.)

alternate difficulty menu

see https://sudden-desu.net/entry/unused-menus-in-land-maker under "Alternate Difficulty Menu"

4 player switch test

normal menu, for comparison unused code for a 4 player version of the switch test menu exists along with the normal switch test, likely retained due to the service menu being common code.

alternate sound test

not necessarily unused, but... hold SERVICE 1 while selecting the sound test from the test mode main menu to enter an alternate sound test with per-track volume controls for the sequenced music. it seems a little buggy, though.

running debug

pop-up debug/test menu like in many other f3 games. TILT: toggle [PAUSE] hold SERVICE 1 + press TILT: bring up popup [EDIT] menu
[PAUSE]
P1 BUTTON 3: (press) advance one frame P2 BUTTON 3: (while held) advance frames
[EDIT]
UP/DOWN: navigation LEFT/RIGHT: navigation in submenus BUTTON 1: select/set BUTTON 2: back/cancel EXIT: exit the menu SLOW LEVEL: left/right to set delay between frames COLOR EDIT: edit color ram DISPLAY: flip the vertical display orientation TEST MODE: enter service menu RESET: reset
[SLOW]
mode indicator displayed during delay

mame cheat

the entry point, $8CD70 which handles printing the mode, should be called by $8DF32 right before the call to display credit count. you'll also need to prevent the tilt error behavior. (2.01)
  <cheat desc="Running Debug">
    <comment>note: this replaces service switch handling</comment>
    <script state="run">
      <action>maincpu.mw@8DF34=0xEE3C</action>
    </script>
    <script state="off">
      <action>maincpu.mw@8DF34=0x0104</action>
    </script>
  </cheat>
  <cheat desc="disable tilt error">
    <script state="run">
      <action>maincpu.mw@008FA=0x00BE</action>
    </script>
    <script state="off">
      <action>maincpu.mw@008FA=0x014A</action>
    </script>
  </cheat>

attack portrait test

$94EE6 is a disabled function that cycles through attack portrait animations when the respective player's B2 is pressed. we can re-enable it in its former calling task at $94EBC easily: 2.01
  <cheat desc="Attack Portrait Test Routine">
    <comment>during a match, p1 B2 and p2 B2 will cycle through attack portrait wipes</comment>
    <script state="run">
      <action>maincpu.mw@94EC6=0x611E</action>
      <action>maincpu.mw@94EC8=0x617C</action>
    </script>
    <script state="off">
      <action>maincpu.mw@94EC6=0x6100</action>
      <action>maincpu.mw@94EC6=0x007E</action>
    </script>
  </cheat>

p1 autoplay

near $9C108 is (still active! task at $9BEE0) code that, when P1 UP+P2 UP are pressed, sets a demo autoplay mode flag. SERVICECOIN + TILT clears the flag. the code that handles the flag exists ($9C132) but is unused. the correct caller is a fairly high priority task, not the function nearby: when called correctly it should constantly insert credits and continue on a loss, but the nearby code is only active during the game round. possibly for a demonstration mode while in development? the autoplay ai properties are hardcoded: 'thinking' delay is 0 and the level is 4.

mame cheat: (incorrect) autoplay demo

⚠ this cheat is an easy patch for demonstration, and not the correct way to call this code. in particular, it only runs during a round and cannot auto-continue from loss.
2.01
  <cheat desc="P1 side computer play">
    <comment>hold p1 UP+p2 UP during a match to activate, TILT to deactivate. on deactivation, computer control remains in effect until end of round.</comment>
    <script state="run">
      <action>maincpu.mw@9C130=0x4E71</action>
    </script>
    <script state="off">
      <action>maincpu.mw@9C130=0x4E75</action>
    </script>
  </cheat>

MAP DATA EDIT

there is task code for a (partially functional) stage pattern editor from $9F218 to at least $9F72E, possibly through $9F9C3. however, the routine is unconnected, and has several strange patterns. possibly missing tables, more unreferenced task loops interspersed, empty functions... the task is called along with some other functions in debug-related code: $6986 to play the opening $698E to map edit $6996 to (unknown, possibly map edit-related or for testing map) $699E to run the teach demo (? uses jsr instead of trap to create task and yield) so it's possible that these would be accessible from some kind of debug menu, but the code to create them as tasks is missing(?)
controls
P1 B3: cycle color P1 JOY: grid position P1 B1: place tile P1 B2: recalculate coordinates?
P2 B3: clear board P2 UP/DOWN: change some kind of address P2 START: toggle SCROLL/START edit mode ? P2 B2: $9F560() ?

location test text

at $9113A, a function to print a location test message, similar to other taito games.
 THANK YOU FOR PLAYING UP TO THIS STAGE.
YOU ARE A VERY GOOD PLAYER, AREN'T YOU? 
BECAUSE THIS GAME IS NOW UNDER LOCATION 
TEST, WE ARE VERY SORRY NOT TO SHOW YOU 
THE NEXT STAGE OF THE GAME.            
 WE ARE TERRIBLY SORRY FOR THIS AND     
HOPE TO SEE YOU AGAIN,                 
 SOMETIME AND SOMEPLACE.                

mame cheat: use location test message after practice stage 3

2.01
  <cheat desc="Practice Use LocTest Text">
    <script state="run">
      <action>temp0=maincpu.md@91026</action>
      <action>maincpu.md@91026=0x60000112</action>
    </script>
    <script state="off">
      <action>maincpu.md@91026=temp0</action>
    </script>
  </cheat>

unused attack pattern

there is an unused piece damage attack pattern at $AC2F2 largeit shows some similarities to ranju's final pattern: there is no respective data for the select screen attack pattern preview, which uses a different format.
mame cheat: aifa use unused attack pattern
(in practice mode, the opponents use an entirely different set of attack patterns) 2.01
  <cheat desc="(non-practice) aifa use unused nuisance pattern">
    <script state="on">
      <action>maincpu.mw@AB14C=C2F2</action>
    </script>
    <script state="off">
      <action>maincpu.mw@AB14C=B3C2</action>
    </script>
  </cheat>
2.02
  <cheat desc="(non-practice) aifa use unused nuisance pattern">
    <script state="on">
      <action>maincpu.mw@AB5AC=C752</action>
    </script>
    <script state="off">
      <action>maincpu.mw@AB5AC=B822</action>
    </script>
  </cheat>

music names

https://forums.insertcredit.com/d/9-lets-share-some-game-music/236
FlutterSprite:
I don’t think there’s any official track order or track names
the arcade rom gives the following names for sequence data:
internal sequence name list
found at $C3886C, some kind of sequence header data. each entry is 168 bytes, which includes 4 bytes (sequence length?) followed by 16 chars sequence name. after some more header information is a series of 14 byte sequence track events(?). n.b.: much of the text on the audio cpu has a dirty 8th bit (possibly formatting control for a display?) that needs to be masked off to read properly in ascii
Sequence-00     
Sequence-01     
Sequence-02     
SELECT_R        
BOSS_RI         
BOSS_RL         
MAP_R           
Sequence-07     
PUSH            
SOUMEI2         
Opening     
Ending      
HIRYU_I         
HIRYU_L         
Sequence-14     
AIFA            
SOUMEI_I        
SOUMEI_L        
Sequence-18     
RENKI           
YOUEN_R         
ROHKO           
RINRE_I         
RINRE_L         
Sequence-24     
ROUCHI          
LAST            
(suffixes _I and _L indicate intro and loop sections for the same song) placeholder sequence names (Sequence-##) most likely do not have leftover sequence data associated.
"reZonance world〜ZUNTATA 30th ANNIVERSARY〜 L'ab-normal Limited 3" included the tracks
19.ランドメーカー (アーケード) SELECT BGM 20.ランドメーカー (アーケード) R1 BGM 21.ランドメーカー (アーケード) R2 BGM 22.ランドメーカー (アーケード) R3 BGM 23.ランドメーカー (アーケード) R4 BGM 24.ランドメーカー (アーケード) R5 BGM 25.ランドメーカー (アーケード) R6 BGM
"R1" being 'round 1' i.e. hiryu's stage theme. rinrei and roushinshi's stages (as well as other music) weren't present. it's not clear why. the audio cpu, sound test, and what exists on reZonance world agree on sequence order.
snd #internal (adapted)reZonance worldy's description
4ESELECTSELECT BGM
4FBOSSfinal round(s)
50MAPmap screen intermission
51PUSHinstruction explanation
52SOUMEI2high score table
53Opening
54Endingcredits
55HIRYUR1 BGMhiryu stage theme
56AIFAR2 BGMaifa stage theme
57SOUMEIR3 BGMsoumei stage theme
58RENKIR4 BGMrenki stage theme
59YOUENR5 BGMyouen stage theme
5AROHKOR6 BGMkouko stage theme
5BRINRErinrei stage theme
5CROUCHIroushinshi stage theme
5DLASTmap intermission before last stage
in 2p vs, music is determined by player character selection. it would be appropriate to consider them respective character themes.

norihiro furukawa composition credit

https://twitter.com/nakayamaraiden/status/100012318167015424
ランドメーカーってタイトルデモ曲だけ自分が書いてたようなw
https://twitter.com/nakayamaraiden/status/700640597476913153
そういえばタイトーのアドバタイズデモというと業務用の「ランドメーカー」のアドバタイズデモ曲はアテクシ担当でした(この一曲だけ)。
norihiro furukawa (@nakayamaraiden) has claimed to have only composed the opening attract demo theme, but y wonders, because of the similar name formatting, if he composed "Ending" as well (pure speculation) (2023-06-16 update:) https://twitter.com/nakayamaraiden/status/1669648174544461824
アトラクトのみです。エンディングは書いた記憶ないですね…。
"Attract only. I don't remember writing the ending...." sorry for the misunderstanding!

sample names

at the beginning of each bank is a table of instrument/sample names and the offset to the parameters for the first split.
on extracting samples from ensoniq sample rom
(the following examples ignore padding(?) bytes in the ensoniq rom) 80 [oo oo NN NN NN NN NN NN NN NN NN NN NN NN] ... the sample offset/name table is followed by 00 00 00 00 sentinel, then the split parameters
SampleParameters {
  ?int16 base_freq; // in multiples of semitones, sign affects interpretation?
  uint24 start_ofs; // where the sample starts in the bank
  uint8  key_range_end; // highest appropriate midi key to play split at
  uint24 loop_start;
  uint8  loop_type; // 0=nonlooping. for es5505, 1,2,4=unidirectional, 3=bidirectional
  uint24 loop_end;  // loop end point (integer part)
  uint8  loop_end_frac; // (?) possibly fractional part of loop end position
}
if key_range_end < 127, additional instrument splits follow. 8 bit mono pcm. mame uses fixed point 20.9 for sample positions. https://github.com/mamedev/mame/blob/ae373baa246e69a52e6e8249e6e8c7897ba1e1fe/src/devices/sound/es5506.h#L236 problems: how do we convert to something preserving fractional loop points... after determining a midi note to play at, $C1946C - C194D6 determines freqcount to send to es5505 for playback. something like this...
00c1946c 1038d834    move.b     (PLAY_NOTE).w,D0b
;; at this point PLAY_NOTE is a midi note << 8, e.g. 0x3C00
;; A6 is the first split's SampleParams for the instrument
                 loop_find_split_for_note:
00c19470 b02e0005    cmp.b      (pSampleParams->key_range_end,A6),D0b
00c19474 6306        bls.b      found_split
00c19476 dcfc000e    adda.w     #0xe ,pSampleParams
00c1947a 60f4        bra.b      loop_find_split_for_note
                 found_split:
00c1947c 294e001a    move.l     pSampleParams ,(#0x1a ,A4 )
00c19480 302c009a    move.w     (unk_A4->note ,A4 ),D0w        ; same as PLAY_NOTE
00c19484 d056        add.w      (pSampleParams->base_freq ),D0w
00c19486 6a28        bpl.b      frequency_positive

00c19488 b07cb800    cmp.w      #0xB800 ,D0w  ; D0 = max(0xB800, D0)
00c1948c 6c04        bge.b      no_clamp
00c1948e 303cb800    move.w     #0xB800 ,D0w  ; -0x4800, 6 octave range per split?

                 no_clamp:
00c19492 4241        clr.w      octave
                 count_octaves_n:
00c19494 5241        addq.w     #0x1 ,octave
00c19496 d07c0c00    add.w      #0xC00 ,D0w
00c1949a 6bf8        bmi.b      count_octaves_n

00c1949c e248        lsr.w      #0x1 ,D0w
00c1949e c07cfffe    and.w      #0xFFFE ,D0w
00c194a2 207c00c0    movea.l    #NEG_NOTE2FREQ_LUT ,A0   ; pow(pow(2,1/12),D0w/64)*1024
         9e6c
00c194a8 30300000    move.w     (0x0 ,A0 ,D0w *0x1 ),D0w
00c194ac e268        lsr.w      octave ,D0w              ; multiply by octaves
00c194ae 6026        bra.b      freqcount_end

;; land maker's samples don't use this route
                 frequency_positive:
00c194b0 b07c5400    cmp.w      #0x5400 ,D0w
00c194b4 6f04        ble.b      no_clamp_p
00c194b6 303c5400    move.w     #0x5400 ,D0w
                 no_clamp_p:
00c194ba 72ff        moveq      #-0x1 ,octave
                 count_octaves_p:
00c194bc 5241        addq.w     #0x1 ,octave
00c194be 907c0c00    sub.w      #0xc00 ,D0w
00c194c2 6af8        bpl.b      count_octaves_p
00c194c4 e240        asr.w      #0x1 ,D0w
00c194c6 c07cfffe    and.w      #0xFFFE ,D0w
00c194ca 207c00c0    movea.l    #POS_LUT ,A0             ; this table is different
         a46c
00c194d0 30300000    move.w     (0x0 ,A0 ,D0w *0x1 ),D0w
00c194d4 e360        asl.w      octave ,D0w

                 freqcount_end:
00c194d6 3940007a    move.w     D0w,(#0x7a ,A4)          ; write to FC reg later
https://github.com/y-ack/landmaker-color-mod/blob/main/scripts/samples.rb#L40
by bank:
0x400000
spoiler
SE01        
SE02        
SE03        
SE04        
SE05        
SE06        
SE07        
SE08        
SE09        
SE10        
SE11        
SE12        
SE13        
SE14        
SE15        
SE16        
SE17        
SE18        
SE19        
SE20        
SE21        
SE22        
SE23        
SE24        
0x500000
spoiler
KETTEI      
CRAR        
DANSA       
GAGE        
KOGEKI      
SHOU1       
SHOU2       
SHOU3       
WARN        
52SE-1      
52SE-3      
52SE-4      
52SE-5      
EFF1        
SINE        
0x600000
spoiler
F-BRONZE    
F-SILVER    
F-GOLD      
F-PLATINA   
F-NO        
F-READY     
F-GO        
F-MAP1      
F-MAP2      
F-MAP3      
F-MAP4      
F-DANGER    
RASOLOOT    
SCI FI      
SOUBEAT     
ROHBEAT2    
0x700000
spoiler
M-BRONZE    
M-SILVER    
M-GOLD      
M-PLATINA   
M-NO        
M-READY     
M-GO        
M-MAP1      
M-MAP2      
M-MAP3      
M-MAP4      
M-DANGER    
SOUVOICE1   
SOUVOICE2   
SOUVOICE3   
SOUVOICE4   
SOUVOICE5   
SOUVOICE6   
0x800000
spoiler
54-1        
19          
28-1        
38-1R       
SYNC LEAD2  
SHARPY      
FIZZY       
MOVIN HIT   
0x900000
spoiler
ANA ERA     
CHORAL      
CLUB HIT    
EURO STAB   
FAT STACK   
FLY CHORD   
MAJOR RAVE  
MINOR RAVE  
MOD TRAN    
MONSTO      
OLD STR     
RAVE HIT    
0xA00000
spoiler
UNI         
JUPI SYNTH1 
ZITER2      
ZITER3      
TAIKO1      
TAIKO2      
TAIKO3      
SYAKUJYO    
TIMBARS     
PONDR2      
KON1        
KON2        
BATA-D      
HIRYU1      
HIRYU2      
HIRYU3      
HIRYU4      
HIRYU5      
HIRYU6      
HIRYU7      
HIRYU8      
HIRYU9      
HIRYU10     
0xB00000
spoiler
ROHBEAT1    
ROHVOICE1   
ROHVOICE2   
ROHVOICE3   
ROHVOICE4   
ROHVOICE5   
ROHVOICE6   
0xC00000
spoiler
CYN 1       
DANCE KICK  
ENSO SN4    
EX SNARE    
GONG CYN    
RAP KICK    
TAMB        
909 KICK    
909 SNARE   
SIMMONS     
BAKICK      
T-HATO      
PI          
HIHATC      
HIHATO      
+EX HI TOM  
+EX LOW TOM 
808 KICK    
808 SNARE   
808 HATC    
808 HATO    
+CHA        
+GILO       
+RIM SHOT   
+RIDE       
CRAP2       
PONDR1      
SNARE PP    
FX1         
FX2         
FX5         
0xD00000
spoiler
ANAPAD      
L212-9      
LBUZZ 5TH   
LFM6+7      
MEM BASS2   
MG BASS PL  
+RHODES 2B  
+FM BASS 1  
+E BASS 28  
29 SLAP BASS
5THGT       
+MINI VIOLIN
38 OBOE     
25 ACOU BASS
30 FRETLESS 
+NOISE 1    
+NOISE 2    
STRAT GUIT2 
BELL V      
ALASKA      
J-8 HARP    
D-STR       
MIAMI BRASS 
0xE00000
spoiler
PAMERA      
ELIP        
SAW LEAD EX 
BRNS        
SYNB-V      
CLR         
HARP        
+MARIMBA    
TAKAGT4     
ACOGT       
ZITER1      
EDR0320     
+BRASS DOWN 
66 PADSAWS  
+PAN FLOOT  
+CLAV1      
0xF00000
spoiler
AMB PAD2    
+FULLSTRINGS
+MOOG BASS1 
+ROCK PIANO 
+ORGAN 2    
+ORGAN 3    
+SIRENS     
+CLAV2      
BARAPHO     
MUTE GTR    
TIMP B1     
52BASS1     
52BASS5     
52BASS6     
SQUARE TONE 
FIESTA      

unused samples

the MAP4 voice lines can be heard in the sound test (no. 034 and 03F) but are not used ingame.

sequencer software

🎎 to do: this needs a lot more research
(in addition to sequence playback) the audio cpu contains a lot of code and strings for what looks like a full sequencer.
$C058C8
offsets for string interpolation(function $C0C4BA) commands, e.g. "MIDI," "Channel," "Rename"
$C05CD7
strings constructed from interpolation:   ENSONIQ EGOS  OS Version 0.42 Personal  Studio
duart
the audio cpu writes various information out over UART as well. $C111D0: duart_putchar? $C11276: ensoniq_putchar? over duart $C11284: ensoniq_print? over duart
early mame debugger duart sniffing attempt
wpset 280017,1,w,temp0 == 0,{temp0 = temp1; temp1 = temp2; temp2 = temp3; temp3 = temp4; temp4 = temp5; temp5 = temp6; temp6 = temp7; temp7 = temp8; temp8 = temp9; temp9 = wpdata; g} wpset 280017,1,w,temp0 != 0,{printf "%c%c%c%c%c%c%c%c%c%c",temp0,temp1,temp2,temp3,temp4,temp5,temp6,temp7,temp8,temp9; temp0=0; temp1=0; temp2=0; temp3=0; temp4=0; temp5=0; temp6=0; temp7=0; temp8=0; temp9=0; g}
string   Land   SE   00 @ $D600 seems to be referenced during "Unexpected Event ID"(function $C10FEE)

various miscellaneous unused code

🎎 this is not a complete list!

unused 16-bit prng?

$00147E (used lcg is at $001468)
LCG_SEED = -$77da
;; if (LCG_SEED == 0) LCG_SEED = 1
00147e  move.w     (LCG_SEED,A5),D0w
001482  bne.b      .no_reseed
001484  moveq      #1,D0
.no_reseed:
;; LCG_SEED = (LCG_SEED * 176) % 32749
001486  muls.w     #176,D0          
00148a  divs.w     #32749,D0
00148e  swap       D0
001490  move.w     D0w,(LCG_SEED,A5)
001494  rts

counter test?

$9C034 counts to... various values... displaying a hexadecimal counter on the screen...
09c034  moveq      #0x0,D2
09c036  ori        #0x700,SR
.loop:
09c03a  move.w     #0xffff,D6w
09c03e  bsr.w      debug_count_to
09c042  move.w     #0xff,D6w
09c046  bsr.w      debug_count_to
09c04a  move.w     #0xff00,D6w
09c04e  bsr.w      debug_count_to
09c052  move.w     #0xf,D6w
09c056  bsr.w      debug_count_to
09c05a  move.w     #0xf0,D6w
09c05e  bsr.w      debug_count_to
09c062  move.w     #0xf00,D6w
09c066  bsr.w      debug_count_to
09c06a  move.w     #0xf000,D6w
09c06e  bsr.w      debug_count_to
09c072  bra.b      .loop
09c074  rts
after the first call of debug_count_to(0xFFFF), which takes around 74 minutes, it doesn't actually stop at 0xFF. y didn't test after that. debug_count_to() @$9C076 isn't called by any other code (it's blocking, anyway).

unused sprites

🎎 note: _BL_ graphic definition filename prefix is omitted for brevity in this section.

early items

ITMC_A01ITMC_A02ITMC_A03ITMC_A04
static versions of the arrow, star, moon, and flare items attached to an unused item base design. the arrow looks very different from its final appearance. moon and flare are closer to their final appearances, though the moon has no outline in this iteration. another unused set of items (sans arrow) with no animation sequence exists
ITME_B02ITME_C03ITME_D04
likely as placeholders for attaching a separate item top (as the final sprites are) to the item base, ITME01: (first frame of) final item designs:
ITMA01ITMB01ITMC01ITMD01
definitions ITMD07 and ITMD08 of the flare item are identical (i.e. reference the same 16x16 chips), and the ITMD08 definition is unused.

tile designs

unused static tile graphics, possibly early designs? PIEAA01 is used in MAP DATA EDIT:
PIEAA01PIEAA01APIEAA01CPIEAA01GPIEAF01
(there is no PIEAA01[B|D|E|F] or PIEA[B-E]01) there are 6 different tile graphics which have full animation definitions, but only 2 are used: PITA_A (normal tile) and PITA_E (another world)
PITA_A01PITA_B01PITA_C01PITA_D01PITA_E01PITA_F01
type F is present in tables used for switching between type A and E on another world state, even though that value is binary.
mame cheat: select tile graphic
2.01
  <cheat desc="select tile graphic">
    <comment>change the tile graphics used in non-another world play</comment>
    <parameter min="0" max="5" step="1"/>
    <script state="change">
      <action>maincpu.mw@955E4=(6878+param*64)</action>
      <action>maincpu.mw@95A54=(6E9C+param*30)</action>
    </script>
  </cheat>

adjacent piece color change

there are two unused animation labels for the effect when a shot tile settles (conveying converting adjacent colors).
EFXCHA@A22A4
EFXCH*@A228E
IROTES*@A22BA
mame cheat: select tile placement effect
2.01
  <cheat desc="select tile placement effect">
    <comment>change the effect sprite when placing tiles</comment>
    <parameter>
      <item value="0x2242">IROTES (final)</item>
      <item value="0x2236">EFXCH (early?)</item>
      <item value="0x223C">EFXCHA (placeholder?)</item>
    </parameter>
    <script state="change">
      <action>maincpu.mw@A21DE=param</action>
    </script>
  </cheat>

pusher wall extension

when the pusher extends, the parts upboard are colored in more and more (usually very subtly, palette depends on the stage). PUSA_C[11-13] are not referenced by game logic (the pusher cannot extend 13 levels). PUSA_C13
PUSA_C01

game mode select

GSELBG02
sprite definition of a single solid color chip from GSELBG01, the wavy space game mode select screen background (i.e., the chip itself is used, the sprite obj definition is not).

select screen

SELSPL14
this placeholder select icon is actually used... during frames after selecting a character where the respective icon flashes white (that is, it's rendered with an all-white palette over the original icon). this is the palette used in the object viewer. (also: gaira's icon is SELSPL10. there is no 11-13.)
SEBGA01ASEBGA01B
SEBGB01ASEBGB01B
unused character select background elements. final:
SELBGA01SELBGB01
near the rest of the select graphics are these:
SELST01ASELST01BSELST01C
SELST02ASELST03A
SELSTG02SELSTG03SELSTG04
SELSTG05SELSTG06SELSTG08
SELSTO1A, SELSTO1B, and SELSTO1C (not shown) are duplicate definitions to SELST01A, SELST01B, and SELST01C, respectively. it seems that at some point a stage select feature may have been planned (though it's also possible that the graphics would have just been associated with characters during character selection). interestingly, the numbering doesn't seem to match up with the final character order. 1, 2, and 3 would be associated with hiryu, soumei, and aifa, but SELSTG04 is inappropriate for renki. possibly:
SELSTG04rinrei or youen
SELSTG05kouko
SELSTG06rinrei or roushinshi
SELSTG08renki
there is no animation data for the SELST01 sequence, but y shows some possible animation fanfiction for it here:

chibi character sprites

🎎 incomplete! come back to this when animation data can be handled better.
soumei has this rabbit-like creature encased in ice (animdata at $E8492):
P3H1A[01-16]
it shares the common 'frozen in ice' attack palette. is it an easter egg? a placeholder for other frozen character sprites?

larger katakana title

TLAND02
unused in favor of the smaller TLAND03, which appears beneath TLAND01 ("LAND MAKER") on the title screen.

tutorial/introduction text

for the after-credit mechanics introduction text, the unused TEAE_A[01-08] are the same text as TEAF_A[01-08] but with a smaller font. four of the TEAF messages, which describe items, are unused
TEAF_A02大きい建物からは アイテム出現
TEAF_A06アイテムをうまく使おう
TEAF_A07大きい建物ほど 強力なアイテム出現
TEAF_A08一気に勝負をつけろ
(item appearance is instead explained in the attract mode teach demo.)

unknown backgrounds

DEMS_A01
DEMS_A02
two backgrounds, similar to those used for win screen 'character land' (KARI[01-09]) backgrounds, unused. ranju and gaira share KARI09. it's not clear if these are placeholders or unused backgrounds for another character, or something else entirely(???).

opening

O3_02D
ice shards for soumei's sequence in the opening. instead, individual kouko rock chunk sprites(!) (P6H3A[01-07]) with an ice palette are used, for random placement and independent movement.
OBGC_A01
OBGC_B01
unused backgrounds for soumei's opening sequence. instead he always appears on black or OBGE_A01 (which, by naming convention, would seem to indicate youen... nonetheless, it's used for soumei).
OBGG_B02
unused parallax layer for rinrei.
OBGJ_A01
unused opening background for gaira(!!). kinda looks like his face... maybe intended as a teaser?

mechanics

terminology

pusherthis.
push damageit goes in here and then the pusher extends.
piece damageit goes in here and then falls on the board as tiles.
natural pushwhen board pieces are pushed forward not as a result of push damage.
breakingclearing/"popping" groups of pieces by hitting the corner.
color changewhen pieces adjacent to a shot tile's settled position change to match color. (also "color conversion," "color spread," etc.)
bronze structurea 2x2 structure piece. produces an arrow item when broken.
silver structurea 3x3 structure piece. produces a star item when broken.
gold structurea 4x4 structure piece. produces a moon item when broken.
platinum structurea 5x5 structure piece. produces a flare item when broken. sometimes abbreviated 'plat'
arrow itemnormally, retracts the pusher wall. see arrow item behavior for more detail.
star itemeradicates from the board all pieces of whichever color breaks it.
moon itemchanges the color of all pieces on the board to the color it is broken with.
flare itemresets the pusher and all push damage, then adds a predetermined building pattern to the board of the same color as it was broken with.

tile bag and rng

⚠ tile bag and rng behavior is known to be different in the arcade mode of the playstation version.
the prng source($001468) is a linear congruential generator with (m=232, a=7, c=5). low bits with these parameters are not suitable, so the return value has its words swapped.
uint p_rand(void) {
  LCG_SEED = 7 * LCG_SEED + 5;
  return LCG_SEED<<16 | LCG_SEED>>16;
}
when a round starts, both players take their own copies of the global rng state, used only when drawing tiles. this means both players can get the same sequence of tiles at the start of the round (until becoming out of phase by rerolling, when tile color pools diverge), and do not affect each others' draws.
get_next_tile_color() @ $93A1C
when a tile is drawn during teach demo 1 (stage==-1) a special sequence of tiles @ $939B0 is used when a tile is drawn during teach demo 2 (stage==-2) a special sequence of tiles @ $939F6 is used otherwise:
  • 1) the global rng state is saved, and the current player's rng state is loaded.
  • 2) whether this is an even(3a) or odd(3b) draw is checked.
    • 3a) the draw is even: loop over the board and construct a bitset of colors present. store this in ram (p1: 0x40715C, p2: 0x40715E).
    • 3b) the draw is odd: load the previously determined valid color set.
  • 4) if the valid color set is empty, the color is RED, otherwise (4b)
    • 4b) call p_rand until the value passes. specifically:
      ;; if a negative random word is generated 
      ;; the lookup takes index -2=4e75 (rts) 
      ;; and has to go around again
      select_random_from_set_loop:
      ; do {
      ; color = p_rand()
          jsr        p_rand.l
      ; color = tile_ids_map[color % 7]
          ext.l      color
          divu.w     #7,color
          swap       color
          move.w     (tile_ids_map,PC,color*0x2),color
          nop
          nop
      ; return_color = color
          move.w     color,return_color
      ; } while((legal_color_bitset & 1<<color) == 0)
          asr.w      #1,color
          btst.l     color,legal_color_bitset
          beq.b      select_random_from_set_loop
      ;; 0xC is grey invalid tile color, 
      ;; so we map to skip to 0xE (purple)
      tile_ids_map:
      ; RED,BLUE,ORANGE,GREEN,CYAN,YELLOW,PURPLE
          dc.w 0,2,4,6,8,10,14
  • 5) the global rng state is restored.

damage and pushback

raw damage

raw damage for each piece is calculated at break time by $96680, which runs once for each piece cleared, including the shot tile. (larger structures count as a single 'piece') non-building tiles (including items) are worth 0.5 damage, but they are counted separately from building damage, so that leftover half-damage carries over between breaks. single buildings are worth 1 damage, structures are worth min(pusher_position, 7) * structure_level + structure_value where pusher position 0 is fully retracted, and position 1 looks like
STRUCTUREVALUEHEX
0bronze88h
1silver2216h
2gold3826h
3platinum5436h
table: damage values by structure level [1] e.g. breaking 2 silvers (structure level=1), at pusher position 2 adds    2*1 + 22  + 2*1 + 22 for 48 raw damage from structures note that 7 push is almost all the way down the board; large structures will not be able to take advantage of it. assuming one did break a gold structure at 5 push, 5*2 == 10 extra raw damage.
[1] internal tile level assigns nonstructure 0 and bronze starting at 2, but level used in structure damage is (internal level - 2) ($96696) [2] pusher row is stored as index into board, div 16 for position here.

damage conversion

$AB03E applies received damage raw damage is converted into two types of damage when it is sent to the opponent (represented by glowing orbs), 'piece damage' and 'push damage,' but the conversion is affected by margin time. the push damage conversion threshold starts at 9, meaning 9 raw damage is needed for 1 push damage. when the round elapsed time reaches 60 seconds, the push damage threshold decreases by 1, and further decreases every 30 seconds after that, until a minimum push damage threshold of 4 (at 180 seconds) [1] the remainder goes into piece damage, i.e.: push damage  += raw damage / push threshold piece damage += raw damage % push threshold in other words, <60s into a match, it takes 9 rawdmg for 1 push; between 60 and 90s, 8 rawdmg is 1 push; and 180s+ is 4 dmg/push note the modulo operator for nuisance tile damage: remainder damage that doesn't go towards push goes into piece. the push threshold influences nuisance tile accumulation! if incoming push damage >= 3, the "DANGER" voice line plays.
[1] (strictly speaking, this is a modifier that counts up and is subtracted from 9 at damage conversion time, but a view of (9 - modifier) as push threshold is simpler)

pushback

pushback($953A4) is evaluated whenever damage is sent. damage is sent on every frame that raw damage is >= 1. (note that all breaks will send at least 1 damage; breaking an item (0.5(shot tile)+0.5(item)) or single tile (0.5+0.5) will both evaluate pushback.) internally, pushback accumulates raw damage sent. it can be thought of like an invisible meter. "pusher [position]" refers to the amount of board covered/topmost line
  • when pusher is at 0, the meter resets.
  • when pusher is NOT at 0,
    • the requirement for pushback is REQ[pusher] + 2
    • the total raw damage this frame is added to the total accumulated pushback
    • if accumulated pushback meter exceeds the requirement:
      • the threshold is subtracted for the meter
      • the push state is set to PUSHBACK
REQ+2
01517
11517
21315
3911
468
546
624
713
813
913
table: pushback meter threshold lookup. note that pusher 0 (and 9?) cannot actually be encountered.
  • pusher 0 is fully retracted, and the threshold is not considered if damage is sent at this position
  • pusher 9 is off of the board.
for help visualizing pushback meter accumulation and requirements, see scripts for mame and fbneo: https://gist.github.com/y-ack/b17cd4a25b673dcfc7303ad8f5c60770
example
given a pusher position of 2 and initial accumulated pushback value of 0, a player makes two breaks: 1B+4, then 1S at pusher 2, 15 raw damage is required to achieve pushback. the first break, 1 bronze and 4 house pieces: (2*0 + 8) + (4) = 12 12/15 is insufficient to trigger pushback the second break, 1 silver: (2*1 + 22) = 24 12 previously accumulated + 24 = 36 36/15 is sufficient to trigger pushback, so pushback will occur this turn, and (given that we have no incoming damage) pusher position will be 1 for the next turn. the pushback value vs requirement is now 21/17, meaning the next shot that sends damage (any break) will also trigger pushback.
esoterics
fundamentally, the behavior of pushback is "sending enough damage will retreat the pusher." however, there are other subtle behaviors hidden in the undocumented mechanic: firstly, while every breaking shot will evaluate damage, it is possible for damage to be counted over multiple frames. the most obvious case is with star items, where the shot tile and item are counted 40 frames before the tiles eliminated are counted. additionally, it seems that there is some limit to the number of pieces that can be counted while sweeping over the board each frame, such that masses of single tiles send damage over multiple frames. damage being evaluated on multiple frames means pushback can be evaluated multiple times per break, and because there is no check for whether pushback state has already occurred, the pushback accumulator can drain multiple times with no benefit to the player. this bug could be prevented by checking whether the push state is already PUSHBACK before checking the pushback requirement. similarly, because arrow items 1) activate on break 2) set the same PUSHBACK state, breaking an arrow will drain pushback if possible, and unless there are tiles in the danger zone, this is essentially 'wasted' pushback. the second behavior has to do with pushback evaluation being triggered by sending damage. this is another separation of the actions of shooting a tile without sending damage (placing) and shooting a tile and sending damage (breaking). no matter how much pushback 'meter' is stored up, the pusher cannot be repelled without making a breaking shot. the other side of this is that pushback cannot be drained without breaking tiles. this includes the behavior of resetting when the pusher is fully retracted.

natural push

after every shot, around the same time as acquiring the next tile, the board is analyzed by $98126, calculating:
  • total visible piece count
  • counts of each color
  • number of unique colors
  • a bitset of colors present (redundantly, also generated by get_next_tile_color()?)
(the latter three seem to only be used for ai decision-making) when a new natural push time needs to be determined (i.e. at the start of the round or if natural push just occurred), $93740 is called to determine the number of shots before the next natural push. this is based solely on the number of pieces visible on the board at the time.
pieces on boardnatural push in:
0-91
10-142
15-194
20-246
25-299
30-3412
35-3915
40-4418
45-4921
50+24

arrow item behavior

arrow item and pushback (by damage accumulation) both set push state to PUSHBACK, which: 1) (if possible) reduces incoming push damage by 1 2) (if possible) moves the pusher wall back 1 row, revealing tiles at some point later, incoming push damage gets evaluated and (if necessary) the pusher pushes forward once and decrements one incoming push damage. when breaking an arrow item specifically, if there are any tiles in the red danger zone on the board, a special behavior is triggered that will skip push forward that turn, 'stalling' the pusher. largefigure: arrow item pusher stall conditions flow diagram

rank calculation

a value separate from normal score, an internal 'rank score' is calculated on loss (by $996DA), determining the rank displayed on the end screen and, if conditions are met, the ending sequence shown. rank score = ((bronze*3 + silver*9 + gold*25 + platinum*60)^2) / ((15 - stages_cleared) * (continues+1)) where structures are counted when broken/sent, and 'stages_cleared' is a stage counter that starts at 0. losing on hiryu's stage is 0, losing on aifa is 1, and so on, up to 10 for clearing gaira and 11 for clearing bonus.
rank scorerankending
0初級(E)D
19級(E)C
2-38級(E+)
4-77級(E+)
8-156級(D)
16-315級(D)
32-634級(D+)
64-1273級(D+)
128-2552級(C)
256-4991級(C)
500-799初段(C+)B
800-11992段(C+)
1200-24993段(B)
2500-39994段(B)
4000-79995段(B+)
8000-159996段(B+)
16000-319997段(A)A
32000-639998段(A)
64000-999999段(A+)
100000+名人(A+)
rank tables (horizontal)
rank score012-34-78-1516-3132-6364-127128-255256-499
rank初級(E)9級(E)8級(E+)7級(E+)6級(D)5級(D)4級(D+)3級(D+)2級(C)1級(C)
endingDC
500- 799800- 11991200- 24992500- 39994000- 79998000- 1599916000- 3199932000- 6399964000- 99999100000+
初段(C+)2段(C+)3段(B)4段(B)5段(B+)6段(B+)7段(A)8段(A)9段(A+)名人(A+)
BA
((BRZ*3 + SLV*9 + GLD*25 + PLT*60)^2) / ((15-STAGE) * (CONTINUES+1)) note: cmp2 was bugged in mame prior to 0.227, causing rank and ending selection to be incorrect.

2p vs stage selection

($8E46A) in vs mode, the stage set is selected by:
  • taking the current stage number
  • adding 1
  • adding a random number 0-3 (inclusive)
  • quantity modulo 8
;; $8E470:
    jsr        p_rand.l
    andi.w     #0x3,D0w
    addq.w     #0x1,D0w
    add.w      (stage_num,A5),D0w
    andi.w     #0x7,D0w           ; hiryu-roushinshi
    move.w     D0w,(stage_num,A5)
that is to say that: a) only stages in aifa, soumei, renki, and youen pools are possible in round 1, b) given a known round 1 stage, there are only 4 stages (16 layouts) possible in the following round 2, c) boss/bonus stage layouts will not show up in 2p vs, d) as an independent event, hiryu stage layouts only have a 1.5625% chance of appearing without going into the tiebreaker round. round distributions when considered independently: largelargelarge one of four layouts ("openers," piece sequences) in the stage's pool is later selected randomly.

artifacts

patents

  • 1998-02-16 JPH11226254 (A) - BLOCK GAME MACHINE, 津森Tsumori 康男Yasuo, 中久木Nakakuki 成一Seiichi (Espacenet) (Google)
    • shooting/structure mechanics
  • 1998-02-16 JPH11226259 (A) - BLOCK GAME MACHINE, 津森Tsumori 康男Yasuo, 中久木Nakakuki 成一Seiichi (Espacenet) (Google)
    • block deflection, settling
  • 1998-02-17 JPH11226255 (A) - BLOCK GAME MACHINE, 津森Tsumori 康男Yasuo, 中久木Nakakuki 成一Seiichi (Espacenet) (Google)
    • large structure composition

flyer

https://flyers.arcade-museum.com/flyers_video/taito/28000501.jpg https://flyers.arcade-museum.com/flyers_video/taito/28000502.jpg
  • error: the moon and star item graphics are misplaced.

pamphlet

(various photos) https://twitter.com/spread_bomb/status/727817185612075009 https://twitter.com/z7Ze3mGJQNGQurt/status/1346343293320249344 https://twitter.com/hatimituxiaoyu/status/1409132070132015104 https://twitter.com/hPa_ll/status/1498917163712716800 Protokami scans

control panel instruction card

https://www.mameworld.info/mrdo/mame_artwork_ingame.php

magazines

❗ need scans!!!
  • [1998-07-15] Gamest vol.227 p.184, release 1998-06-15 (src)
  • [1998-08-15] Gamest vol.230 p.180-181, release 1998-07-15 (src)
photo: https://twitter.com/suoh_takamura/status/1515503956843438080 large

taito universe trading card

largelarge
(more information (japanese): https://gmdisc.com/archives/679)

fanart

(1999-03-02) rinrei by ろさ (久保田千寿) http://orange.zero.jp/senju.rose/rosa/gallery/04game02.html (2000-10-01) rinrei, aifa, youen, shaoren by QQQ Arcadia 0005 p.170 (200?-??-??) youen by QQQ https://twitter.com/qqq99999_qqq/status/1491399059458117635 (2002-08-23) aifa by HAL44 http://www.ocn1.net/takebo/itadakimono9.htm (2012-11-21) soumei by KaiSuki https://www.deviantart.com/kaisuki/art/LandMaker-Soumei-339008288 (2013-08-24) aifa by いのり https://twitter.com/q_od/status/371489553036505088 (2017-04-22) aifa by randomouscrap https://www.deviantart.com/randomouscrap/art/Aifa-Land-Maker-request-676564777 (2018-12-25) renki and soumei by 藤堂与吉 https://twitter.com/shiromochi_flag/status/1077505874598322176 (2022-06-26) aifa by カービィ (miitopia) https://twitter.com/G0OvffOExpv2cLc/status/1540979593821028352 (2022-07-23) ranju by カービィ (miitopia) https://twitter.com/G0OvffOExpv2cLc/status/1550987519344271360 (2022-10-22) rinrei by kyatt7 https://qcs.shsbs.xyz/api/File/raw/kyatt7-10-22-rinrei (archive) (2022-10-24) aifa by haloopdy https://qcs.shsbs.xyz/api/File/raw/dlvxs (2022-12-11) hiryu by kyatt7 https://cdn.discordapp.com/attachments/849101921000095754/1051615897028153445/image.png (2023-01-18) roushinshi, shaoren by chen https://twitter.com/kinoglossia/status/1615625364889378816 (img) (2023-01-21) hiryu, renki, gaira, aifa, ranju, rinrei, soumei by kyatt7 https://twitter.com/kyatt7/status/1616928671046197248 (img) (2022-12-11 lines) (2023-01-25) youen, aifa by Retinacyst https://retinacyst.tumblr.com/post/707493955606020096 (2023-01-25) aifa by chen https://twitter.com/kinoglossia/status/1618505766528483328 (2023-01-26) aifa, youen by Retinacyst https://twitter.com/retinacyst/status/1618872946038284290 (2023-01-28) roushinshi, shaoren by chen https://twitter.com/kinoglossia/status/1619604518546849792 (2023-09-01) aifa by squidkeki https://twitter.com/squidkeki/status/1697834018744549519 (is it fanart if it's by the original art director...?) (2020-08-10) aifa by 加藤久和 https://twitter.com/arotak/status/1314903377457807361

STAFF

credits

ART DIRECTOR & CHARACTER DESIGN

加藤 久和 (@arotak)

GAME DIRECTOR & GAME DESIGN

中久木 成一 (http://www.nakakuki.net/)

PROGRAM

TMR (津森 康男 YASUO TSUMORI) 谷口 晃 (HIKARU TANIGUCHI) ジラプマX (林康高 YASUTAKA HAYASHI)

CHARACTER DESIGN

高田 明義 (AKI) (AKIYOSHI TAKADA) GREAT FUJIMOTO (藤本一弘 KAZUHIRO FUJIMOTO) ディスティーノブ (細見 晋敬 NOBUHIRO HOSOMI) 川石 徹 (TOHRU KAWAISHI)

GAME DESIGN

野村 昌弘 (MASAHIRO NOMURA)

SOUND

渡部 恭久 (Yack. @Feldherrn) 石川 勝久 (@babi_ztt) 瓜田 幸治 (@Yukiharu_Urita) NORIHIRO FURUKAWA (@nakayamaraiden)

DESIGN

竹浪 利行 (TOSHIYUKI TAKENAMI)

VOICE

LISLE "WEAPON" WILKERSON (@lisleweapon) BRIAN MATT-UHL (@brianmatt_tokyo)

"LAND MAKERS"

SEIJI KAWAKAMI (河上 聖冶) KEPPEL MAEKAWA (前川 浩之 @keppelclub) NOBUAKI KUROKI (黒木 伸章) NAOMITSU ABE (阿部 直光) YOUSUKE TSUDA (津田 洋介) YOUICHIRO KUGIMIYA (釘宮 陽一郎) TAKAYUKI TSUCHIYA (土屋 貴之) NAOTO OMURA (大村 直人)

SPECIAL THANKS

MASAKI OGATA (緒方 正樹) YOSHIHIRO SUZUKI (鈴木 善博) YOSHIHISA NAGATA (永田 喜久) TOSHIAKI KATO SON KEN SHIGENOBU SHIMIZU (清水 重信) ゴリゴ ダイラ ハイパー ヨシハラ コバヤシ ダイスケ KURI CP ALL STAFF CP MARKETING STAFF PEACOCK (likely TOMOKI ODA (小田 智樹))
(some information taken from https://w.atwiki.jp/game_staff/pages/771.html) GameStaff@wiki credits 鈴木善博 / YOSHIHIRO SUZUKI twice. the first occurrence is the correct placement.

maps

🎎 to do: x.x

audio cpu

🎎 to do: everything.
c0c4bastring interpolation with control sequences for display
c192eewrites es5505 registers for play sound
bank 1: sequencer code bank 2: sequence data main sequence playback code locations old notes
$C11A0A"maybe find sequence"
$C14CB8playback control? beat+measure counting
https://twitter.com/babi_ztt/status/1250245632142397441
石川勝久:
グリッドはF3初期だからシーケンス再生ソフトがまだ不完全だったんだよねぇ
https://blog.goo.ne.jp/nakayamaraiden/e/02fa8ff92769b1388c8bf09a84e3b4b6
古川 典裕:
SQ-1とEPSはF3ボードの開発用として散々使い倒した

resources

https://www.4gamer.net/games/040/G004098/20070927044/
なお,スライドではES5505に「8bitリニアPCM」とキャプションが付けられているが,ES5505自体は16bit PCMに対応する音源チップである。8bitと書かれているのは,「当時のROM容量の制限からデータサイズを抑える必要があったため」(石川氏)だそうだ。
Nicola's MAME Ramblings - F3 sound - discusses sample rom banks ENSONIQ EPS/ASR SEQUENCE STRUCTURE - maybe hints for decoding sequence headers https://github.com/jedpossum/Mame-Music-Scripts/blob/master/taitof3.lua - interesting ram addresses for currently playing sequence
summary
addressnotes
$5766note channel 11?
$579Echan 10?
$57D6chan 9?
$580Echan 8?
$5846chan 7?
$587Echan 6?
$58EEchan 5?
$5926chan 4?
$595Echan 3?
$5996chan 2?
$59CEchan 1?
$5D4Echan 12?
$FFD06Csong name
$FFD0B0measure
$FFD0B2beat
$FFD0BAmeasure count
$FFD0E6tempo
nakayamaraiden - タイトーメモリーズ - on f3 sound reproduction issues. it's suggested reading.
When changing the tempo in the middle of a song, F3 requires tempo data to be sent from the outside (main). Therefore, tempo changes are not included in the sound data itself. In test mode (as a common specification with other platforms), tempo change processing is not performed, so the default tempo of the song is played from beginning to end.

2.01J -> 2.02O Program ROM shifts

shift2.01J2.02O2.02O endcontent
0x4C$01154C$01154C$011598
76 bytes
0003000c36a036a136a136a136a136a136a136a136a236a136a136a136a336a436a536a636a736a836a936aa36ab36ac36ad36ae36af36b036b136b236b336b436b536b636b736b836b936ba
0x3AE$013954$0139A0$013D02unknown (for text ram?)
866 bytes
000000000000000000000000000000000000000000000000e000eeeee000eeee000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000eeeeeeeeeeee8888e000eeeee000eeeee000eeeee000eeeee0008eeee00088eee000f8eee000f8eeeeeeeeeeeeeeeeeeeeeeeeee888888e8fff8f888888f8f8f8e888f88eee88f8eeeeeeeeeeeeeeeeeeeeeeeee888888888fff8ff8f888ff88f8ee8f88f8ee8f88eeeeeeeeeeeeeeeeeeeeeeee888888ee8ffff888f8888f88f8e8ff88f8ee8888eeeeeeeeeeeee888eeeee8f8888888f88fffffff8f8888f88888e8f888ffe8f8eeeeeeeeeeeeeeeeeeeeeeee888888888ff8f8ffff8888f88f8e88888f8e8eeeeeeeeeeeeeeeeeeeeeeeeeee8e88e8888e8fe8ff8e8f88f8ee8f88f8ee8f88f8eeeeeeeeeeee8eeeeeee8eee888e8888ff88f88f88f888f8e88f8888ee8f8eeeeeee8ff8ee888ff8ee8f8888888f88888fff8ff8888f8f88ee8f8f8eee8f8f8eeeeeeeeeeeeeeeeeeeeeeeee8888e888f8ff88ff8ff88f8888f88f8ee8f88f8eeeeeeeeeeeeeeeeeeeeeeeee888e8888ff888fff88f888f888f8e8f8ff88e88feeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000f8eee00088eee0008eeee000eeeee000eeeee000eeeee000eeee000000008e888f88888f8f8ffff8f888888888e8eeeeeeeeeeeeeeeeeeeeeeee00000000f8ee8f88f8888f888ffffff888888888eeeeeeeeeeeeeeeeeeeeeeee00000000f8ee8888f8888f88ff88ff8f88888888eeeeeeeeeeeeeeeeeeeeeeee000000008f88e8f88f8888f888ffff88e888888eeeeeeeeeeeeeeeeeeeeeeeee000000008f8e8eee8f888ee8fff88ee88888eee8eeeeeeeeeeeeeeeeeeeeeeee000000008e8f88f8888f88fffff88ff888888888eeeeeeeeeeeeeeeeeeeeeeee00000000e88f888888f888f8ff88888f888eee88eeeeeeeeeeeeeeeeeeeeeeee00000000ee8f8f8e888f8f888ff8fff888888888eeeeeeeeeeeeeeeeeeeeeeee00000000e8f88f8e88f88f888ffffff888888888eeeeeeeeeeeeeeeeeeeeeeee0000000088f8e888ff8888ff88f88f8888f88f88ff8888ff888ee888eeeeeeee00000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000d8
0x3B4$015BFC$015FAA$015FB0unknown palette init-related resource 001003C006C0
0x3F4$015FBC$016370$0163B0new palette (for?)
64 bytes
00F8009000885840009868500098705000784848000080F800E8480800A038100000000000F8780000F8880000F8980000F8A80000F8B8000028188800F8F8F8
0x400$091470$091864$091870region check
cmpi.w     #1,(0x1ffffe)
bne.w      0x91958
12 bytes
0C790001001FFFFE660000EA
0x404$0A06C8$0A0AC8$0A0ACCfunction table entry 000A0BEC
0x406$0A06D4$0A0AD8$0A0ADAword table entry 0002
0x40A$0A06F0$0A0AF6$0A0AFAfunction table entry 000A0B00
0x41C$0A07E2$0A0BEC$0A0BFEnew function
cmpi.w     #1,(0x1ffffe)
beq.b      0x0a0bfc     ;(rts)
jsr        0x0059ee
rts
18 bytes
0C790001001FFFFE67064EB9000059EE4E75
0x450$0A1114$0A1530$0A1564new function
movem.l    { A0  D1  D0 }, -( SP )
move.w     #0xd8, D0w
movea.l    #0x0139a0, A0     ; ❗
jsr        0x005bce
move.w     #0x36, -(SP )
move.l     #0x61c222, -(SP )
move.l     #0x01154c, -(SP ) ; ❗
jsr        0x0056e6
adda.w     #0xa, SP
movem.l    (SP)+, { D0  D1  A0 }
rts
52 bytes
48E7C080303C00D8207C000139A04EB900005BCE3F3C00362F3C0061C2222F3C0001154C4EB9000056E6DEFC000A4CDF01034E75
0x45E$0A112C$0A157C$0A158Anew region code
cmpi.w     #0x2 ,D1w
bne.b      0x0a158a
cmp.w      (-0x5f0, A5), D1w
beq.b      0x0a158a
bsr.b      0x0a1530    ; ❗
14 bytes
0C4100026608B26DFA10670261A6
0x460$0A1416$0A1874$0A1876unknown 0006

emulation notes

  • audio improperly emulated
    • possibly incorrect clock timings
    • effect chip not emulated(?)
m68k cmp2 was bugged in mame prior to 0.227, causing rank and ending selection to be incorrect. as of mame 0.253, win messages and endings are visible thanks to this research! the taito f3 has 4 total clip planes, and up to 2 can be assigned to each playfield. the low bits are in 625x00 and the high bits are in the upper nybble of 624400 (1, 2) and 624600 (3, 4)
previous notes on ending slides
known emulation issues (MAME 0.227)
  • line ram 5600 mode is unemulated
    • win messages don't display
    • possibly cause of first ending slide not appearing
ending slides have maybe a table of clip plane and playfield column scroll addresses at $FF102
slideclip 1 loclip 1 hiclip 2 loclip 2 hi
1nullnull625600(unemu)624600
2625600(unemu)624600625400(unemu)624600
3625400(unemu)624600625200624400
4625200624400625000624400
5625600624600625000624400
table: line ram base addresses finalburn neo has a kludge to make win messages(playfield 4?) show up, it also allows the first ending message to show up, but it will overlap the second message/tilemap.
playstation version: not emulated. cut down spritesheets: how much of the originals were unused? some color, graphics changes. what is going on with the sound (w. this is not a psx resource. XEBRA can emulate pocketstation 'POCKET LAND MAKER' (japan only)

scripts and tools for y's reference

mame spaces:list
rom = manager.machine.system.name 
for d in pairs(manager.machine.devices) do 
  for spacename, space in pairs(manager.machine.devices[d].spaces) do
    for e = 1,space.map.entries:size(),1 do 
      mapentry = space.map.entries[e]
      print((d.." "..spacename.." "..e))
      print(string.format("echo 0x%x 0x%x 0x%x %s",mapentry.address_start,mapentry.address_end,mapentry.region_offset, mapentry.read.name))
      for i=mapentry.address_start,mapentry.address_end,1 do
      end
    end
  end 
end
mame spaces:save
rom = manager.machine.system.name
for d in pairs(manager.machine.devices) do 
  for spacename, space in pairs(manager.machine.devices[d].spaces) do
    for e = 1,space.map.entries:size(),1 do 
      mapentry = space.map.entries[e]
      print((rom.."_"..d.."_"..spacename.."_"..e..".bin"):gsub(":",""):gsub(" ","-"))
      print(string.format("\tregion %d: %x, %x (%s)",e,mapentry.address_start,mapentry.address_end,mapentry.read.name))
      out = io.open((rom.."_"..d.."_"..spacename.."_"..e..".bin"):gsub(":",""):gsub(" ","-"),"wb")
      for i=mapentry.address_start,mapentry.address_end,1 do
        out:write(string.char(space:read_u8(i)))
      end
      out:close()
    end
  end
end

ensoniq sample data

interleaved 0s. remove them for processing.
cat ensoniq.bin | ruby -e 'print ARGF.read.b.scan(/.(.)/m).join' > ensoniq_unpadded.bin
patterns and extraction in imhex
(cannot do anything useful with sample params structure because it will not handle the 24-bit pointers...)
#include <std/io.pat>
#include <std/mem.pat>
#pragma endian big

fn bank_relative(u16 offset) {
  return addressof(parent) & 0xF80000;
};

struct SampleParams {
  u8 uk1[2];
  u8 start_ofs[3]; // 24-bit pointers
  u8 uk2;
  u8 loop_ofs[3]; // maybe
  u8 uk3;
  u8 end_ofs[3];
  u8 uk4;
};

struct SampleName {
  SampleParams* params : u16 [[pointer_base("bank_relative")]];
  char name[12];
};

SampleName bank1names[24] @ 0x200001;
SampleName bank2names[15] @ 0x280001;
SampleName bank3names[16] @ 0x300001;
SampleName bank4names[18] @ 0x380001;
SampleName bank5names[8] @ 0x400001;
SampleName bank6names[12] @ 0x480001;
SampleName bank7names[23] @ 0x500001;
SampleName bank8names[7] @ 0x580001;
SampleName bank9names[31] @ 0x600001;
SampleName bank10names[23] @ 0x680001;
SampleName bank11names[16] @ 0x700001;
SampleName bank12names[16] @ 0x780001;

fn main() {
  str output;
  for (u8 i = 0, i < 24, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank1names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 15, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank2names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 16, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank3names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 18, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank4names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 8, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank5names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 12, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank6names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 23, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank7names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 7, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank8names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 31, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank9names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 23, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank10names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 16, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank11names[i].name);
  }
  output = output +"\n";
  for (u8 i = 0, i < 16, i = i + 1) {
    output = output + std::format("|`{}`|\n",bank12names[i].name);
  }
  
  std::print(output);
};
playing instrument log
bp c17cd0,1,{temp0=(a6+d0+$4); logerror "%c%c%c%c%c%c%c%c%c%c%c%c\n",b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0; g} bp c19454,1,{temp0=(a6+d0+$4); logerror "%c%c%c%c%c%c%c%c%c%c%c%c\n",b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0++,b@temp0; g} diff -yb ensoniq-sample-names.txt land-maker-sample-log-uniq.txt --suppress-common-lines

dumping graphics

mame gfx layout defs https://github.com/y-ack/landmaker-color-mod/blob/main/scripts/landmakrsavexpm.lua for palmod raws

array-ize block def

from ghidra.program.model.data import ArrayDataType, UnsignedIntegerDataType
if getSymbolAt(currentAddress) is None:
	raise Exception("refusing to overwrite listing; no data label at current position")
dataSize = 4
height = getByte(currentAddress.add(1))
width = getByte(currentAddress.add(3))
listStart = currentAddress.add(4)
length = (height+1) * (width+1)
uint = UnsignedIntegerDataType()
clearListing(listStart, listStart.add((length - 1) * dataSize))
createData(listStart, ArrayDataType(uint, (height+1) * (width+1), dataSize))

animations

flow format
wordsubsequent
0x00code pointer
0x01code pointer
0x04code pointer
0x05list node pointer
0x09animation data pointer
0x0C?? can be followed by 0x09 and data pointer
animlist
0x00code pointer
0x0Bwtime? often 6 or 60, w??? usually 0, pbl graphic
0x0Aend anim list

mame cheats

  <cheat desc="Region Select">
    <comment>region for region checks</comment>
    <parameter>
      <item value="0x0000">(0) Undefined (behaves like World?)</item>
      <item value="0x0001">(1) Japan</item>
      <item value="0x0002">(2) Americas</item>
      <item value="0x0003">(3) World</item>
    </parameter>
    <script state="change">
      <action>maincpu.mw@1FFFFE=param</action>
    </script>
  </cheat>
  <cheat desc="Stage layout always...">
    <comment>Force stage layout for both players. CANNOT BE REVERTED WITHOUT HARD RESET!</comment>
    <parameter min="0" max="120" step="1"/>
    <script state="change">
      <action>maincpu.mw@98550=303C</action>
      <action>maincpu.mw@98552=param</action>
      <action>maincpu.mw@98602=303C</action>
      <action>maincpu.mw@98604=param</action>
      <action>maincpu.mw@9865A=303C</action>
      <action>maincpu.mw@9865C=param</action>
      <action>maincpu.mw@98708=303C</action>
      <action>maincpu.mw@9870A=param</action>
    </script>
  </cheat>
P2A2A curtsy P2A3A ... dance kick? P2C1A pull out fan hold up P2C3 fan dance P2G1 victory celebration jump P2G2 defeat fall P2H1 release doves P2H2A little dance with gold(?) flower trim P2H4A fan spin

input checks

119eprocess raw inputactive
11dc==active
12ccprocess input edge differencesactive
12d6write edge differencerising
667aobject checkactive
6668object check main menurising
6728==rising
673c==rising
6754==rising
696eobject check menu, STARTactive
8ce68debug popup P2 B3+P1 rB3->pop uprising
8d214debug popup menu UPrising
8d22adebug popup menu DOWNrising
d82bedebug popup menu B1->selectrising
8d310YES/NO prompt (left/right)active
8d398YES/NO prompt B2->cancelrising
8d3aaYES/NO prompt B1->selectrising
8d41eOFF/ON prompts (left/right?)active
8d4a6OFF/ON prompts B2->cancelrising
8d4b8OFF/ON prompts B1->selectrising
8d56cdebug popup slow level (left/right)active
8d604debug slow level B1->setrising
8d60edebug slow level B2->cancelrising
8d7fadebug color edit LEFTrising
8d808debug color edit RIGHTrising
8d85adebug color edit (up/down?)active
8d93cdebug color edit B2->backrising
8d946debug color edit B1->color submenurising
8dae8debug color edit RGB submenu (LEFT/RIGHT)active
8da50debug color edit RGB submenu UPrising
8da5edebug color edit RGB submenu DOWNrising
8dc28debug color edit RGB submenu B2->set(?)rising
8dc28debug color edit RGB submenu B1->setrising
8dea6main loop.....? STARTrising
8e27acredit inserted...? STARTrising
8e380"PUSH ANY BUTTON TO CANCEL" STARTrising
8e388"PUSH ANY BUTTON TO CANCEL" B1rising
8e79cmain loop.....? need bpactive
8ef7econtinue timer B1->dec timer
8f2faSTART-> for player jump in?active
8f764unref'd? rSTART+B1->set MATCH_OVER
908a6title screen into d3, redundant/unuse?rising
90a74title screen secret code inputrising
90a7a==rising
90f86match setup reset inputsactive
90f92match setup reset inputsrising
91006??? write... something? probably p2 input or prev input hack, need bpactive
910a8practice TRY NEXT LEVEL text B1->shorten->skiprising
912f8location test text B1/B2/B3->skiprising
919d8win screen B1->skipactive
92782map screen B1->skipactive
93b22in-match p1 controls->stateactive
93b28in-match p1 controls->staterising
94ee6debug cycle B2 cycle through attack portraitsrising
9c10eearly auto p1 demo play p1+p2 UP->on, TILT->OFFactive
9c15cearly p1 auto-play write inputsrising
9c162==rising
9c186==rising
9dcdacharacter select interfaceactive
9dd1acharacter select grid interfacerising
9ef6egame mode select interfaceactive
9ef72game mode select interfacerising
9f2e6map data edit controlsrising
9f76aalt map data edit controls ???rising
9f890alt map data edit controls ???rising
9f950alt map data edit controls ???rising
a0710called by a0682 jsr table tutorial actions reset input, pusher++
a0728==; reset input, pusher--
a0762==; take inputs from 407a0c/d, 407a0a--
a0784==; reset input; 407a0e = *(a4+2)
a07a6==; reset input; nextTile = *(a4+2)
a07be==; reset input; incomingPushTotal=1, something
a0934detect B1 for teach button press graphicactive
a094adetect left/right for teach joy graphicactive
a0a48reset inputs for demo stage -1
a0ac2reset inputs for? demo stage -1
a0db4more reset inputs while loading demo assets
a0e52==
a0e9c==
a11b8==
a1298==
a135areset inputs for demo stage -2
a15aareset inputs for demo
fed52ending sequence B1/START->skiprising
(omitted: service menu)
8f2TILT->tilt erroractive
90aSERVICE1->service coinfalling
559eSERVICEMODE/SERVICE_UNUSED->backup data failure prompt escaperising
8cde6TILT->debug [PAUSE]rising
8cde6SERVICE1+rTILT->debug popup activateactive
8e038SERVICEMODE/SERVICE_UNUSED->enter service menurising
8eefcSERVICE1/COIN1/COIN2->reset continue timerrising
9c124TILT->p1 side autoplay disableactive
100056SERVICEMODE/SERVICE_UNUSED->escape monitor testrising
1001dcSERVICE1->switch test ???rising
1009acSERVICEMODE/SERVICE_UNUSED->exit switch testactive
101562SERVICE1->alternate sound testactive

task slots

0$14c8:main loop(wait a moment)
1$8d67e:service menu $8de38:copyright notice $8de88:attract loop $8df06:credit inserted $8e066:service menu
2$8e31c:teach demo
3$8ed22:round control
4$8ed62:"start round?"
5$8ed3c:"per frame code with damage?"
6$8e300,$8ed32:3988
7$8ed42,907ba:3d5e
8$8ed72:94a50
9$8ed82:p1 autoplay input check $8f8a0:map screen $8f8da,$8f978:win screen $90894,$90d54:'insert coin' blink
10$8e3c0,$8e972,$8ea0a,$8f810:8e096 $8e658,$8e6de,$8e99c,$8e9e6:charram black transition
12$8edea:game mode select $8ee12,$8f878:character select
14$8de22:4498
15$8de0c:10170

player states

-1shot tile traveling
0full control, can fire
2?
3wall pushing back?
4shot tile converting adjacent colors
5breaking
6sending damage (damage orbs)
8initial + ?
9pushing forward?
10push done
11?

unrelated

https://twitter.com/TSUKAPPI/status/704254180328808450 1996 graph paper, https://twitter.com/TheMogMiner/status/1032984277925945345
The audio hardware on the Taito F3 board, for example, is almost identical, right down to the memory map, with the Ensoniq SD-1.
https://twitter.com/TSUKAPPI/status/1250237806284165121
I remember that when ENSONIQ was introduced, programmers were having a hard time with Galactic Storm and early F3 motherboards.
https://twitter.com/babi_ztt/status/1250245632142397441
Since Grid Seeker was in the early stages of the F3, the sequence playback software was still incomplete
https://twitter.com/blapa_3rd/status/1388473208576241668
パズルボブル2Xとクレオパトラフォーチュン位ですね。F3末期はサターンも衰退していたので、ぽっぷんぽっぷやランドメーカーもPSに移植されちゃいましたw
Puzzle Bobble 2X and Cleopatra Fortune. At the end of F3, Saturn was also declining, so Pop'n Pop and Landmaker were also transplanted to PS.
https://twitter.com/arotak/status/443787079919472640
僕が入った頃のタイトーはバリバリ硬派なアメコミなグラフィックがメインの会社だったんです。アニメっぽい絵を描く方が異端の存在で、その手のものをちょこちょこ引き受けて描いてました。
When I first joined Taito, the main company was crunchy and hard-core American comic graphics. Drawing an anime-like picture is a heretic, and I took on and drew things of that kind.
https://twitter.com/kenji_kaido/status/1223808603108196352
There are two types of specifications at Taito: the pre-development specifications and the post-development final specifications. The final specifications are written after completion, so they're pretty standard, but I get the impression that the pre-development specifications are often a bit too much.
https://togetter.com/li/664375 (kaido on x-55 cleo) https://twitter.com/suddendesu/status/1312535651917590529
Big thanks to whoever liked this 2+ year old tweet which reminded I need to update the Land maker article
https://twitter.com/arotak/status/443424335668060160
偉そうなことを色々書いてますが、当時自分が企画を立てたもので開発中止になったものだってあるんです。プリルラも一度は開発中止になったのでした。救ったのは僕ではなく直談判した当時の担当プログラマーです。プリルラが陽の目をみたのは彼の手柄です
I've written a lot of things that look great, but at that time I had a plan and the development was discontinued. The development of Pu-Li-Ru was also once discontinued. It wasn't me who saved me, but the programmer in charge at the time who spoke directly. It is his credit that Pu-Li-Ru saw the sun

palette mods

tools, raws, list of known palette mods: https://github.com/y-ack/landmaker-color-mod https://github.com/y-ack/PalMod